在C#中使用夏令时处理时区

时间:2018-04-22 10:54:46

标签: c# .net timezone

我们有一个与航空相关的应用程序,特别是航班。

时间必须存储在本地,所以我选择使用UTC时间+偏移量,但现在我意识到这是一个糟糕的选择:

通过将时区存储为偏移量,我会忘记原始时区,这对处理夏令时有影响。

例如,我可以在Alpine,UT中将时间存储为UTC时间和-6偏移量,也可以在亚利桑那州凤凰城以UTC时间和-6偏移量存储时间。

但是当夏令时到来时,阿尔卑斯山的时间会发生变化,而菲尼克斯则不会。

所以,我需要存储正确的时区,我已经看到也有不同的语法列表,所以我假设有不同的标准。

在C#中,使用本地时区存储本地时间以使其适用于夏令时更改的最佳选择是什么?

2 个答案:

答案 0 :(得分:3)

根据问题评论中的讨论,我了解到您正在处理航班时刻表 - 即未来航班打算离开的时间。这确实是当地时间比UTC时间更重要的情况。

由于您有当地的出发时间和地点(例如:盐湖城下午5:00),那么您应该在您的数据库中存储预定的出发时间两个值:

  • 17:00 - 当地的相关出发时间
  • SLC - 时间相关的位置

如果这是此航班的特定次出现,那么您也应该存储日期:

  • 2018-06-01T17:00 - 特定相关的当地出发时间
  • SLC - 当地时间相关的位置

这些是与您的业务用例在内容相关的详细信息。不要将它们转换为UTC 而忽视它们。

也就是说,您可以考虑将它们存储为DateTimeOffset2018-06-01T17:00-06:00),这会使得为给定实例转换为UTC微不足道。但是这种方法存在两个问题:

  • 它不能用于重复,因为偏移可能会改变。
  • 即使对于单个实例,偏移可能会更改 - 如果控制时区的政府决定在该事件发生之前更改其标准偏移或夏令时规则。如果您采用DateTimeOffset方法或基于UTC的方法,则必须准备好在面对此类更改时重新计算未来事件。 (有关详情,请参阅我的博客文章: On the Timing of Time Zone Changes Time Zone Chaos Inevitable in Egypt 。还有无数其他示例。)

关于位置 - 因为您正在处理适用于航空业的数据,我建议使用IATA机场代码,例如我在上面显示的SLC。在其他情况下,可以存储IANA时区标识符,如America/Denver,或Windows时区标识符,如Mountain Standard Time

您可能会发现my "Airport Time Zones" gist (code and output table)对于使用IATA机场代码非常有用。您必须决定数据将如何流经您的系统。如果您在Windows上运行并希望使用TimeZoneInfo类将时间转换为不同的时区,请使用此处显示的Windows时区ID。如果您想使用IANA时区ID,请考虑使用Noda Time,或者您可以使用我的TimeZoneConverter库。这里有几种不同的选择,所以要仔细探索它们并选择对你有意义的选项。

野田时间将是一个很好的选择,恕我直言。您不仅可以获得出色的时区支持,而且还可以使用LocalTimeLocalDateTime等类型,与所描述的方案完美匹配。

答案 1 :(得分:1)

正如我在评论中所写,存储本地日期。而是将datetime值存储为UTC,并在需要显示时转换为本地日期时间 您可以使用ConvertTimeFromUtc类的TimeZoneInfo方法。

这意味着您还必须保留一个位置列表以及它们所关联的TimeZoneInfo - 例如,
JerusalemIsrael Standard Time相关联 Rome W. Europe Standard Time,{。} Hawaii Hawaiian Standard Time的{​​{1}} 等等。 (我敢打赌你可以在某处找到这样的在线清单。)
请注意,ConvertTimeFromUtc方法也可以为您处理夏令时问题。

然后你可以做这样的事情来按地点获取当地时间:

DateTime GetLocalDateByCityName(DateTime utc, string cityName)
{
    var timeZoneInfoId = GetTimeZoneInfoIdByCityName(string cityName);
    return TimeZoneInfo.ConvertTimeFromUtc(utc, TimeZoneInfo.FindSystemTimeZoneById(timeZoneInfoId);
}

当然,在GetTimeZoneInfoIdByCityName中,您可以获得特定城市的TimeZoneInfoId。