If I have some UTC time, how do I get in C# the corresponding local time in Poland? Or in France? Or in Equador?
On Windows:
var poland = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.UtcNow, "Central European Standard Time");
var france = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.UtcNow, "Romance Standard Time");
var ecuador1 = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.UtcNow, "SA Pacific Standard Time"); // (Mainland)
var ecuador2 = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.UtcNow, "Central America Standard Time"); // (Galapagos Is.)
On Linux, OSX, and other .NET Core platforms:
var poland = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.UtcNow, "Europe/Warsaw");
var france = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.UtcNow, "Europe/Paris");
var ecuador1 = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.UtcNow, "America/Guayaquil"); // (Mainland)
var ecuador2 = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTimeOffset.UtcNow, "Pacific/Galapagos"); // (Galapagos Is.)
(Note that Ecuador has two time zones, one for the mainland, one for the Galapagos Islands.)
A good reference for figuring out which Windows ID to use is the CLDR windowsZones.xml
file, which is the source of truth for converting between Windows and IANA time zone identifiers.
(I understand this doesn't make sense for big countries like Russia where many times are used)
Indeed, Russia has many different time zone identifiers. One would have to know what part of Russia they were interested in.
In most the existing examples people use a TimeZoneInfo
object:
var zone = TimeZoneInfo.FindSystemTimeZoneById("SE Asia Standard Time");
The problem is that it's not enough. Many countries have something
like "summer time" or "daylight saving
time", where they
use a different time offset to UTC depending on the day of the year
and even exact hour (in the day of time change).
For example, rather than knowing whether I need the Central Daylight
Time
or Central Standard Time
option, I want to be able to pick
America/Chicago
and let it handle that for me.
No, that's not a problem. Likely you're confused about the requirement because the word "Standard" is in the name of the identifier. Despite this, a TimeZoneInfo
object represents the entirety of the time zone, including both standard time and daylight/summer time.
To clarify, the TimeZoneInfo.FindSystemTimeZoneById
method takes an identifier. The resulting object's Id
property will then contain that identifier.
"America/Chicago"
is an IANA time zone identifer.
"Central Standard Time"
is both a Windows time zone identifier, and an English localized "standard name" of the same zone (on Windows).
"Central Daylight Time"
is an English localized "daylight name" of that zone (on Windows). It is not a valid time zone identifier.
Are there any C# built-in functions that deal with this, or do I need
to handle all the calculations where summer time begins and ends
according to the law of a chosen country myself?
All of the built-in calculations handle this. Only pass valid identifiers, and the framework will determine the correct offset from UTC regardless of whether standard time or daylight/summer time is in effect.
Here is a working example that demonstrates these concepts:
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time"); // Use "America/Chicago" on non-Windows systems
Console.WriteLine("Id: " + tzi.Id);
Console.WriteLine("DisplayName: " + tzi.DisplayName);
Console.WriteLine("StandardName: " + tzi.StandardName);
Console.WriteLine("DaylightName: " + tzi.DaylightName);
Console.WriteLine();
DateTime winterDate = new DateTime(2020, 1, 1);
DateTime summerDate = new DateTime(2020, 7, 1);
TimeSpan winterOffset = tzi.GetUtcOffset(winterDate);
TimeSpan summerOffset = tzi.GetUtcOffset(summerDate);
bool winterIsDst = tzi.IsDaylightSavingTime(winterDate);
bool summerIsDst = tzi.IsDaylightSavingTime(summerDate);
Console.WriteLine("Winter Offset: " + winterOffset + " IsDST: " + winterIsDst);
Console.WriteLine("Summer Offset: " + summerOffset + " IsDST: " + summerIsDst);
Output:
Id: Central Standard Time
DisplayName: (UTC-06:00) Central Time (US & Canada)
StandardName: Central Standard Time
DaylightName: Central Daylight Time
Winter Offset: -06:00:00 IsDST: False
Summer Offset: -05:00:00 IsDST: True
Lastly, if you want to be able to use either set of identifiers on any operating system, there are two different ways you can handle that:
You can retrieve a TimeZoneInfo
with my TimeZoneConverter library. For example:
// Either of these will work on any platform:
TimeZoneInfo tzi = TZConvert.GetTimeZoneInfo("America/Chicago");
TimeZoneInfo tzi = TZConvert.GetTimeZoneInfo("Central Standard Time");
You can use Noda Time instead of TimeZoneInfo
, which has ZonedDateTimeProviders
for both Bcl (Windows) and Tzdb (IANA) identifiers.
// This will work on any platform:
DateTimeZone dtz = DateTimeZoneProviders.Tzdb["America/Chicago"];
// This will work with Windows on .NET Framework only
DateTimeZone dtz = DateTimeZoneProviders.Bcl["Central Standard Time"];
You might also want to read the timezone tag wiki here on Stack Overflow. Especially the section titled "Time Zone Databases".