dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.36k stars 4.74k forks source link

Wrong timezone on Linux #46152

Closed Rast1234 closed 3 years ago

Rast1234 commented 3 years ago

Description

I am writing unit tests for DateTime serialization. Sometimes DateTime.ToString gives me wrong TZ offset:

new DateTime(863999999999, DateTimeKind.Unspecified).ToString("zzz");
// "+05:00" - OK
new DateTime(863999999999+1, DateTimeKind.Unspecified).ToString("zzz");
// "+04:03" - why?!

new DateTime(2000,01,01, 1,2,3, DateTimeKind.Unspecified).ToString("zzz")
// "+05:00" - OK
new DateTime(200,01,01, 1,2,3, DateTimeKind.Unspecified).ToString("zzz")
//"+04:03" - why?!

Same results with DateTimeKind.Local. I tracked it down to TimeZoneInfo.GetAdjustmentRuleForTime which selects AdjustmentRule with offset of 57 minutes, which is subtracted from offset later.

Other observations:

Dotnet-GitSync-Bot commented 3 years ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

wfurt commented 3 years ago

cc: @tarekgh @bartonjs

Clockwork-Muse commented 3 years ago
new DateTime(200,01,01, 1,2,3, DateTimeKind.Unspecified).ToString("zzz")
//"+04:03" - why?

In general, most places were considered to be synced to local solar noon before (usually) the mid-late 1800s, so almost all timezones have strange offsets if you go back in time far enough.

That said, looking at various databases it appears the offset should be +04:02:33, so perhaps the bug is really "where did the seconds go?"

Rast1234 commented 3 years ago

considered to be synced ... the mid-late 1800s

Thanks, this covers my curiosity. So, to be safe and sane, i should use datetimes after 1900s because otherwise TZ databases could give unpredictable offsets.

Clockwork-Muse commented 3 years ago

Thanks, this covers my curiosity. So, to be safe and sane, i should use datetimes after 1900s because otherwise TZ databases could give unpredictable offsets.

That depends. What are you trying to do?

In the real world, countries can change their offset at any time, to whatever they want (A few years ago, Haiti changed DST rules on ~24 hours notice, and North Korea abruptly changed their offset by 30 minutes to "move away from the Japanese timezone" - they changed it back less than a year later). This is besides all the current real-world rules with offsets of 15 minutes (and DST adjustments of 30 minutes). And since the data is sourced from the OS, it can be modified by admins. So you're usually best off considering it to be configuration data (something you have no control over). If you're trying to do testing of what would be your own library code, make a fake timezone (or make sure your test environment uses a known database, and restrict to a few known times/zones).

That particular offset is recorded in 1916. The next line records +03:45:05 in 1919. I think Hong Kong has its first offset (ie "everything before this was local solar") in 1945.

Rast1234 commented 3 years ago

In the real world, countries can change their offset at any time

Yeah, i've seen these things happen, this is not what i'm concerned about. Thanks for interesting examples though!

My case is simple: i have custom format strings for a DateTime. Due to its Kind quirks, i wrote some tests to ensure my DateTimes get serialized/deserialized in expected way. To easily debug tests, i used this approach:

var d = new DateTime(1,2,3,4,5,6,7, kind);
var s = d.ToString(myFormat);
// do some asserts and vice versa

This allowed me to see if i mixed up mm and MM in format string so that numbers appear in wrong places or if i get wrong results because of kind.

Now i'll just use year 2000 to avoid cryptic "local solar" situations :)

tarekgh commented 3 years ago

@Clockwork-Muse thanks for your answers.

That said, looking at various databases it appears the offset should be +04:02:33, so perhaps the bug is really "where did the seconds go?"

This is a limitation in TimeZoneInfo that doesn't currently support seconds offset https://github.com/dotnet/runtime/blob/5808716d9669e0ed2fdaad2222a1611219c2bec2/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs#L95

Thanks, this covers my curiosity. So, to be safe and sane, i should use datetimes after 1900s because otherwise TZ databases could give unpredictable offsets.

@Clockwork-Muse already replied this comment but I want add on Linux .NET report the offsets according to the data we read from there which should be correct (with the exception we don't support seconds offsets). the offset should be predictable. The issue is Windows TZ data doesn't carry the historical data which is limiting how far you can go back to get TZ offsets. I think in the future the .NET can try to start using ICU TZ data on Windows which can help in consistency too.

@Rast1234 That said, I am going to close this issue but feel free to send any more questions you think we can help with.