当服务器设置为 UTC 时创建夏时制感知 DateTime

Posted

技术标签:

【中文标题】当服务器设置为 UTC 时创建夏时制感知 DateTime【英文标题】:Creating daylight savings-aware DateTime when server set to UTC 【发布时间】:2014-01-23 14:25:57 【问题描述】:

我的应用程序托管在 Windows Azure 上,所有服务器都设置为 UTC。

我需要知道任何给定的DateTime 何时受夏令时限制。为简单起见,我们可以假设我的用户都在英国(因此使用格林威治标准时间)。

我用来转换 DateTime 对象的代码是

public static DateTime UtcToUserTimeZone(DateTime dateTime)

    dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);

    var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Greenwich Standard Time");

    var userDateTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);

    return DateTime.SpecifyKind(userDateTime, DateTimeKind.Local);

但是,以下测试在最后一行失败;转换后的DateTime 不知道它应该在夏令时。

[Test]
public void UtcToUserTimeZoneShouldWork()

    var utcDateTime = new DateTime(2014, 6, 1, 12, 0, 0, DateTimeKind.Utc);
    Assert.IsFalse(utcDateTime.IsDaylightSavingTime());

    var dateTime = Utils.UtcToUserTimeZone(utcDateTime);
    Assert.IsTrue(dateTime.IsDaylightSavingTime());

请注意,这仅在我的 Windows 时区设置为 (UTC) 协调世界时 时才会失败。当它设置为 (UTC) 都柏林、爱丁堡、里斯本、伦敦(或任何其他遵守夏令时的北半球时区)时,测试通过。如果您在 Windows 中更改此设置,则需要重新启动 Visual Studio 才能使更改完全生效。

我错过了什么?

【问题讨论】:

【参考方案1】:

您的最后一行指定该值位于运行它的系统的本地时区 - 但它不是真的...它是使用不同的时区转换的。

在我看来,基本上DateTime 是somewhat broken - 这使得它很难正确使用。没有办法说“这个 DateTime 值在时区 x 中” - 您可以执行转换以找到特定的本地时间,但这不记得时区。

例如,来自DateTime.IsDaylightSavingTime 的文档(强调我的):

该方法判断当前的 DateTime 值是否在 TimeZoneInfo.Local 属性返回的本地时区的夏令时范围内。

显然我有偏见,但我建议使用我的 Noda Time 项目来代替 - 很可能使用 ZonedDateTime。您可以调用GetZoneInterval 来获取那个ZonedDateTimeZoneInterval,然后使用ZoneInterval.Savings 来检查当前偏移量是否包含夏令时组件:

bool daylight = zonedDateTime.GetZoneInterval().Savings != Offset.Zero;

这有点啰嗦...我添加了一个feature request 以考虑将IsDaylightSavingTime() 方法添加到ZonedDateTime 以及...

【讨论】:

@MattJohnson:好吧,它目前还不是核心库的一部分——但它可能应该是。在那之前,扩展方法肯定有效...... 谢谢乔恩,我想知道这是否是一个尝试 NodaTime 的好机会。看起来马特的建议可以为我解决这个问题。 @RichardEv:嗯,我认为这只是另一个说明为什么 BCL 类型(和时区 ID!)很难使用。继续,试试 Noda Time……你知道你想:) 好吧,我将在我的积压工作中添加 rework codebase 以使用 Noda Time。 ;-)【参考方案2】:

您缺少的主要内容是“格林威治标准时间”不是伦敦的TimeZoneInfo id。那个实际上属于“(UTC)蒙罗维亚,雷克雅未克”。

您想要“GMT 标准时间”,它映射到“(UTC) Dublin, Edinburgh, Lisbon, London”

是的,Windows 时区很奇怪。 (至少你不住在法国,这被奇怪地贴上了“浪漫标准时间”的标签!)

另外,你不应该做这部分:

return DateTime.SpecifyKind(userDateTime, DateTimeKind.Local);

这将使其他各种代码认为它来自本地机器的时区。将其保留为“未指定”类型。 (或者更好的是,使用DateTimeOffset 而不是DateTime

那么你还需要在TimeZoneInfo对象上使用.IsDaylightSavingsTime方法,而不是.DateTime对象上的方法。 (有两个不同的方法,名称相同,作用于不同的对象,具有不同的行为。哎哟!)

但我完全同意这太复杂且容易出错。只需使用Noda Time。你会很高兴你做到了!

【讨论】:

目前,在TimeZoneInfo 上使用.IsDaylightSavingsTime 方法将满足我的要求,非常感谢。

以上是关于当服务器设置为 UTC 时创建夏时制感知 DateTime的主要内容,如果未能解决你的问题,请参考以下文章

如何在夏时制开始时跳过的一小时内将时间戳存储在 Oracle 中

当存储为 UTC 时,如何全局序列化和反序列化 Date 与 DateTime?

可感知 tz 的日期时间序列在 pandas 系列应用(lambda)操作中产生基于 UTC 的 .date() 输出

从UTC转换为任何时区

测试正确的时区处理

在 Python 中获取随机时区感知日期时间