.Net DateTime 包含本地时间和夏令时

2023-12-08

恐怕我不太明白.Net 是如何实现的DateTime类处理本地时间戳(我住在德国,所以我的语言环境是 de_DE)。也许有人可以启发我一点;-)

The DateTime可以使用年、月等参数调用构造函数。另外还有一个DateTimeKind的价值Local, Utc, or Unspecified可以提供(=默认)。

Example:

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 的值。我们的时区不存在这一时刻,因为切换到夏令时会跳过这一时刻。所以我预计会得到构造对象的异常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(但不适合all场景),以及野田时间在很多方面都非常优越。不过,我可以添加更多详细信息来解决您的问题和意见。

First, MSDN有这样的说法:

UTC 时间适用于计算、比较以及在文件中存储日期和时间。本地时间适合显示在桌面应用程序的用户界面中。时区感知应用程序(例如许多 Web 应用程序)还需要与许多其他时区一起工作。

...

时区之间的转换操作(例如 UTC 与当地时间之间,或一个时区与另一个时区之间)会考虑夏令时,但算术和比较操作则不会。

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

顺便说一句,野田时间解决这个问题的部分是ZoneLocalMappingResolver,在转换时使用LocalDateTime to a ZonedDateTime通过localDateTime.InZone方法。有一些合理的默认值,例如InZoneStrictly, or InZoneLeniently,但它并不只是像你所展示的那样默默地转移DateTime.

关于你的主张:

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

事实上,这不是双射。 (经过维基百科上双射的定义,它不满足条件 3 或 4。)只有 UTC 到本地方向的转换才是函数。本地到 UTC 方向的转换在前向 DST 转换期间存在不连续性,并且在后退 DST 转换期间存在模糊性。您可能希望查看图表在 DST 标签 wiki 中.

回答您的具体问题:

我如何以预期的方式正确处理这个问题(根据.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));
}

如果没有正确考虑,拥有 DateTimeKind 有何意义?

起初,DateTime没有一种。它的表现就好像种类是未指定的。DateTimeKind已在 .NET 2.0 中添加。

它涵盖的主要用例是防止双重转换。例如:

DateTime result = DateTime.UtcNow.ToUniversalTime();

or

DateTime result = DateTime.Now.ToLocalTime();

在 .NET 2.0 之前,这些都会导致错误数据,因为ToUniversalTime and ToLocalTime方法必须假设输入值是not转换。它会盲目地应用时区偏移,即使该值是already在所需的时区。

还有一些其他边缘情况,但这是主要的情况。另外,还有一个隐藏的fourthkind,它的使用使得以下内容在回退过渡期间仍能保持不明确的值。

DateTime now = DateTime.Now;
Assert.True(now.ToUniversalTime().ToLocalTime() == now);

乔恩·斯基特有关于此的一个很好的博客文章,您现在还可以在评论中看到它的讨论.NET 参考源 or in 新的 coreclr 源.

我什至不敢问闰秒(23:59:60)是如何处理的;-)

实际上.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 秒的字段。如果他们were要完全支持它,可能只是在解析过程中。

