小编典典

日期时间与日期时间偏移

all

DateTimea和 a 有什么区别,什么DateTimeOffset时候应该使用?


目前,我们有一种以 TimeZone-aware 方式处理 .NETDateTime的标准方法:每当我们生成 a时,DateTime我们都会使用
UTC(例如使用DateTime.UtcNow),并且每当我们显示时,我们都会从 UTC 转换回用户的本地时间.

这很好用,但我一直在阅读DateTimeOffset以及它如何在对象本身中捕获本地和 UTC 时间。


阅读 128

收藏
2022-02-28

共1个答案

小编典典

DateTimeOffset瞬时时间 (也称为 绝对时间
)的表示。我的意思是每个人都通用的时间点(不考虑闰秒,或时间膨胀的相对论效应)。另一种表示瞬时时间的方法是使用DateTimewhere
.Kindis DateTimeKind.Utc

这与 日历时间 (也称为 民用时间 )不同,后者是某人日历上的一个位置,全球有许多不同的日历。我们称这些日历 为时区
。日历时间由DateTimewhere
.KindisDateTimeKind.Unspecified或表示DateTimeKind.Local。并且.Local仅在您对使用结果的计算机的位置有隐含理解的情况下才有意义。(例如,用户的工作站)

那么,为什么DateTimeOffset不是 UTCDateTime呢? 这都是关于视角的。 让我们打个比方——我们会假装自己是摄影师。

想象一下,您站在日历时间线上,将相机对准摆在您面前的瞬时时间线上的一个人。您根据您的时区规则排列您的相机 -
由于夏令时或您的时区法律定义的其他更改,这些规则会定期更改。(你的手不稳,所以你的相机会摇晃。)

站在照片中的人会看到你的相机来自的角度。如果其他人在拍照,他们可能从不同的角度。这就是代表的Offset部分DateTimeOffset

因此,如果您将相机标记为“东部时间”,则有时您从-5 指向,有时您从-4
指向。世界各地都有摄像机,它们都标记了不同的事物,并且都从不同的角度指向同一个瞬时时间线。其中一些彼此相邻(或重叠),因此仅知道偏移量不足以确定时间与哪个时区相关。

那么UTC呢?好吧,这是保证手部稳定的唯一相机。它在三脚架上,牢固地固定在地面上。它不会去任何地方。我们将其视角称为零偏移。

瞬时时间与日历时间可视化

那么 - 这个类比告诉我们什么?它提供了一些直观的指导方针——

  • 如果您要表示相对于某个特定地点的时间,请在日历时间中使用DateTime. 请确保您永远不会将一个日历与另一个日历混淆。 Unspecified应该是你的假设。 Local只对来自 有用DateTime.Now。例如,我可能会获取DateTime.Now它并将其保存在数据库中 - 但是当我检索它时,我必须假设它是Unspecified. 我不能相信我的本地日历与最初的日历相同。

  • 如果您必须始终确定时刻,请确保您代表的是瞬时时间。用于DateTimeOffset强制执行,或DateTime按惯例使用 UTC。

  • 如果您需要跟踪瞬时时间,但您还想知道“用户认为这是他们本地日历上的什么时间?” - 那么你 必须 使用DateTimeOffset. 这对于计时系统非常重要,例如,对于技术和法律问题。

  • 如果您需要修改以前记录的DateTimeOffset- 您在偏移量中没有足够的信息来确保新的偏移量仍然与用户相关。您 必须存储一个时区标识符(想想 - 我需要该相机的名称,这样即使位置发生了变化,我也可以拍摄新照片)。

还应该指出的是,Noda Time对此有一个表示ZonedDateTime,而 .Net
基类库没有类似的东西。您需要同时存储 aDateTimeOffset和 aTimeZoneInfo.Id值。

  • 有时,您会想要表示“查看它的人”的本地日历时间。例如,在定义 今天的 含义时。今天总是午夜到午夜,但这些代表了瞬时时间线上几乎无限数量的重叠范围。(实际上,我们有有限数量的时区,但您可以将偏移量表达到滴答声)因此,在这些情况下,请确保您了解如何限制“谁在问?” 质疑到单个时区,或酌情将它们转换回瞬时时间。

这里有一些关于DateTimeOffset支持这个类比的其他一些小细节,以及一些保持它直截了当的技巧:

  • 如果您比较两个DateTimeOffset值,在比较之前它们首先被归一化为零偏移。换句话说,2012-01-01T00:00:00+00:002012-01-01T02:00:00+02:00指的是同一瞬间,因此是等价的。

  • 如果您正在进行任何单元测试并且需要确定偏移量,请分别测试值 DateTimeOffset属性.Offset

  • .Net 框架内置了一种单向隐式转换,可让您将 a 传递给DateTime任何DateTimeOffset参数或变量。这样做时 .Kind事情。如果您传递 UTC 类型,它将以零偏移量传入,但如果您传递.Localor .Unspecified,它将假定为 local 。该框架基本上是在说,“好吧,你让我将日历时间转换为瞬时时间,但我不知道这是从哪里来的,所以我打算使用本地日历。” DateTime如果您在具有不同时区的计算机上加载未指定的文件,这将是一个巨大的问题。(恕我直言 - 这应该抛出一个异常 - 但它没有。)

无耻的插头:

许多人与我分享他们发现这个类比非常有价值,因此我将它包含在我的 Pluralsight
课程“日期和时间基础”中。您将在标题为“日历时间与瞬时时间”的剪辑的第二个模块“上下文很重要”中找到相机类比的逐步演练。

2022-02-28