You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用C# System.TimeZoneInfo时微软时区不全,求全面解决方案

解决微软时区列表不全的几种方案

我之前也碰到过类似的问题,微软自带的Windows时区确实存在覆盖不全的情况,尤其是一些小众或者分区更细的地区(比如你提到的格陵兰)。这里有几个靠谱的解决思路:

1. 切换到IANA时区数据库(TZDB)

从.NET Core 3.0以及.NET 5+开始,官方已经原生支持IANA时区(也就是我们常说的Olson时区),这个数据库是目前最全面、更新最及时的时区标准,完全覆盖格陵兰的所有时区(比如America/Nuuk对应格陵兰东部,America/Thule对应西北部等)。

使用起来也很简单,直接用TimeZoneInfo.FindSystemTimeZoneById传入IANA的时区ID就行:

// 获取格陵兰东部时区(Nuuk)
var nuukTimeZone = TimeZoneInfo.FindSystemTimeZoneById("America/Nuuk");
// 转换UTC时间到本地时间
DateTime utcTime = DateTime.UtcNow;
DateTime nuukLocalTime = TimeZoneInfo.ConvertTimeFromUtc(utcTime, nuukTimeZone);

如果需要和Windows时区互相转换,还可以用TimeZoneInfo.TryConvertIanaIdToWindowsIdTimeZoneInfo.TryConvertWindowsIdToIanaId方法做映射。

2. 自定义缺失的时区

如果你的项目还在使用.NET Framework(不支持IANA),或者需要针对特定场景定制时区规则,可以手动创建自定义时区。通过TimeZoneInfo.CreateCustomTimeZone方法,你可以定义时区的ID、显示名称、标准时间偏移、夏令时规则等。

举个例子,创建格陵兰西部的自定义时区:

// 定义标准时间偏移(UTC-4)
TimeSpan standardOffset = new TimeSpan(-4, 0, 0);
// 定义夏令时规则(假设3月最后一个周日到10月最后一个周日,偏移UTC-3)
TimeZoneInfo.AdjustmentRule dstRule = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(
    new DateTime(2000, 1, 1),
    DateTime.MaxValue.Date,
    new TimeSpan(1, 0, 0),
    TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 3, 1, 2, 0, 0), 3, 5, DayOfWeek.Sunday),
    TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 10, 1, 2, 0, 0), 10, 5, DayOfWeek.Sunday)
);

// 创建自定义时区
var greenlandWestTimeZone = TimeZoneInfo.CreateCustomTimeZone(
    "Greenland Western Standard Time",
    standardOffset,
    "(GMT-04:00) Greenland Western",
    "Greenland Western Standard Time",
    "Greenland Western Daylight Time",
    new[] { dstRule }
);

// 后续就可以用这个自定义时区做转换了
DateTime utcNow = DateTime.UtcNow;
DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(utcNow, greenlandWestTimeZone);

注意:自定义时区需要你自己维护夏令时规则的更新,比较适合固定规则的场景。

3. 使用第三方时区库——NodaTime

如果你的项目涉及复杂的时区处理(比如频繁的时区转换、历史时区规则查询),强烈推荐使用NodaTime。这个库由Jon Skeet主导开发,完全基于IANA时区数据库,API设计更严谨,避免了.NET原生DateTime的很多坑,而且时区数据更新及时。

使用NodaTime处理格陵兰时区的示例:

// 初始化时区提供器
var tzProvider = DateTimeZoneProviders.Tzdb;
// 获取格陵兰东部时区
var nuukZone = tzProvider["America/Nuuk"];

// UTC时间转换为本地时间
Instant utcInstant = SystemClock.Instance.GetCurrentInstant();
ZonedDateTime nuukTime = utcInstant.InZone(nuukZone);

// 本地时间转换为UTC时间
LocalDateTime localDateTime = new LocalDateTime(2024, 5, 1, 12, 0, 0);
ZonedDateTime zonedDateTime = localDateTime.InZoneStrictly(nuukZone);
Instant utcInstantFromLocal = zonedDateTime.ToInstant();

NodaTime还支持时区数据的动态更新,不需要依赖.NET框架的更新,非常灵活。


内容的提问来源于stack exchange,提问作者Tar

火山引擎 最新活动