Skip to content

DateTime daylight savings issue #53519

Closed
Closed

Description

@bramve-fenetre

Description

For context our application is hosted in the Europe/Amsterdam timezone and we use dates dating back in the 1900's. Throughout the years different daylight saving rules are applied to this timezone. This is where there we found a weird problem where a wrong timezone offset is used for summer days in years where there should be no daylight time savings yet. Here is the code on how to see the issue:

//System timezone Europe/Amsterdam  (GMT+1 without daylight saving, GMT+2 with daylight saving)

// Date before daylight saving time was introduced
var beforeDaylightSaving = DateTime.Parse("1958-07-03T23:00:00.000Z");
var beforeDaylightSavingISOString = beforeDaylightSaving.ToString("s");
//Results in GMT +2: 1958-07-04T01:00:00
//Expected GMT +1: 1958-07-04T00:00:00
//NOT OK

// Date in daylight saving time
var daylightSaving = DateTime.Parse("2000-07-03T22:00:00.000Z");
var daylightSavingISOString = daylightSaving.ToString("s");
//Results in GMT +2: 2000-07-04T00:00:00
//Expected GMT +2: 2000-07-04T00:00:00
//OK

// Date outside of daylight saving time
var noDaylightSaving = DateTime.Parse("2000-11-03T23:00:00.000Z");
var noDaylightSavingISOString = noDaylightSaving.ToString("s");
//Results in GMT +1: 2000-11-04T00:00:00
//Expected GMT +1: 2000-11-04T00:00:00
//OK

In the above example there are 3 scenario's

  1. A date in summer months in a year where DTS was not applicable (Before 1977)
  2. A date in summer months in a year where DTS was applicable
  3. A date in winter months in a year where DTS was applicable

Out of these 3 scenario's the first scenario is done incorrectly, there are 2 hours added in the DateTime after parsing the ISO string. This should be just 1 hour as in the year 1958 Daylight saving time did not exist in the Netherlands.

These same 3 scenario's can be tested in Javascript by just doing new Date(isoString) in the browser console, this results in the correct behavior. I would expect the same in .NET here.

Configuration

  • Tested this on .NET Core 3.1 and the old .NET Framework 4.7
  • Windows 10 x64
  • Important: Timezone Europe/Amsterdam