DateTime.Parse("2015-06-30T23:59:60Z")

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.Net DateTime 包含本地时间和夏令时 的相关文章

  • codecvt 不是 std 标头吗?

    此代码使用 Visual C 11 进行编译 并在 Windows 7 上按预期运行 但无法使用 Windows 7 上的 MinGW 4 7 0 或 Linux 上的 gcc 4 8 0 进行编译 编译用 std c 11 flag in
  • 从 GetLastError() 函数返回的错误代码中获取文本

    我需要获取从 GetLastError 函数获得的错误代码的文本 我看到了一些示例 但我想要一个获取代码并返回字符串的函数 谢谢大家 我猜你想要这样的东西 DWORD dwLastError GetLastError TCHAR lpBuf
  • 调用事件,h(args) 与 EventName?.Invoke()

    我总是这样调用事件 void onSomeEvent string someArg var h this EventName if h null h this new MyEventArgs someArg 今天 VS 2015 告诉我这可
  • 如何利用磁盘 IO 队列

    我需要从 3 7 GB 文件中读取小数据序列 我需要阅读的职位是不相邻 但我可以命令 IO 以便从头到尾读取文件 该文件存储在 iSCSI SAN 上 该 SAN 应该能够处理 优化排队 IO 问题是 如何一次性请求我需要的所有数据 位置
  • 在 C# 中将“set”添加到接口的属性中

    我希望通过为该接口中的属性提供设置访问器来 扩展 该接口 界面看起来像这样 interface IUser string UserName get 我想要这样的东西 interface IMutableUser IUser string U
  • 将一个文件写入.c中的另一个文件

    我有一个读取文件然后将其内容复制到另一个文件的代码 我需要使其仅复制每 20 个符号 然后跳过 10 个符号 然后再次跳过 20 个符号 依此类推 我必须使用 lseek 函数 但我不知道如何将所有这些放入循环中来执行此操作 main ar
  • 如何混淆整数?

    我需要从 C 中的整数列表生成唯一值的列表 我以为是 MD5 或类似的 但它们生成了太多字节 整数大小为 2 个字节 例如 我想获得单向通信 0 gt ARY812Q3 1 gt S6321Q66 2 gt 13TZ79K2 因此 在证明哈
  • 如何使用 Moq 模拟 Web 服务调用?

    The using下面点击了我不想实际点击的外部资源 我想测试someResult以及使用它的代码 但每次我运行单元测试时 该代码仍然尝试访问真正的 Web 服务 如何使用最小起订量来伪造对 Web 服务的真实调用 但不模拟使用中的其余代码
  • 如何BSWAP 64位寄存器的低32位?

    我一直在寻找如何将 BSWAP 用于 64 位寄存器的低 32 位子寄存器的答案 例如 0x0123456789abcdef位于 RAX 寄存器内 我想将其更改为0x01234567efcdab89用一条指令 因为性能 所以我尝试了以下内联
  • 如何测试抽象类的受保护抽象方法?

    我一直在研究测试名为的抽象类的最佳方法TabsActionFilter 我保证继承自的类TabsActionFilter将有一个名为GetCustomer 在实践中 这种设计似乎效果很好 我遇到的一些问题是弄清楚如何测试OnActionEx
  • Windows 10 ScrollIntoView() 不会滚动到列表视图中间的项目

    我有一个包含 20 个项目的列表视图 我想以编程方式滚动列表视图 ListView ScrollIntoView ListView Items 0 将滚动列表视图到第一项 ListView ScrollIntoView ListView I
  • DISM.exe 返回代码?

    我有一个程序调用 dism exe 程序 它在后台运行一些命令 现在 我只检查返回代码 0 或其他任何内容 以显示进程失败或成功 我可以用什么来交叉检查返回代码以获得准确的返回错误 DISM 参考了哪些回报 评论中提供的链接DISMAPI
  • 修改排列算法以防止重复打印输出的策略

    我一直在审查实践算法 目前正在研究一种我非常喜欢的排列算法 void permute char set int begin int end int range end begin if range 1 cout lt lt set lt l
  • Eclipse 调试模式下的 GDB 找不到 stdlib/rand.c

    我试图让 gdb 在 ubuntu 上与 eclipse cdt 一起运行 以开始调试一些简单的程序 所以我做了我认为必要的步骤来让它运行 1 创建可执行项目 2 Compile 3 Run 4 创建文件 gdbinit 并将其放在主项目文
  • ASP.NET/ADO.NET:处理 .NET 对象内的许多数据库连接?

    我们有一个 NET 对象 它对数据库进行大量读 写操作 在该对象 或使用它的 ASP 页 的整个生命周期中 它可能会通过查询 更新来访问数据库 1 到 10 次 它不是在每次对象需要访问数据库时打开和关闭数据库连接 而是只是在实例化期间打开
  • 文件/文件夹结构的递归搜索

    我正在尝试为返回文件和文件夹列表的 Web 服务构建递归搜索功能 我创建了这两个方法 因此它们充当递归搜索 它首先获取顶级内容 然后将任何文件添加到 fileList 并将任何子文件夹添加到 subFoldersList 我们传入访问级别
  • 检测 Windows 重新启动是否是由于 Windows 更新造成的

    我的电脑上的一些应用程序一直在检测 Windows 更新是否重新启动 这是可以观察到的 因为它们会在 Windows 更新自动重启后重新启动 这非常有帮助 因为这些应用程序会重新加载更改 甚至unsaved更改或恢复选项卡 如果是浏览器 执
  • 警告 C4172:返回局部变量或临时变量的地址[重复]

    这个问题在这里已经有答案了 可能的重复 指向局部变量的指针 https stackoverflow com questions 4570366 pointer to local variable 我在这个网站上阅读了很多关于同一问题的其他主
  • 在 C# 中调用并排显示窗口

    愚蠢的问题是否有一种简单的方法可以清除桌面 然后打开两个资源管理器窗口并调用 并排显示窗口 任务栏调用 只是想知道 MS 库中是否有 api 可以做到这一点 您可以使用TileWindowsWinAPI 函数通过 p invoke 将所需窗
  • 如何从 dll 导出 C++ 类? [复制]

    这个问题在这里已经有答案了 我有一个有两个重载函数的类 如何从 dll 导出它以及如何由其他 C 类使用它 我的班级是这样的 define DECLDIREXP declspec dllexport define DECLDIRIMP de

随机推荐