Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TimeZoneInfo should have consistent Ids across Windows and Linux #14929

Closed
eerhardt opened this issue Jul 27, 2015 · 51 comments
Closed

TimeZoneInfo should have consistent Ids across Windows and Linux #14929

eerhardt opened this issue Jul 27, 2015 · 51 comments
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.DateTime
Milestone

Comments

@eerhardt
Copy link
Member

eerhardt commented Jul 27, 2015

AB#1179633

Currently Windows has TimeZoneInfo.Id values like "Pacific Standard Time" and "Eastern Standard Time". But on Linux they are "America/Los_Angeles" and "America/New_York".

We should make these Ids consistent across platforms so the same code can run on both Windows and Linux. Currently if you call TimeZoneInfo.FindSystemTimeZoneById you need to pass in different strings between Windows and Linux.

@Clockwork-Muse
Copy link
Contributor

Some of this is the intention of the System.Time package (among other things).

@mattjohnsonpint
Copy link
Contributor

I think the right thing to do here is to use CLDR mappings (either directly, or via ICU).

AFAIK, all Windows IDs can be mapped to TZDB Ids. This will let current code run on Linux without modification.

We could also map in the other direction, as many TZDB Ids can be mapped back to Windows zones. Though users would have to consider that it might throw an InvalidTimeZoneException for a valid time zone if it's not mapable.

@mattjohnsonpint
Copy link
Contributor

@Clockwork-Muse - Since this particular bit is about platform support for the existing TimeZoneInfo class, it can't go in the System.Time package.

@karelz
Copy link
Member

karelz commented Nov 8, 2016

We need formal API proposal.
It will be entangled with all the other TimeZoneInfo issue - will likely be non-trivial API design and implementation.

@manigandham
Copy link

Some recent discussion here: https://github.com/dotnet/corefx/issues/11897

Seems like a small external library is best suited based on comments to keep up with changes without affecting system packages.

https://github.com/mj1856/TimeZoneConverter

@svrooij
Copy link

svrooij commented Jul 25, 2017

Not sure if this is the correct place, but maybe it is even a good idea to just support creating a TimeZoneInfo with the required fields. Just a public constructor maybe? Or a not read-only id....

I'm currently using Ews-api-core to talk to Office 365. And I cannot set the timezone to display correctly. That is because the TimeZoneInfo.Id gets serialised as XML into the soapheader incorrectly and Office 365 expects the Windows ID <typ:TimeZoneDefinition Id="W. Europe Standard Time"/> , but my application is running on Unix and this is serialised to <typ:TimeZoneDefinition Id="Europe/Amsterdam"/>.

@mattjohnsonpint
Copy link
Contributor

@svrooij - Does my TimeZoneConverter library help? If not, can you elaborate for the team as to why? Thanks.

@svrooij
Copy link

svrooij commented Jul 26, 2017

@mj1856 Thanks for the reply Matt, but that doesn't help. The problem is that the id's on unix are not in the format that Exchange wants. And Exchange uses the offset and the timezone id to set the timezone on appointments. And if the id isn't in the list it recorgnises it just picks a random one with the same offset (and then displays a notice to the user that the appointment is in a different timezone)

@mattjohnsonpint
Copy link
Contributor

Sure. So you call:

string tz = TZConvert.IanaToWindows("Europe/Amsterdam");
// Result:  "W. Europe Standard Time"

Working .net fiddle here

You would do this in your code before you pass the time zone on to exchange.

@mattjohnsonpint
Copy link
Contributor

@svrooij - I found your report at sherlock1982/ews-managed-api#9. They would have to take the dependency on TimeZoneConverter with the above code to make this work seamlessly for you.

@jpenniman
Copy link

@eerhardt /@karelz, I'd like to take a stab at a proposal and implementation. This issue (and dotnet/corefx#11897) directly impacts at least 2 of our customers and more as we migrate customer to Core and Linux. Third party packages are a workaround, but doesn't change the fact that TimeZoneInfo isn't truly xplat/portable, since consuming code can't "build once, run anywhere".