Activity

  1. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Jun 1, 2021
  2. Joe4evr commented on Jun 1, 2021

    @Joe4evr
    Contributor
    • Tested this on .NET Core 3.1 and the old .NET Framework 4.7
    • Windows 10 x64

    Can you test this on .NET 5 (and/or Linux) for completeness? With .NET 5 all culture-related APIs (which includes time zones) switched the Windows implementation to ICU data, so that may give the correct answer for such historical dates.

  3. bramve-fenetre commented on Jun 1, 2021

    @bramve-fenetre
    Author

    @Joe4evr Tested it with .NET 5 now (5.0.6). The same behavior still occurs in that version

  4. huoyaoyuan commented on Jun 1, 2021

    @huoyaoyuan
    Member

    I suspect TimeZone.CurrentTimeZone.ToLocalTime(DateTime.Parse("1958-07-03T23:00:00.000Z")).ToString("s") may work.

  5. tarekgh commented on Jun 1, 2021

    @tarekgh
    Member

    @bramve-fenetre this is a known limitation when running on Windows. Windows doesn't carry the historical time zone data. When you look at the Windows time zone data in the registry HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\W. Europe Standard Time you will find the data is covering the whole date ranges from 1/1/1 to 12/31/999. and the following adjustment rule will apply even for year 1985 which state the daylight saving always starts in March and ends in October.

    DateStart                  : 1/1/0001 12:00:00 AM (Unspecified)
    DateEnd                    : 12/31/9999 12:00:00 AM (Unspecified)
    DaylightDelta              : 01:00:00
    DaylightTransitionStart    : M:3, D:1, W:5, DoW:Sunday, FixedDate:False
    DaylightTransitionEnd      : M:10, D:1, W:5, DoW:Sunday, FixedDate:False
    BaseUtcOffsetDelta         : 00:00:00
    NoDaylightTransitions      : False
    

    It is already something we are considering to fix in the future release to try to depend on other source of the data rather than Windows registry data.

    P.S. this should work fine on Linux though.

  6. rogerhendriks commented on Jun 15, 2021

    @rogerhendriks

    @tarekgh 2 questions:
    This is closed but what is the Issue # we can follow then as you are considering this?

    This is quite serious: in our applications all dates and calculated ages in the summer <=1958 are incorrect on Windows.
    What is the recommended workaround?

  7. tarekgh commented on Jun 15, 2021

    @tarekgh
    Member

    What is the recommended workaround?

    Currently there is no easy work around for that. You may consider using NodaTime Library if this is really a blocker.

    This is closed but what is the Issue # we can follow then as you are considering this?

    I'll reopen this issue and move it to the future. When I meant considering it, I meant this came up in some other offline discussions. We don't consider this high priority because it is limited scenario and also the limitation is on the OS too.

  8. added this to the Future milestone on Jun 15, 2021
  9. 7 remaining items

  10. tarekgh commented on Sep 30, 2025

    @tarekgh
    Member

    Could we consider using ICU for time zone data on Windows?

    This is possible but comes with some challenges. The PR #119662 moves the calculation to rely on a simpler form of caching, which gets us one step closer. The main challenge is that our APIs expose time zone rules originally designed around the Windows format. This would need further investigation and depends on the priority of the work.

  11. lscorcia commented on Oct 6, 2025

    @lscorcia
    Contributor

    Thanks for spotting this! The issue I posted is indeed a duplicate. Maybe it's just me but I feel Google is really getting worse since the AI push, I miss the simple pagerank days!

    I am closing this as the issue is Windows time zone data. I would suggest reporting this to Windows OS through the feedback hub.

    Done. I'm pretty sure no one reads that but it's worth a shot.
    Is there already an open issue to track the possibility to switch .net to ICU data for time zones?

    Can you please confirm that there is currently no way - except maybe editing the Registry - to replace the DST rules using the public API perimeter? Basically this means that all DST<->Local conversions must be assumed to be broken on Windows, especially in Web contexts where browsers widely implement ICU timezones/DST. In our case this actually can lead to litigation as people are entering past contract dates and the server is generating PDFs with the supplied data in the user timezone. When the date is at midnight, depending on how the date is rendered, it could be displayed with a difference of a whole day.

    For the other poor souls who may find this thread in the future - on Linux the problem does not exist as the system provided timezone data is from the very comprehensive ICU library. On Windows consider using NodaTime as it uses its own DST source and does not exhibit the issue. If you need a quick bandaid solution you can replace .ToLocalTime with a custom implementation that uses NodaTime and emulates .ToLocalTime behavior:

    using NodaTime;
    ...
    private static DateTime ToLocalTimeNoda(DateTime dtUtc)
    {
       var instant = Instant.FromDateTimeUtc(dtUtc);
       var tzInfo = DateTimeZoneProviders.Tzdb.GetSystemDefault();
       var localInstant = instant.InZone(tzInfo);
       return DateTime.SpecifyKind(localInstant.ToDateTimeUnspecified(), DateTimeKind.Local);
    }

    Output (system timezone set to "W. Europe Standard Time", or the equivalent "Europe/Rome"):

    .ToLocalTime
    UTC 1993-09-19T22:00:00Z > Local 20/09/1993 00:00:00
    UTC 1993-09-29T23:00:00Z > Local 30/09/1993 01:00:00 (wrong)
    
    .ToLocalTimeNoda
    UTC 1993-09-19T22:00:00Z > Local 20/09/1993 00:00:00
    UTC 1993-09-29T23:00:00Z > Local 30/09/1993 00:00:00 (correct)
    
  12. tarekgh commented on Oct 6, 2025

    @tarekgh
    Member

    Is there already an open issue to track the possibility to switch .net to ICU data for time zones?

    Not that I’m aware of. You’re welcome to file a request, but implementing it would depend on the level of demand. This behavior has existed on Windows for a long time, and it seems that users generally care more about recent time zone transitions than historical ones.

    Can you please confirm that there is currently no way - except maybe editing the Registry - to replace the DST rules using the public API perimeter?

    I would say the right thing to do here is to file the request to Windows to fix that. You can do that through the Feedback Hub on Windows.

    There is another idea that can be used as a workaround, in which you can create your own custom TZ. This will allow you to pass the list of TZ rules.

  13. Clockwork-Muse commented on Oct 7, 2025

    @Clockwork-Muse
    Contributor

    Basically this means that all DST<->Local conversions must be assumed to be broken on Windows

    You have ABSOLUTELY NO GUARANTEE that the client has timezone rules that match your servers' (note that I didn't say "correct"), regardless of the OS/data source or the rule in question.

    • There's no guarantee they have an updated set of rules available (old OS no longer updated, or doesn't have historical data).
    • There's no guarantee they've updated to the latest rules available.
    • There's no guarantee their OS uses matching rules (There are multiple versions of ICU, after all, and it's packaged per distro anyways).
    • There's no guarantee they haven't customized the data for any number of reasons.
    • There's no guarantee the rules are even correct (bugs).
    • There's no guarantee the rules have been updated in a timely manner (the record between notification of a DST rule change and its implementation is ~48 hours, by Haiti. Microsoft took three months to issue the patch for Windows, but even Linux/ICU would most likely miss that).
    • If you have multiple instances of your application running, you have no guarantee two running calls use the same set of rules (distributed application updates).

    In our case this actually can lead to litigation as people are entering past contract dates and the server is generating PDFs with the supplied data in the user timezone. When the date is at midnight, depending on how the date is rendered, it could be displayed with a difference of a whole day.

    This sounds more like an application design flaw/oversight. Even ignoring the "past data" problem here, future dates can be subject to rule changes. This is why John Skeet (the author of Noda Time) wrote storing UTC is not a silver bullet.

    In that spirit, if someone is entering any date, past or present, it likely needs to include an explicit timezone (id), and be explicitly in that local time (and not translated to/from UTC or another timezone). Or alternatively, have it be translated to an permanent offset, and always refer to that.

  14. xtqqczze commented on Oct 7, 2025

    @xtqqczze
    Contributor

    Is there already an open issue to track the possibility to switch .net to ICU data for time zones?

    Based on @Clockwork-Muse’s detailed explanation, I’m not convinced that using ICU for all time zone data on Windows provides a clear benefit.

  15. Clockwork-Muse commented on Oct 7, 2025

    @Clockwork-Muse
    Contributor

    Is there already an open issue to track the possibility to switch .net to ICU data for time zones?

    Based on @Clockwork-Muse’s detailed explanation, I’m not convinced that using ICU for all time zone data on Windows provides a clear benefit.

    To be clear, I believe that using an ICU source on Windows would be a good thing, and in the 99% case will work just fine.

    For the remaining 1%, it's not a problem really solved by choice of data source, but rather a problem with how date/time values are thought about and handled. Almost all of these cases revolve around being specifically a local time in some time zone, and ignoring things like conversion to other timezones (more or less).

    If your contracts are being written as "date in timezone", then that value needs to always be transmitted and stored that way, not converted to UTC and back.

  16. rogerhendriks commented on Oct 7, 2025

    @rogerhendriks

    The main issue is that the result is inconsistent, depending on the year. In JavaScript, this works as expected, in .NET Core it does not. Regardless of all interesting technical explanations, this should be addressed from a functional standpoint — whether the fix is applied in Windows or Core. Working on international focussed solutions we would really appreciate a Core focussed solution without using external solutions like NodaTime. If this needs to be fixed in windows i think feedback from the .NET Core product owner will make a lot more impact then the Feedback Hub. This is known to only focus on obvious problems, not specific problems for developers so the issue will reside forever.

  17. Clockwork-Muse commented on Oct 7, 2025

    @Clockwork-Muse
    Contributor

    The main issue is that the result is inconsistent, depending on the year. In JavaScript, this works as expected, in .NET Core it does not.

    ... ignoring whether or not the Windows team does update the historical data, my point was, explicitly, that these sort of data inconsistencies can be found for all OSes and applications. JavaScript may only be working because the browser bundles its own timezone package, which is not guaranteed (and at minimum requires that the browser be up-to-date with the latest rules).

    Even if .NET Core provided its own set of timezone data (it does not - the data comes from the host OS, quite deliberately), this doesn't actually solve your problem; it just means that now you have to somehow ensure that the client application that is contacting your server is using the same timezone rules as the server. Or at least, communicates some additional information about those rules when communicating a date/time value.

    And again, you also have to consider things like updates to the rules that can change what the translation should be/should have been, even just on your "trusted" servers. The rules are updated multiple times a year, and sometimes that includes revisions to historical rules, not just future ones.

  18. lscorcia commented on Oct 11, 2025

    @lscorcia
    Contributor

    I agree that our own applications need better safeguards. Where applicable, we are switching to DateOnly (or rather, a custom DateOnly implementation as we still need to support .net 4.8 and .net 6 AND we need XML serialization. Thankfully that's coming with .net 10, and we are getting closer on rewriting the last processes still on fw4.8). Where it's not, we're going to use NodaTime to compute Local, unspecified DateTimes with the correct rules.

    Fully fixing this on Windows side would require a fundamental rework of all time zone data - one cannot just select "W. Europe Standard Time" and hope that all those countries will agree on a similar DST schedule. Windows would need to drop all current time zone names and switch to using actual locations like "Europe/Rome". It would be an immense work and would probably imply a lot of compatibility issues.

    I think that the points in the replies above are very valid, but I don't see a contradiction in allowing the developer to load time zones and DST from a different source. Having the ability to choose whether to use OS data or an external source could reduce the amount of "inevitable" errors quite a bit. There's also more than just browsers to deal with - UTC dates produced by servers with obsolete rules for example (am I the only one still dealing with Java 6 applications on old Oracle servers?). UTC dates coming from archived, historic data. UTC dates coming from specialized hardware.

    Errors can always happen, but if the OS rules could be overridden using an appropriate mechanism then it would be possible for a developer to patch his own copy of tzdata and maintain consistency, at least until an OS patch is released.

  19. tarekgh commented on Oct 11, 2025

    @tarekgh
    Member

    one cannot just select "W. Europe Standard Time" and hope that all those countries will agree on a similar DST schedule. Windows would need to drop all current time zone names and switch to using actual locations like "Europe/Rome". It would be an immense work and would probably imply a lot of compatibility issues.

    in .NET we support both Windows and IANA names. You shouldn't worry about naming if you are using .NET.

  20. Clockwork-Muse commented on Oct 11, 2025

    @Clockwork-Muse
    Contributor

    Where it's not, we're going to use NodaTime to compute Local, unspecified DateTimes with the correct rules.

    NodaTime has a NetStandard 2.0 build, you can use it on .NetF 4.8. If you're doing a lot of timezone work, you may find it easier to just switch to using NodaTime altogether. Which also allows you to include your own set of rules alongside your application.

    That said, it doesn't necessarily fix your problems, because you still have to somehow ensure the set of rules the application is using matches what the server is using. Or you have to design your application in such a way where the differences are rendered immaterial. Whether your application can update its rules in a more timely manner than the host OS is potentially an interesting question, but only a small aspect of the problem.

    UTC dates produced by servers with obsolete rules for example (am I the only one still dealing with Java 6 applications on old Oracle servers?). UTC dates coming from archived, historic data. UTC dates coming from specialized hardware.

    Again, a potential fundamental problem here is that you're storing/transmitting a UTC date when really you should probably be storing/transmitting the local date/time + zone (+ maybe offset).

    A reasonable general guideline may very well be "Never translate to UTC, only every translate from UTC".

  21. xtqqczze commented on Oct 11, 2025

    @xtqqczze
    Contributor

    in .NET we support both Windows and IANA names. You shouldn't worry about naming if you are using .NET.

    Given that ICU is now integrated into Windows, it would be ideal for .NET to leverage the IANA time zone database directly when handling IANA names.

  22. Clockwork-Muse commented on Oct 12, 2025

    @Clockwork-Muse
    Contributor

    Again, you have no way to guarantee that any two concurrently running instances of your application are using the same set of rules, whether Windows switches to ICU or not (although obviously switching would help). Strictly speaking, similar problems also occur when updating the timezone rules for any single particular deployment.

    Properly handling this requires that you architect your application to take this eventuality into account. There likely exist some libraries to assist, but this is a 2% problem, usually only for scheduling applications and future dates. The same techniques can also be used for past dates, however.

  23. locked and limited conversation to collaborators on Nov 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions