DateTime vs DateTimeOffset
目前,我们有一种标准的处理.Net DateTimes的TimeZone感知方式:每当我们产生一个DateTime
我们使用UTC(例如使用DateTime.UtcNow
),并且每当我们显示一个时,我们就从UTC转换回用户的当地时间。
这工作正常,但我一直在阅读有关DateTimeOffset
以及它如何捕获对象本身的本地和UTC时间。 所以问题是,使用DateTimeOffset
和我们已经做的事情会有什么优势?
DateTimeOffset
是瞬时时间(也称为绝对时间)的表示。 因此,我的意思是每个人都有一个普遍的时间(不是闰秒,也不是时间膨胀的相对论效应)。 另一种表示瞬间时间的方法是使用DateTime
,其中.Kind
是DateTimeKind.Utc
。
这与日历时间(也称为公民时间)有所不同,这是在某人日历上的位置,并且全球有许多不同的日历。 我们称这些日历时区。 日历时间由DateTime
表示,其中.Kind
是DateTimeKind.Unspecified
或DateTimeKind.Local
。 而.Local
只在您隐含了解使用结果的计算机所在位置的情况下才有意义。 (例如,用户的工作站)
那么,为什么使用DateTimeOffset
而不是UTC DateTime
? 这完全是关于视角。 我们用一个比喻 - 我们假装是摄影师。
想象一下,你站在日历的时间轴上,在你面前的瞬间时间线上指着一个人的照相机。 根据您的时区规则排列相机 - 由于夏令时或由于其他时区法定义的其他变化而定期更改。 (你没有稳重的手,所以你的相机抖动。)
站在照片中的人会看到你的相机来自哪个角度。 如果其他人正在拍照,他们可能来自不同的角度。 这是DateTimeOffset
的Offset
部分所代表的内容。
因此,如果您将相机标为“东部时间”,有时候您指的是-5,有时您指的是-4。 世界各地都有摄像机,都标记了不同的东西,并且从不同角度指向相同的瞬时时间线。 它们中的一些恰好在彼此旁边(或者在彼此之上),所以只知道偏移量不足以确定时间与哪个时区相关。
那么UTC呢? 那么,这是一个相机,保证有一个稳定的手。 它在三脚架上,牢固地固定在地面上。 它不会去任何地方。 我们称其视角为零偏移。
所以 - 这个比喻告诉我们什么? 它提供了一些直观的指导。
如果您特别表示相对某个地点的时间,请在日历时间内用DateTime
表示它。 只要确保你永远不会混淆一个日历与另一个日历。 Unspecified
应该是你的假设。 Local
仅来自DateTime.Now
。 例如,我可能会得到DateTime.Now
并将其保存在数据库中 - 但是当我检索它时,我必须假定它是Unspecified
。 我不能相信我的本地日历是它原来的日历。
如果你必须时刻确定,确保你代表瞬间时间。 使用DateTimeOffset
来强制执行它,或按惯例使用UTC DateTime
。
如果您需要追踪瞬间时间,但是您还想知道“用户认为这是他们当地日历上的什么时间?” - 那么你必须使用DateTimeOffset
。 例如,这对于计时系统非常重要 - 无论是出于技术还是法律方面的考虑。
如果您需要修改以前记录的DateTimeOffset
- 您仅在偏移量中没有足够的信息来确保新的偏移量对用户仍然有用。 您还必须存储时区标识符(想想 - 我需要该摄像机的名称,以便即使位置发生变化也可拍摄新图像)。
还应该指出的是,Noda Time有一个名为ZonedDateTime
的表示,而.Net基类库没有类似的东西。 您需要同时存储DateTimeOffset
和TimeZoneInfo.Id
值。
有时候,你会想要表示一个日历时间,对于“正在查看它的人”是本地的。 例如,定义今天的含义时。 今天总是午夜到午夜,但是这些代表瞬时时间线上的重叠范围数量接近无限。 (实际上我们的时区数量是有限的,但是您可以将偏移量表示为tick)因此,在这些情况下,确保您了解如何限制“谁在问?”。 质疑到一个单一的时区,或处理将其翻译回适当的瞬间时间。
这里有一些关于DateTimeOffset
其他一些备份这个比喻的小技巧,以及一些保持直线的技巧:
如果比较两个DateTimeOffset
值,则它们在比较之前首先归一化为零偏移量。 换句话说, 2012-01-01T00:00:00+00:00
和2012-01-01T02:00:00+02:00
表示相同的瞬时时刻,因此是等效的。
如果您正在执行任何单元测试并需要确定偏移量,请分别测试DateTimeOffset
值和.Offset
属性。
.Net框架中内置了一种单向隐式转换,可以将DateTime
传递到任何DateTimeOffset
参数或变量中。 当这样做时, .Kind
重要 。 如果你传递一个UTC一种,它将搭载与零偏移,但如果你通过其中.Local
或.Unspecified
,就会认为是本地的 。 该框架基本上是这样说的:“嗯,你要求我把日历时间转换为瞬间时间,但我不知道这是从哪里来的,所以我只打算使用本地日历。” 如果您在具有不同时区的计算机上加载未指定的DateTime
,这是一个巨大的问题。 (恕我直言 - 这应该抛出一个例外 - 但它没有。)
无耻插头:
很多人与我分享他们觉得这个比喻非常有价值,所以我将它加入了我的Pluralsight课程,日期和时间基础知识。 您会在标题为“日历时间与瞬时时间”的剪辑中找到第二个模块中的相机比喻的逐步演练,即“上下文事项”。
来自微软:
DateTimeOffset值的这些用法比DateTime值的用法要普遍得多。 因此,应将DateTimeOffset视为应用程序开发的默认日期和时间类型。
源:“在DateTime,DateTimeOffset,TimeSpan和TimeZoneInfo之间选择”,MSDN
我们使用DateTimeOffset
来处理几乎所有的事情,因为我们的应用程序处理特定时间点(例如创建/更新记录时)。 另外,我们在SQL Server 2008中也使用DATETIMEOFFSET
。
当你只想处理日期,只处理时间或者处理一般意义上的时候,我发现DateTime
是有用的。 例如,如果你有,你要每天都去送行上午7时报警,你可以存储在一个DateTime
利用DateTimeKind
的Unspecified
,因为你希望它在早上7点,无论DST的熄灭。 但是,如果要表示警报发生的历史记录,则可以使用DateTimeOffset
。
使用DateTimeOffset
和DateTime
混合时要特别小心,特别是在类型之间进行赋值和比较时。 此外,只有比较DateTime
是相同的情况下DateTimeKind
因为DateTime
忽略时区比较时偏移。
DateTime只能存储两个不同的时间,即本地时间和UTC。 Kind属性指示哪个。
DateTimeOffset通过能够在世界任何地方存储本地时间而扩展了这一点。 它还存储当地时间和UTC之间的偏移量。 注意DateTime不能做到这一点,除非你添加一个额外的成员到你的类来存储UTC偏移量。 或者只能使用UTC。 这本身就是一个好主意btw。
链接地址: http://www.djcxy.com/p/8131.html