2011-08-30 31 views
6

Tôi đang gặp sự cố xung quanh ngày và giờ phân tích cú pháp:Phân tích Ngày giờ có múi giờ đã biết nhưng không được cung cấp

Tôi đang cố gắng phân tích chuỗi ngày giờ được trích xuất từ ​​trang web tiếng Đức. Nó được đưa ra trong định dạng 'day.month.year 24 giờ: phút, như:

01.01.2011 17:00 

Và nó luôn nằm trong múi giờ Đức. Nhưng ở đây có vấn đề:

  • '01 .01.2011 17:00' nên được phân tích để một struct DateTime với '01 .01.2011 16:00' trong UTC (ở đây, các múi giờ là CET, mà không ánh sáng ban ngày tiết kiệm thời gian)
  • khi '01 .06.2011 17:00' nên được phân tích để một struct DateTime với '01 .01.2011 15:00' trong UTC (ở đây, các múi giờ là CEST, với thời gian tiết kiệm ánh sáng ban ngày)

Tôi không biết làm cách nào để đạt được điều này. Nếu tôi đặt đồng hồ địa phương của mình thành múi giờ của Đức và tôi phân tích cú pháp với DateTime.ParseExact và cờ DateTimeStyles.AssumeLocalDateTimeStyles.AdjustToUniversal, nó được phân tích cú pháp chính xác. Tuy nhiên, tôi muốn bất kỳ khách hàng nào phân tích nó độc lập với đồng hồ và múi giờ địa phương của họ. Ngoài ra, tôi không muốn làm các múi giờ bù đắp bản thân mình, bởi vì nó phụ thuộc vào ngày (mùa hè: -2/mùa đông: -1).

Khi tôi có giờ trong UTC, bạn sẽ dễ dàng chuyển đổi nó thành múi giờ địa phương bất kỳ.

+0

bản sao có thể có của [Tạo một ngày giờ trong một Múi giờ cụ thể trong C# fx 3.5] (http://stackoverflow.com/questions/246498/creating-a-datetime-in-a-specific-time-zone-in -c-fx-3-5) –

+0

@ Michael Haren: Không, nó không phải là bản sao. Tôi đã xem xét điều này trước khi tôi hỏi câu hỏi này và nó đã không giúp với vấn đề cụ thể của tôi. –

+0

không phải là một bản sao có lẽ nhưng tôi figured nó muốn được hữu ích –

Trả lời

1

Sau khi thấy rằng nhiệm vụ không thể archieved với sự giúp đỡ của các khuôn khổ/Silverlight WP7, tôi đã viết một helper nhỏ không công việc:

public static class DateTimeHelper 
{ 
    /// <summary> 
    /// Tries to parse the given datetime string that is not annotated with a timezone 
    /// information but known to be in the CET/CEST zone and returns a DateTime struct 
    /// in UTC (so it can be converted to the devices local time). If it could not be 
    /// parsed, result contains the current date/time in UTC. 
    /// </summary> 
    public static bool TryParseCetCest(string s, string format, IFormatProvider provider, DateTimeStyles style, out DateTime result) 
    { 
     // Parse datetime, knowing it is in CET/CEST timezone. Parse as universal as we fix it afterwards 
     if (!DateTime.TryParseExact(s, format, provider, style, out result)) 
     { 
      result = DateTime.UtcNow; 
      return false; 
     } 
     result = DateTime.SpecifyKind(result, DateTimeKind.Utc); 

     // The boundaries of the daylight saving time period in CET and CEST (_not_ in UTC!) 
     // Both DateTime structs are of kind 'Utc', to be able to compare them with the parsing result 
     DateTime DstStart = LastSundayOf(result.Year, 3).AddHours(2); 
     DateTime DstEnd = LastSundayOf(result.Year, 10).AddHours(3); 

     // Are we inside the daylight saving time period? 
     if (DstStart.CompareTo(result) <= 0 && result.CompareTo(DstEnd) < 0) 
      result = result.AddHours(-2); // CEST = UTC+2h 
     else 
      result = result.AddHours(-1); // CET = UTC+1h 

     return true; 
    } 

    /// <summary> 
    /// Returns the last sunday of the given month and year in UTC 
    /// </summary> 
    private static DateTime LastSundayOf(int year, int month) 
    { 
     DateTime firstOfNextMonth = new DateTime(year, month + 1, 1, 0, 0, 0, DateTimeKind.Utc); 
     return firstOfNextMonth.AddDays(firstOfNextMonth.DayOfWeek == DayOfWeek.Sunday ? -7 : 
                (-1 * (int)firstOfNextMonth.DayOfWeek)); 
    } 
} 

Bí quyết là để phân tích nó mà không cờ DateTimeStyles.AssumeUniversal (điều này làm cho TryParseExact giả ngày là UTC và trở về ngày quy đổi/điều chỉnh cục bộ), respecifying nó như là UTC và sau đó điều chỉnh thủ công nó thành actu al UTC tương đương.

Nó tuân thủ các quy tắc DST có thể được tìm thấy here. Tôi đã thử nghiệm nó với tất cả 4 trường hợp ranh giới ngay trước/sau khi bắt đầu/kết thúc thời gian tiết kiệm ánh sáng ban ngày. Điều đó cho thấy một lần nữa tầm quan trọng của thử nghiệm: Tôi đã phải thay đổi các nhà điều hành < trong DstStart.CompareTo(result) < 0 để <= để làm cho nó tạo ra kết quả chính xác.

Tôi có cảm giác rằng tôi đang phát minh lại bánh xe ở đây (mà tôi ghét phải làm), nhưng không muốn sử dụng một thư viện dành riêng cho công việc đơn giản này. Tôi đã có một cái nhìn tại Noda Thời gian đó là một dự án tuyệt vời, nhưng tôi nghĩ rằng nó không cần thiết cho việc này.

Tôi hy vọng tôi có thể tiết kiệm một chút thời gian với người trợ giúp nhỏ này. Nó là cố ý không chung chung cho tất cả các múi giờ (nếu bạn cần sử dụng một lib như Noda Thời gian thay vào đó), nhưng đối với những trường hợp mà bạn chỉ có một múi giờ cố định duy nhất, như trong trường hợp của tôi.

+0

Tôi rất ngạc nhiên khi bạn không sử dụng AssumeUniversal - tôi sẽ có * dự kiến ​​* để trả lại nó đã có với UTC DateTime; xin lỗi vì đã gây hiểu nhầm cho bạn về mặt trận đó nếu không phải như vậy. DateTime làm phiền tôi: ( –

+0

@Jon: Tôi nghĩ chính xác như vậy là trung thực, và tự hỏi tại sao nó tạo ra kết quả lạ như vậy. Cho đến khi tôi nhận ra nó là điều đúng nhưng khác với những gì bạn có thể mong đợi: Nếu bạn để nó phân tích '17 : 00 'nó giả định 5pm UTC như mong đợi nhưng trả về một cấu trúc DateTime với 7pm (Kind = local) trong trường hợp của tôi (local timezone = german) .Nếu bạn gọi '' SpecifyKind (.., Utc) '', nó trả về một utc struct ... –

+0

Và có: DateTime thực sự gây phiền nhiễu :) –

4

Có vẻ như bạn biết bạn nên phân tích cú pháp múi giờ nào với múi giờ đó. Giả sử .NET 3.5 (và do đó TimeZoneInfo) bạn nên logic:

  • phân tích nó như một thời gian "địa phương" (không múi giờ cụ thể)
  • Chuyển đổi mà giờ địa phương một thời gian UTC

Thật không may DateTime makes that slightly tricky. CHỈNH SỬA: Tôi nghĩ bạn muốn chuyển đổi phân tích cú pháp bằng cách sử dụng DateTimeStyles.AssumeUniversal - nhưng điều đó kết thúc bằng việc trả lại một số địa phươngDateTime, gây phiền toái. Về cơ bản bạn muốn kết thúc với mộtDateTime với thời điểm thích hợp để bạn có thể sử dụng:

parsed = DateTime.SpecifyKind(parsed, DateTimeKind.Unspecified); 

Sau đó bạn có thể nhận được một giá trị tính theo giờ UTC với:

DateTime utc = TimeZoneInfo.ConvertTimeToUtc(parsed, germanTimeZone); 

Lưu ý rằng bạn thực sự muốn có một thời gian ngày "không xác định" trước, để bạn có thể chuyển đổi thành UTC theo múi giờ tùy ý. Bạn cũng nên nhớ khả năng rằng thời gian cục bộ không rõ ràng (xảy ra hai lần) hoặc không thể (không xảy ra) do thay đổi DST.

Và vâng, điều này sẽ dễ dàng hơn rất nhiều trong Noda Time khi nó đã hoàn thành :)

+0

+1 Tuyệt vời, các '' ConvertTimeToUtc'' là bằng cách nào đó là phần thiếu. Tuy nhiên, tôi có một câu hỏi cuối cùng: tôi lấy '' germanTimeZone'' từ đâu? –

+0

@Philip: Bạn tìm ra ID múi giờ thích hợp và sử dụng 'TimeZoneInfo.FindSystemTimeZoneById'. Tôi nghĩ rằng bạn muốn "W. Giờ chuẩn Châu Âu" (bỏ qua thực tế là nó được gọi là thời gian tiêu chuẩn; đó chỉ là ID - nó vẫn sẽ xử lý thời gian tiết kiệm ánh sáng ban ngày). –

+0

Có lẽ tôi đã đề cập đến việc tôi đang xây dựng một ứng dụng Windows Phone 7, trong đó 'TimeZoneInfo'' chỉ có phương thức' ConvertTime'' và các múi giờ '' Utc'' và '' Local''. –

Các vấn đề liên quan