Is there a formal API proposal template I should use? Who would I submit it to?

@eerhardt
Copy link
Member Author

@jpenniman - thanks for your interest. Please check out the API review process doc for information on how to make an API proposal.

My original hope was that the existing APIs could be made to work with both sets of time zone IDs and not require new APIs. But thinking more about how this would actually work, it may not be possible, or ideal. So if you have ideas on new APIs that would help here, please feel free to submit them.

@tarekgh
Copy link
Member

tarekgh commented Feb 21, 2018

@jpenniman the challenge here is we are trying to avoid carrying the TZ data inside the framework. the reason is that will required to have some changes inside coreclr every time the TZ data get changed. so I would suggest looking at @mj1856 TimeZoneConverter and how to make it xplate and package can be used. consistently. we have other TZ things need to be done so I prefer to have a complete plan for everything together instead of picking some items here and there to do.

@jpenniman
Copy link

Thanks @eerhardt , I'll take a look.

@tarekgh, I agree, TZ data in the framework would be challenging and not ideal. However, it also makes the code not portable. We have customers with hundreds of services that will break when we move to Linux. I'd rather see if there's a way to fix TimeZoneInfo. Matt's TimeZoneConverter is a workaround, but has the same maintenance problem--now I have to rely on him to keep the package up-to-date instead of Microsoft. Using a workaround isn't just a couple lines of code in a service in my case... it's thousands of lines across hundreds of services, and in a few cases, data migrations. I'm not saying the TZ data has to be in the clr, just that, ideally, it would be nice if, the TimeZoneInfo api works the same xplat with the same values. IOW, if I call TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time") on Linux, it "just works".

This is a major piece in several lift-and-shift efforts we have lined up. There's a lot of interest to move to core and Linux, especially among our AWS customers, because it reduces compute consumption costs by 60% and gives them all the managed features that are lost when running Windows on AWS.

I'll take a look at Matt's stuff as well as NodaTime and see if there's a happy medium--compatible API without TZ data in the clr.

Java ships a TZ database with JRE (a separate file, not embedded) and is updated separately from the JRE itself (using tzupdater). I'll take a look at how Apache is handling this in the OpenJDK. Maybe we can borrow some ideas from them, since Java already solved the platform differences.

@Clockwork-Muse
Copy link
Contributor

What Oracle recommends is to get updated timezones by updating the JRE/JDK, but provides the ability to update timezones via their tool. It's a binary file that's been loaded via, assumedly, the default implementation of a specialized Dictionary interface (essentially).

NodaTime essentially has its own interface, they just make you load the file manually, and use DI, if you want a different version.

Presumably you could have a nuget package that comprises a pre-built resource file (plus one dependency for the necessary interfaces, allowing people to do things like have network-based ones or whatever).


I say this, but updating timezone rules is a hard problem if you've ended up persisting any of them somewhere; scheduling apps can't be updated independently from their datastore, for example (which is why I mentioned a network-based database, although attempting to do this live is an even harder problem)

@tarekgh
Copy link
Member

tarekgh commented Feb 22, 2018

@jpenniman I fully agree with you that the framework should solve this problem. the only thing is we need just some time to bake the whole space and provide all missing functionality. One of the ideas we have but not fully investigated is to have TZ providers that can plug into TimeZoneInfo and can support providing data and possibly code for some of the special TZ calculations.

@mattjohnsonpint
Copy link
Contributor

So the key problem with regard to the xplat issue is that someone has to provide the mapping data from CLDR. It could be the framework, or a library like mine, or the operating system, or some other component on the operating system. But it is indeed data dependent, and there's no getting around that.

AFAIK, there do not exist OS-level APIs for conversion between IANA and Windows time zone IDs on Windows, Linux, or Mac. So that idea falls flat.

