gpt4 book ai didi

c# - .Net DateTime 与本地时间和 DST

转载 作者:太空狗 更新时间:2023-10-30 00:31:13 25 4
gpt4 key购买 nike

恐怕我不太明白.Net 的DateTime类处理本地时间戳(我住在德国,所以我的语言环境是 de_DE)。也许有人可以启发我一点;-)
DateTime可以使用年、月等参数调用构造函数。另外一个 DateTimeKind Local 的值, Utc , 或 Unspecified (=default) 可以提供。

例子:

DateTime a = new DateTime(2015, 03, 29, 02, 30, 00, DateTimeKind.Local);
DateTime b = new DateTime(2015, 03, 29, 02, 30, 00, DateTimeKind.Utc);
DateTime c = new DateTime(2015, 03, 29, 02, 30, 00, DateTimeKind.Unspecified);
DateTime d = new DateTime(2015, 03, 29, 02, 30, 00);

根据定义,值 c 和 d 是相同的。但是,如果我将所有内容相互比较,则所有四个都是相同的。在 VS 的调试器中检查对象显示 Ticks值(以及 InternalTicks)对所有人都是相同的。但是,内部 dateData值不同,但显然被比较运算符忽略。

您可能已经注意到,我构建了今年 3 月 29 日凌晨 02:30 的值。这个时刻在我们的时区中不存在,因为它被切换到夏令时跳过。所以我曾期望在构造对象时得到一个异常(exception) a ,但这并没有发生。

此外, DateTime有一个方法 ToUniversalTime()将解释为本地时间的值转换为等效的 UTC 值。为了测试,我运行了一个循环,如下所示:
DateTime dt = new DateTime(2015, 03, 29, 01, 58, 00, DateTimeKind.Local);
DateTime dtEnd = new DateTime(2015, 03, 29, 03, 03, 00, DateTimeKind.Local);
while (dt < dtEnd)
{
Log(" Localtime " + dt + " converted to UTC is " + dt.ToUniversalTime());
dt = dt.AddMinutes(1);
}

结果是:
Localtime 29.03.2015 01:58:00 converted to UTC is 29.03.2015 00:58:00
Localtime 29.03.2015 01:59:00 converted to UTC is 29.03.2015 00:59:00
Localtime 29.03.2015 02:00:00 converted to UTC is 29.03.2015 01:00:00
Localtime 29.03.2015 02:01:00 converted to UTC is 29.03.2015 01:01:00
Localtime 29.03.2015 02:02:00 converted to UTC is 29.03.2015 01:02:00
...
Localtime 29.03.2015 02:58:00 converted to UTC is 29.03.2015 01:58:00
Localtime 29.03.2015 02:59:00 converted to UTC is 29.03.2015 01:59:00
Localtime 29.03.2015 03:00:00 converted to UTC is 29.03.2015 01:00:00
Localtime 29.03.2015 03:01:00 converted to UTC is 29.03.2015 01:01:00
Localtime 29.03.2015 03:02:00 converted to UTC is 29.03.2015 01:02:00

因此,.Net 将不存在的时间戳从本地时间转换为 UTC 没有问题。此外,向现有本地时间戳添加一分钟不是本地感知的,并且会提供不存在的时间戳。

因此,添加 64 分钟后,转换后的 UTC 时间戳仅比之前大 4 分钟。

换句话说,本地时间和 UTC 之间的转换应该是一个双射,给出合法时间戳值之间的一一对应关系。

长话短说:我如何以预期的方式正确处理这个问题(根据.Net)?拥有是什么感觉 DateTimeKind如果没有正确考虑?我什至不敢问闰秒(23:59:60)是如何处理的;-)

最佳答案

迈克的回答很好。是的,DateTimeOffset几乎总是比 DateTime 更受欢迎(但不适用于所有场景)和 Noda Time在许多方面都非常优越。但是,我可以添加更多详细信息来解决您的问题和意见。

一、MSDN has this to say :

UTC time is suitable for calculations, comparisons, and storing dates and time in files. Local time is appropriate for display in user interfaces of desktop applications. Time zone-aware applications (such as many Web applications) also need to work with a number of other time zones.

...

Conversion operations between time zones (such as between UTC and local time, or between one time zone and another) take daylight saving time into account, but arithmetic and comparison operations do not.



由此我们可以得出结论,您提供的测试无效,因为它使用本地时间执行计算。它的用处仅在于它突出了 API 如何允许您打破其自己的文档化指南。一般来说,由于本地时区不存在该日期的02:00到03:00之前的时间,所以在现实世界中不太可能遇到,除非通过数学方法获得,例如每天重复不考虑 DST 的模式。

顺便说一句,Noda Time 解决这个问题的部分是 ZoneLocalMappingResolver , 用于转换 LocalDateTimeZonedDateTime通过 localDateTime.InZone方法。有一些合理的默认值,例如 InZoneStrictly , 或 InZoneLeniently ,但它并不会像您用 DateTime 说明的那样默默地移动.

关于你的断言:

