DateTime与DateTimeOffset

冷暖自知 提交于 2019-12-28 20:06:45

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

目前,我们有一种以TimeZone感知方式处理.net DateTimes的标准方法:每当我们生成一个DateTime我们用UTC(例如使用DateTime.UtcNow )来做,每当我们显示一个时,我们就会从UTC转换回用户的当地时间。

这工作正常,但我一直在阅读有关DateTimeOffset以及它如何捕获对象本身的本地和UTC时间。 所以问题是,使用DateTimeOffset与我们已经做的事情有什么好处?


#1楼

来自微软:

DateTimeOffset值的这些用法比DateTime值的用法更常见。 因此,应将DateTimeOffset视为应用程序开发的默认日期和时间类型。

来源: “在DateTime,DateTimeOffset,TimeSpan和TimeZoneInfo之间选择”MSDN

我们使用DateTimeOffset几乎所有内容,因为我们的应用程序处理特定时间点(例如,创建/更新记录时)。 另外,我们也在SQL Server 2008中使用DATETIMEOFFSET

当你想要只处理日期,只处理时间或者在一般意义上处理时,我认为DateTime是有用的。 例如,如果你有,你要每天都去送行上午7时报警,你可以存储在一个DateTime利用DateTimeKindUnspecified ,因为你希望它在早上7点,无论DST的熄灭。 但是,如果要表示警报发生的历史记录,则可以使用DateTimeOffset

使用DateTimeOffsetDateTime的混合时要特别小心,尤其是在分配和比较类型时。 此外,只有比较DateTime是相同的情况下DateTimeKind因为DateTime忽略时区比较时偏移。


#2楼

DateTimeOffset瞬时时间的表示(也称为绝对时间 )。 通过这种方式,我的意思是对每个人都是普遍的时刻(不考虑闰秒 ,或时间膨胀的相对论效应)。 另一种表示瞬时时间的方法是使用DateTime ,其中.KindDateTimeKind.Utc

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

那么,为什么DateTimeOffset而不是UTC DateTime这完全取决于观点。 让我们用一个类比 - 我们假装是摄影师。

想象一下,您正站在日历时间轴上,将摄像机对准在您面前的瞬时时间线上的人。 您根据时区规则排列相机 - 由于夏令时,或由于您所在时区的法律定义的其他更改而定期更改。 (你没有稳定的手,所以你的相机不稳定。)

站在照片中的人会看到相机来自的角度。 如果其他人正在拍照,他们可能来自不同的角度。 这就是DateTimeOffsetOffset部分所代表的内容。

因此,如果您将相机标记为“东部时间”,有时您指向-5,有时您指向-4。 世界各地都有摄像机,所有摄像机都标有不同的东西,并且从不同角度指向同一瞬时时间轴。 其中一些是紧挨着(或在彼此之上),因此只知道偏移量不足以确定时间与哪个时区相关。

UTC怎么样? 嗯,这是一台相机,保证有稳定的手。 它在三脚架上,牢固地固定在地面上。 它不会去任何地方。 我们将其视角称为零偏移。

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

  • 如果您特别表示相对于某个地方的时间,请使用DateTime在日历时间内表示。 请确保您不会将一个日历与另一个日历混淆。 Unspecified应该是您的假设。 Local仅来自DateTime.Now 。 例如,我可能会获得DateTime.Now并将其保存在数据库中 - 但是当我检索它时,我必须假设它是Unspecified 。 我无法依赖我的本地日历与最初拍摄的日历相同。

  • 如果您必须始终确定当下,请确保您正在表示瞬时时间。 使用DateTimeOffset强制执行它,或按惯例使用UTC DateTime

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

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

    还应该指出, Noda Time有一个名为ZonedDateTime的表示,而.Net基类库没有任何类似的东西。 您需要存储DateTimeOffsetTimeZoneInfo.Id值。

  • 有时,您会希望表示“任何人正在查看它”的本地日历时间。 例如,在定义今天的意义时。 今天总是午夜到午夜,但这些代表了瞬时时间轴上几乎无限数量的重叠范围。 (实际上我们有一个有限数量的时区,但是你可以将偏移量表示为勾号)所以在这些情况下,请确保你理解如何限制“谁在问?” 向下一个时区提问,或者处理将它们转换回适当的瞬时时间。

以下是关于DateTimeOffset的一些其他一些DateTimeOffset这个类比的内容,以及一些保持笔直的提示:

  • 如果比较两个DateTimeOffset值,则在比较之前首先将它们标准化为零偏移量。 换句话说, 2012-01-01T00:00:00+00:002012-01-01T02:00:00+02:00指的是相同的瞬时时刻,因此是等效的。

  • 如果你正在做的任何单元测试和需要肯定的偏移量, 同时测试的DateTimeOffset值, .Offset财产分开。

  • .Net框架内置了一个单向隐式转换,允许您将DateTime传递给任何DateTimeOffset参数或变量。 这样做时, .Kind重要 。 如果你传递一个UTC一种,它将搭载与零偏移,但如果你通过其中.Local.Unspecified ,就会认为是本地的 。 该框架基本上是在说:“嗯,你让我把日历时间转换为瞬间时间,但我不知道它来自哪里,所以我只想使用本地日历。” 如果您在具有不同时区的计算机上加载未指定的DateTime ,这是一个巨大的问题。 (恕我直言 - 这应该抛出异常 - 但事实并非如此。)

无耻插头:

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


#3楼

大多数答案都很好,但我想添加一些MSDN链接以获取更多信息


#4楼

我看到的DateTimeOffset的唯一不利方面是微软“忘记”(按设计)在XmlSerializer类中支持它。 但它已被添加到XmlConvert实用程序类中。

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

我说继续使用DateTimeOffset和TimeZoneInfo是因为所有的好处,只要注意创建将要或可能序列化为XML(或所有业务对象)的实体。


#5楼

最重要的区别是DateTime不存储时区信息,而DateTimeOffset则存储时区信息。

尽管DateTime区分UTC和Local,但绝对没有与之关联的显式时区偏移。 如果进行任何类型的序列化或转换,将使用服务器的时区。 即使您通过添加分钟来手动创建本地时间来抵消UTC时间,您仍然可以在序列化步骤中获得位,因为(由于DateTime中没有任何显式偏移),它将使用服务器的时区偏移量。

例如,如果使用Json.Net和ISO日期格式序列化Kind = Local的DateTime值,您将获得类似2015-08-05T07:00:00-04的字符串。 请注意,最后一部分(-04)与您的DateTime或您用于计算它的任何偏移无关......它只是服务器的时区偏移量。

同时,DateTimeOffset显式包含偏移量。 它可能不包括时区的名称,但至少它包含偏移量,如果序列化它,您将获得值中明确包含的偏移量,而不是服务器的本地时间。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!