One possibility would be to use ICU for the conversions - if ICU is available. But then what to do when it is not?

One could also use an online service to perform the conversions, but again - what to do when offline or when the service is unavailable for whatever reason?

If we go down the path of having the framework itself perform the conversions, then the framework has to provide and maintain the data. It revs in an ad-hoc and unpredictable manner, and sometimes those changes are considered urgent by some. So keeping it in a separate component makes the most sense IMHO. The best we could do with plug-ability would be to reduce the API calls needed. It wouldn't change the nature of having to update the components.

@jpenniman
Copy link

I agree providing at least the database as a separate package would allow for easier updating rather than embedded into each platform runtime. Someone would need to keep it up-to-date -- perhaps an automated process that checks for a new version daily and builds a new package if there is. I wouldn't rely on a human to do it. @mj1856, even your TimeZoneConverter library is a version behind as of this post.

Assuming we can solve the data distribution how should the API behave? My initial thoughts are all the APIs on TimeZoneInfo work with the Windows version of the zone ids. For example, TimeZoneInfo.FindSystemTimeZoneById(), would only see "Eastern Standard Time" as valid and return an instance with that Id. That enables migrations, though would break any existing born on Linux/Mac projects. It's not much different from Java, in that the same database is used across all platforms--whether you're on Windows or Linux, you have to use "America/New York" in java. Microsoft conveniently already has that database and keeps it up-to-date ;)

Another idea is to combine them. So, GetSystemTimeZoneIds() would return the full set of Windows and IANA id's combine. Then, TimeZoneInfo.FindSystemTimeZoneById() would behave similar to Matt's TZConvert.GetTimeZoneInfo() method, returning the zone for any valid id regardless of platform.

In addition to standardizing TimeZoneInfo, perhaps a new set of timezone APIs could be part of the new System.Time package which work with the IANA standard, since that's what most other platforms and languages have adopted. These apis could offer conversion methods similar to Matt's TimeZoneConverter.

Thoughts?

@Clockwork-Muse
Copy link
Contributor

...Any package distributed should be completely independent from whatever the system reports as its timezone, since there're ways to create your own (including on Windows); you're only looking in that database. Done correctly, there may be other time zone databases supplied, like for IATA, which people are unlikely to be using for their local systems.

The question then is how you get from the system time zone (assuming you actually care - many applications are going to be on a server and should absolutely not care) to a "proper" one (note you should be doing this even if the ids are the same, since otherwise the rules might be different). At that point you'd want some sort of system-to-database-mapper, probably as another package (although this one should change far less frequently)

@jpenniman
Copy link

...Any package distributed should be completely independent from whatever the system reports as its timezone, since there're ways to create your own (including on Windows); you're only looking in that database.

I agree. This is how Sun did it and how Oracle continues to do it. The java apis only use their shipped database and not the system's, regardless of the system. My thought was, for compatibility/portability, the database contains both "Windows" timezone ids AND IANA timezone ids all in one (or at least appears to from the API side of it). That covers 100% of the current OS's that support coreclr. So, regardless of the system, if I call TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"), it will resolve, whether I'm on a Windows server, CentOS server, Mac Server, etc. Likewise, if I call TimeZoneInfo.FindSystemTimeZoneById("America/New York"), it will resolve on Windows, Linux, Mac, etc.

I like the idea of making that database plug-able, so devs could use any database they choose.

assuming you actually care - many applications are going to be on a server and should absolutely not care

I disagree. That's whole reason this issue and related ones were filed--the application does care because it was initially built on Windows/Microsoft ecosystem and is being ported to, or integrating with new services hosted on Linux. Every calendaring service, including Exchange and Office 365 care very much about the timezone id. One of our customers has a business rule that time cards are due by Tuesday at noon, in the local timezone of the branch. Therefor, we have to store the timezone (and offset) of the branch.

