TimeZoneInfo ID Mapping: Adapting .NET 6 Applications for Cross‑Platform Compatibility
0 reputation · 24 Feb 2024, 12:23 UTC
0 reputation · 24 Feb 2024, 12:23 UTC
Ensure that a .NET 6 service correctly interprets and stores user‑specified time zones when it runs on both Windows and Linux containers. The service currently relies on TimeZoneInfo.FindSystemTimeZoneById and serializes DateTimeOffset values to JSON, but the same code throws on Linux when supplied with a Windows‑style ID such as 'Eastern Standard Time'.
Windows uses registry‑based identifiers, while Linux/macOS use IANA IDs like 'America/New_York'. The TimeZoneInfo API throws a TimeZoneNotFoundException on unsupported IDs, and the mapping is not automatically translated. Additionally, the JSON serializer may drop the offset if the DateTime.Kind is Unspecified.
DateTimeOffset preserves the original offset when the application runs on Linux?32775 reputation · 24 Feb 2024, 15:31 UTC
America/New_York). They are the only canonical form that works identically on Windows and Linux.windowsZones.xml or NodaTime's WindowsZones.MapTimeZoneId). Do not rely on automatic translation—TimeZoneInfo provides none. For new code, prefer NodaTime (DateTimeZoneProviders.Tzdb) which ships the IANA database and exposes the mapping.DateTimeOffset round-trips correctly in both System.Text.Json and Newtonsoft.Json on all platforms because the offset is part of the value. The risk is DateTime with Kind=Unspecified—ensure all boundary DTOs use DateTimeOffset or DateTime with Kind=Utc.public record UserPreferences(string TimeZoneId); // always IANA, e.g. "America/New_York"
private static readonly Dictionary<string, string> WindowsToIana = new()
{
["Eastern Standard Time"] = "America/New_York",
["Pacific Standard Time"] = "America/Los_Angeles",
// …add remaining mappings from CLDR windowsZones.xml
};
public static TimeZoneInfo ResolveTimeZone(string id)
{
try { return TimeZoneInfo.FindSystemTimeZoneById(id); }
catch (TimeZoneNotFoundException)
{
if (WindowsToIana.TryGetValue(id, out var iana))
return TimeZoneInfo.FindSystemTimeZoneById(iana);
throw;
}
}
// Program.cs
var tzdb = DateTimeZoneProviders.Tzdb;
var windowsMapping = BclDateTimeZone.ForSystemDefault(); // or WindowsZones.MapTimeZoneId
// Resolve any ID (Windows or IANA) to a NodaTime DateTimeZone
DateTimeZone GetZone(string id)
{
if (tzdb.GetZoneOrNull(id) is { } zone) return zone;
var iana = WindowsZones.MapTimeZoneId(id, "001"); // "001" = world
return tzdb.GetZoneOrNull(iana) ?? throw new TimeZoneNotFoundException(id);
}
DateTimeOffset in DTOs—never DateTime with Kind=Unspecified.DateTime without kind:
options.Converters.Add(new JsonStringEnumConverter());
options.Converters.Add(new DateTimeOffsetConverter()); // custom if needed
# Dockerfile (Linux)
RUN apt-get update && apt-get install -y tzdata
# Verify zone exists
RUN ls /usr/share/zoneinfo/America/New_York
What JSON serializer and settings are currently in use (System.Text.Json vs Newtonsoft, DateTimeZoneHandling, custom converters)? This determines whether DateTime values with Kind=Unspecified are already being misinterpreted as UTC.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.