You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
A date in summer months in a year where DTS was not applicable (Before 1977)
A date in summer months in a year where DTS was applicable
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
@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.
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.
@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?
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.
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.
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:
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)
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.
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.
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.
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.
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.
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.
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.
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".
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.
Activity
Joe4evr commentedon Jun 1, 2021
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.
bramve-fenetre commentedon Jun 1, 2021
@Joe4evr Tested it with .NET 5 now (5.0.6). The same behavior still occurs in that version
huoyaoyuan commentedon Jun 1, 2021
I suspect
TimeZone.CurrentTimeZone.ToLocalTime(DateTime.Parse("1958-07-03T23:00:00.000Z")).ToString("s")may work.tarekgh commentedon Jun 1, 2021
@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 Timeyou will find the data is covering the whole date ranges from1/1/1to12/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.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.
rogerhendriks commentedon Jun 15, 2021
@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?
tarekgh commentedon Jun 15, 2021
Currently there is no easy work around for that. You may consider using NodaTime Library if this is really a blocker.
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.
7 remaining items
tarekgh commentedon Sep 30, 2025
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.
lscorcia commentedon Oct 6, 2025
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!
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:
Output (system timezone set to "W. Europe Standard Time", or the equivalent "Europe/Rome"):
tarekgh commentedon Oct 6, 2025
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.
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.
Clockwork-Muse commentedon Oct 7, 2025
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.
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.
xtqqczze commentedon Oct 7, 2025
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.
Clockwork-Muse commentedon Oct 7, 2025
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.
rogerhendriks commentedon Oct 7, 2025
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.
Clockwork-Muse commentedon Oct 7, 2025
... 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.
lscorcia commentedon Oct 11, 2025
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.
tarekgh commentedon Oct 11, 2025
in .NET we support both Windows and IANA names. You shouldn't worry about naming if you are using .NET.
Clockwork-Muse commentedon Oct 11, 2025
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.
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".
xtqqczze commentedon Oct 11, 2025
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.
Clockwork-Muse commentedon Oct 12, 2025
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.