We always use UTC server side. In that respect, yes, timezone doesn't matter. But business doesn't run on UTC, it runs in the local timezone. I agree that in the simple example, the server uses UTC and the client uses local time, but unfortunately, many scenarios are far more complex than that.

@Clockwork-Muse
Copy link
Contributor

I disagree. That's whole reason this issue and related ones were filed--the application does care because it was initially built on Windows/Microsoft ecosystem and is being ported to, or integrating with new services hosted on Linux. Every calendaring service, including Exchange and Office 365 care very much about the timezone id. One of our customers has a business rule that time cards are due by Tuesday at noon, in the local timezone of the branch. Therefor, we have to store the timezone (and offset) of the branch.

Sorry, my point wasn't clear. What I meant was, in almost no case is the actual timezone of the server itself relevant, but the timezone of the entity under view. So yeah, storing and retrieving the timezone (and offset, because of rule changes) of the branch is what I'd expect to happen. If you meant "timezone database of the server", that would have made more sense

We always use UTC server side. In that respect, yes, timezone doesn't matter. But business doesn't run on UTC, it runs in the local timezone. I agree that in the simple example, the server uses UTC and the client uses local time, but unfortunately, many scenarios are far more complex than that.

Hm, personally I think applications should work correctly regardless of the current timezone of the machine they're running on (UTC or otherwise), but that gets tricky in client mode. Or more specifically,
consumer client mode, since most corporate client machines should be pulling "business unit timezone" from a central config server somewhere, if it's relevant (since it's likely any central processing would also have to take that same timezone into account, too).

@Clockwork-Muse
Copy link
Contributor

Also, and this didn't hit me until later, we would absolutely NOT want to just pull the Windows zones in from the host box: that would be the fastest way to break an app running on both a Linux and Windows machine and using Windows zones. Consider what happens if the app is updated with the new package version, but the OS isn't, or vice versa. OS-level resources may not even be under programmer control, in some cases (ie, Functions as a Service). If we're doing a resource package, it's going to have to control all its resources.

@mattjohnsonpint
Copy link
Contributor

mattjohnsonpint commented Feb 26, 2018

@jpenniman - A version behind? If you mean WRT IANA version, no new time zone IDs have come out, and mappings are the same from CLDR.

@mattjohnsonpint
Copy link
Contributor

