NET theo dõi một số lịch sử nhưng không phải lúc nào cũng chính xác. Bạn đã vấp phải một trong những điều không chính xác.
.NET nhập tất cả thông tin múi giờ của nó từ Windows qua sổ đăng ký, như được mô tả here và here. Nếu bạn nhìn vào sổ đăng ký tại HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\Russian Standard Time\Dynamic DST
bạn sẽ thấy rằng nó chỉ theo dõi thông tin từ năm 2010 trở đi cho múi giờ này. Các ngày thử nghiệm trong năm 2000 sẽ không hoạt động tốt vì nó sẽ quay trở lại quy tắc sớm nhất có sẵn (2010).
cơ sở UTC bù đắp thông tin là theo dõi trong registry, nhưng không trong lớp AdjustmentRule
rằng NET nhập khẩu nó vào. Nếu bạn kiểm tra các quy tắc điều chỉnh cho múi giờ này, bạn sẽ thấy rằng năm 2012 và 2013 không được nhập vào tất cả:
var tz = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
foreach (var rule in tz.GetAdjustmentRules())
{
Console.WriteLine("{0:d} - {1:d}", rule.DateStart, rule.DateEnd);
}
OUTPUT:
1/1/0001 - 12/31/2010
1/1/2011 - 12/31/2011
1/1/2014 - 12/31/2014
Mặc dù họ tồn tại trong Windows registry, 2012 và 2013 không được nhập vì không có điều chỉnh thời gian tiết kiệm ánh sáng ban ngày.
Điều này tạo ra sự cố khi thay đổi bù đắp cơ sở - như nó có cho múi giờ này. Vì hiện tại là +3, và hai năm mà nó là +4 không được nhập, thì sẽ có vẻ như là +3 cho những năm còn thiếu.
Không có giải pháp tốt cho việc này bằng cách sử dụng TimeZoneInfo
. Ngay cả khi bạn cố gắng tạo múi giờ tùy chỉnh của riêng mình, bạn sẽ gặp sự cố khi kết hợp loại thay đổi này vào cấu trúc dữ liệu có sẵn.
May mắn thay, có một tùy chọn khác. Bạn có thể sử dụng tiêu chuẩn IANA time zones, qua thư viện Noda Time.
Các mã sau sử dụng Noda Thời gian để phù hợp với những gì bạn đã viết trong mã ban đầu của bạn:
DateTimeZone tz = DateTimeZoneProviders.Tzdb.GetSystemDefault();
Console.WriteLine(Instant.FromUtc(2012, 1, 1, 0, 0).InZone(tz).LocalDateTime);
Console.WriteLine(Instant.FromUtc(2012, 6, 1, 0, 0).InZone(tz).LocalDateTime);
Console.WriteLine(Instant.FromUtc(2000, 1, 1, 0, 0).InZone(tz).LocalDateTime);
Console.WriteLine(Instant.FromUtc(2000, 6, 1, 0, 0).InZone(tz).LocalDateTime);
Nếu múi giờ địa phương của bạn chưa được thiết lập cho Moscow, bạn có thể thay đổi dòng đầu tiên:
DateTimeZone tz = DateTimeZoneProviders.Tzdb["Europe/Moscow"];
OUTPUT:
1/1/2012 4:00:00 AM
6/1/2012 4:00:00 AM
1/1/2000 3:00:00 AM
6/1/2000 4:00:00 AM
cập nhật
Sự cố được mô tả ở trên AdjustmentRule
không theo dõi thay đổi bù đắp cơ sở được mô tả trong bài viết Hỗ trợ của Microsoft KB3012229 và sau đó được khắc phục trong Khuôn khổ .NET 4.6 và trong .NET Core.
Trong the reference sources, người ta có thể thấy rằng AdjustmentRule
hiện giữ trường m_baseUtcOffsetDelta
. Trong khi trường này không được hiển thị thông qua thuộc tính công khai, nó sẽ ảnh hưởng đến tính toán, và nó phản ánh trong tuần tự hóa nếu bạn sử dụng các phương thức FromSerializedString
và ToSerializedString
(nếu có ai đó thực sự sử dụng).
Hãy xem 'DateTimeOffset'. *** Nếu *** bạn có thể nắm bắt bù đắp múi giờ khi bạn nắm bắt dữ liệu bạn sẽ có hình dạng tốt hơn để hiển thị nó và tính toán chống lại nó. –