As described in the timezone tag wiki, there are two different styles of time zones.

  • Those provided by Microsoft for use with Windows and the .Net TimeZoneInfo class (when running on Windows) are identified by a value such as "Eastern Standard Time".

  • Those provided by IANA in the TZDB, and used by the .NET TimeZoneInfo class when running on Linux or OSX, are identified by a value such as "America/New_York".

Many Internet-based APIs use the IANA time zones, but for numerous reasons one might need to convert this to a Windows time zone id, or vice-versa.

How can this be accomplished in .Net?

Solution 1

Current Status:

Starting with .NET 6, both forms of time zones are supported on any platform that has both time zone data and ICU installed, which is most installations of Windows, Linux, and MacOS. See Tobias's answer.

Original Answer:

The primary source of the data for conversion between Windows and IANA time zone identifiers is the windowsZones.xml file, distributed as part of the Unicode CLDR project. The latest dev version can be found here.

However, CLDR is released only twice annually. This, along with the periodic cadence of Windows updates, and the irregular updates of the IANA time zone database, makes it complicated to just use the CLDR data directly. Keep in mind that time zone changes themselves are made at the whim of the world's various governments, and not all changes are made with sufficient notice to make it into these release cycles before their respective effective dates.

There are a few other edge cases that need to be handled that are not covered strictly by the CLDR, and new ones pop up from time to time. Therefore, I've encapsulated the complexity of the solution into the TimeZoneConverter micro-library, which can be installed from Nuget.

Using this library is simple. Here are some examples of conversion:

string tz = TZConvert.IanaToWindows("America/New_York");
// Result:  "Eastern Standard Time"

string tz = TZConvert.WindowsToIana("Eastern Standard Time");
// result:  "America/New_York"

string tz = TZConvert.WindowsToIana("Eastern Standard Time", "CA");
// result:  "America/Toronto"

There are more examples on the project site.

It's important to recognize that while an IANA time zone can be mapped to a single Windows time zone, the reverse is not true. A single Windows time zone might be mapped to more than one IANA time zone. This can be seen in the above examples, where Eastern Standard Time is mapped to both America/New_York, and to America/Toronto. TimeZoneConverter will deliver the one that CLDR marks with "001", known as the "golden zone", unless you specifically provide a country code and there's a match for a different zone in that country.

Note: This answer has evolved over the years, so comments below may or may not apply to the current revision. Review the edit history for details. Thanks.

Solution 2

Starting with .NET 6, it is finally possible to work with time zones in a cross-platform manner, so these manual workarounds are no longer needed.

The TimeZoneInfo.FindSystemTimeZoneById(string) method automatically accepts either Windows or IANA time zones on either platform and converts them if needed.

// Both of these will now work on any supported OS where ICU and time zone data are available.
TimeZoneInfo tzi1 = TimeZoneInfo.FindSystemTimeZoneById("AUS Eastern Standard Time");
TimeZoneInfo tzi2 = TimeZoneInfo.FindSystemTimeZoneById("Australia/Sydney");

Note that, as specified on the link, the .NET Core Alpine Linux-based Docker images do not have the necessary tzdata installed by default, so it must be installed in your Dockerfile for this to work correctly.

Solution 3

I know this is an old question, but I had a use case I though I would share here, since this is the most relevant post I found when searching. I was developing a .NET Core app using a docker linux container, but for deployment on a windows server. So I only needed my docker linux container to support the windows timezone names. I got this working without changing my application code by doing the following:

cp /usr/share/zoneinfo/America/Chicago "/usr/share/zoneinfo/Central Standard Time"
cp /usr/share/zoneinfo/America/New_York "/usr/share/zoneinfo/Eastern Standard Time"
cp /usr/share/zoneinfo/America/Denver "/usr/share/zoneinfo/Mountain Standard Time"
cp /usr/share/zoneinfo/America/Los_Angeles "/usr/share/zoneinfo/Pacific Standard Time"

Then, in my .NET code, the following worked without any modification: TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time")