In other words, converting between local time and UTC should be a bijection, giving a one-to-one correspondence between legal timestamp values.



实际上,这不是双射。 (根据 the definition of bijection on Wikipedia ,它不满足标准 3 或 4。)只有在 UTC 到本地方向的转换是一个函数。本地到 UTC 方向的转换在回弹 DST 转换期间不连续,在回退 DST 转换期间有歧义。您可能希望查看图表 in the DST tag wiki .

要回答您的具体问题:

How do I handle this correctly the intended way (according to .Net)?


DateTime dt = new DateTime(2015, 03, 29, 01, 58, 00, DateTimeKind.Local);
DateTime dtEnd = new DateTime(2015, 03, 29, 03, 03, 00, DateTimeKind.Local);

// I'm putting this here in case you want to work with a different time zone
TimeZoneInfo tz = TimeZoneInfo.Local; // you would change this variable here

// Create DateTimeOffset wrappers so the offset doesn't get lost
DateTimeOffset dto = new DateTimeOffset(dt, tz.GetUtcOffset(dt));
DateTimeOffset dtoEnd = new DateTimeOffset(dtEnd, tz.GetUtcOffset(dtEnd));

// Or, if you're only going to work with the local time zone, you can use
// this constructor, which assumes TimeZoneInfo.Local
//DateTimeOffset dto = new DateTimeOffset(dt);
//DateTimeOffset dtoEnd = new DateTimeOffset(dtEnd);

while (dto < dtoEnd)
{
Log(" Localtime " + dto + " converted to UTC is " + dto.ToUniversalTime());

// Math with DateTimeOffset is safe in instantaneous time,
// but it might not leave you at the desired offset by local time.
dto = dto.AddMinutes(1);

// The offset might have changed in the local zone.
// Adjust it by either of the following (with identical effect).
dto = TimeZoneInfo.ConvertTime(dto, tz);
//dto = dto.ToOffset(tz.GetUtcOffset(dto));
}

What is the sense of having DateTimeKind if it is not taken into account correctly?



原来, DateTime没有一种。它表现得好像种类是未指定的。 DateTimeKind是在 .NET 2.0 中添加的。

它涵盖的主要用例是防止双重转换。例如:
DateTime result = DateTime.UtcNow.ToUniversalTime();

或者
DateTime result = DateTime.Now.ToLocalTime();

在 .NET 2.0 之前,这些都会导致错误数据,因为 ToUniversalTimeToLocalTime方法必须假设输入值未转换。它会盲目地应用时区偏移量,即使该值已经在所需的时区中。

还有其他一些边缘情况,但这是主要的情况。此外,还有一种隐藏的第四种类型,用于使以下内容在回退转换期间仍能保持不明确的值。
DateTime now = DateTime.Now;
Assert.True(now.ToUniversalTime().ToLocalTime() == now);

Jon Skeet 有 a good blog post about this ,您现在也可以在 the .NET Reference sources 的评论中看到它的讨论。或在 the new coreclr sources .

I don't even dare to ask how leap seconds (at 23:59:60) are handled ;-)



.NET 实际上根本不支持闰秒,包括当前版本的 Noda Time。它们也不受任何 Win32 API 支持,您也不会在 Windows 时钟上观察到闰秒。

在 Windows 中,闰秒通过 NTP 同步应用。时钟滴答作响,就好像闰秒没有发生一样,在下一次时钟同步期间,时间会被调整并被吸收。这是下一个闰秒的样子:

Real World              Windows
-------------------- --------------------
2015-06-30T23:59:58Z 2015-06-30T23:59:58Z
2015-06-30T23:59:59Z 2015-06-30T23:59:59Z
2015-06-30T23:59:60Z 2015-07-01T00:00:00Z <-- one sec behind
2015-07-01T00:00:00Z 2015-07-01T00:00:01Z
2015-07-01T00:00:01Z 2015-07-01T00:00:02Z
2015-07-01T00:00:02Z 2015-07-01T00:00:02Z <-- NTP sync
2015-07-01T00:00:03Z 2015-07-01T00:00:03Z

我在午夜过后 2 秒显示同步,但它真的可能要晚得多。时钟同步一直发生,而不仅仅是在闰秒。计算机的本地时钟不是超精密仪器 - 它会漂移,并且必须定期进行校正。您不能假设当前时间总是单调增加 - 它可以向前跳跃或向后跳跃。

此外,上面的图表并不完全准确。我展示了以秒为单位的硬转变,但实际上,操作系统通常会通过在几秒钟(一次几毫秒)的较长时间段内以几个亚秒级增量分散变化的影响来引入较小的更正。

在 API 级别,没有一个 API 支持秒字段中超过 59 个。如果他们完全支持它,它可能只是在解析期间。
DateTime.Parse("2015-06-30T23:59:60Z")

这将引发异常。如果它要工作,它必须修改额外的闰秒并返回前一秒( 2015-06-30T23:59:59Z )或下一秒( 2015-07-01T00:00:00Z )。

关于c# - .Net DateTime 与本地时间和 DST,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29037121/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com