WRT the system's local time zone - that is a separate topic, out of scope for this discussion. The time zone data is indeed installed on the system, but we're not talking about what the time zone the system is set to (whether that's custom or standard).

WRT pulling in Windows zones from the host box - That is exactly how the TimeZoneInfo.FindSystemTimeZoneById API works, and has been working since it was created back in .NET 3.5. The Linux version is no different in that regard, it's just pulling in different data.

For that matter, .NET Core has already shipped with pulling Windows IDs on Windows, and IANA IDs on Linux/Mac. Changing that would be breaking existing code.

IMHO, there are really only two viable paths:

  • Make TimeZoneInfo.FindSystemTimeZoneById take either form of identifier - essentially merging my library in. Somehow provide data updates. This takes care of the "make it just work" factor.

  • Create some sort of "provider model" wherein one can supply the time zone data they care about instead of getting it from the OS.

I think both of these could co-exist, but I think this issue was primarily focused on the first one.

@exiled78
Copy link

For the record, I thought this might work for an app developed on windows, but deployed to AWS Lambda Function. It works, however, the catch code does not accommodate daylight savings. The windows code correctly gives the offset as +11, the catch code incorrectly gives +10. Note the tz.IsDaylightSavingTime() incorrectly returns false on linux.

                TimeZoneInfo tz;
                DateTimeOffset offset;
                try
                {
                    tz = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
                    offset = dateTime.ToOffset(tz.GetUtcOffset(dateTime));
                }
                catch (TimeZoneNotFoundException)
                {
                    //Should work on linux
                    tz = TimeZoneInfo.FindSystemTimeZoneById("Australia/Sydney");
                    offset = dateTime.ToOffset(tz.GetUtcOffset(dateTime));
                }

@tarekgh
Copy link
Member

tarekgh commented Mar 14, 2018

the catch code incorrectly gives +10. Note the tz.IsDaylightSavingTime() incorrectly returns false on linux.

This looks a bug and needs to be investigated.

@msftgits msftgits transferred this issue from dotnet/corefx Jan 31, 2020
@msftgits msftgits added this to the 5.0 milestone Jan 31, 2020
@maryamariyan maryamariyan added the untriaged New issue has not been triaged by the area owner label Feb 23, 2020
@mynkow
Copy link

mynkow commented May 5, 2020

Greetings from mid 2020.

Hala

@tarekgh tarekgh removed the untriaged New issue has not been triaged by the area owner label May 5, 2020
@tarekgh tarekgh modified the milestones: 5.0, Future May 5, 2020
@mm86133
Copy link

mm86133 commented Jul 1, 2020

Still waiting on something so crucial to be solved especially with Docker Linux/Windows.

@tarekgh
Copy link
Member

tarekgh commented Jul 1, 2020

@molekm can't you use the library https://github.com/mj1856/TimeZoneConverter?

@timmydo
Copy link

timmydo commented Aug 10, 2020

It would be nice if something like DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU (from https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/globalization-icu) existed for the tz data. TimeZoneConverter would only be a solution for people that can rewrite their code to point to the that library.

@udlose
Copy link

udlose commented Aug 19, 2020

@molekm can't you use the library https://github.com/mj1856/TimeZoneConverter?

@tarekgh With all due respect, we shouldn't have to use an external library to do something so fundamental as working with TimeZones. I had hoped that the abstraction of "concern of which OS my app is running on" should be handled by .NET Core as that was one of the main goals of the .NET Core platform. Very disappointed that this issue has been opened for a little over 5 years now with little more than "...use an external library to do this" as a solution.

Can you please provide an update on where this is in progress? .NET 5.0 is slated for release in November 2020 which is less than 3 months away now.

@tarekgh
Copy link
Member

tarekgh commented Aug 19, 2020

@udlose I agree with you in principle. The issue here we were trying to avoid in the .NET is to carry Time zone data which will need to get updated when something change. This create a nightmare in servicing old versions of the .NET especially time zone is a core functionality. That is why we were planning to get into the point to depend on something that we can use and not require servicing the .NET.

In 5.0 we started to use ICU library for globalization support but this is huge work as globalization is kind affecting most of the basic functionality in the .NET. We are trying to stabilize the product with using ICU. When I suggested using TimeZoneConverter it was a temporary till the framework introduce the functionality.

We have the plan in 6.0 to look using ICU which we already integrated in 5.0 to do the TZ Ids conversions and possibly introduce more better interfaces for time zone in general. I hope this clarify why we delayed this work but I am emphasizing this is in our near future plan to get it done. Thanks for your understanding.

@ericsampson
Copy link

@tarekgh thanks for the update. It's not a small task to do 'well'. I'm happy to see the ICU changes coming in 5.0

@udlose
Copy link

udlose commented Aug 19, 2020

@tarekgh Thank you for the update and I do appreciate the challenge you face. I am happy to see there is a plan for it in the near future and I'm looking forward to leveraging it. In the meantime, I will use the TimeZoneConverter you suggest.

@tarekgh
Copy link
Member

tarekgh commented Mar 12, 2021

This is addressed by #49412

@tarekgh tarekgh closed this as completed Mar 12, 2021
.NET Core impacting internal partners automation moved this from .NET 6 Committed to Done Mar 12, 2021
@dotnet dotnet locked as resolved and limited conversation to collaborators Apr 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
api-needs-work API needs work before it is approved, it is NOT ready for implementation area-System.DateTime
Projects
No open projects
Development

No branches or pull requests