927

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


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

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

4

10 回答 10

1477

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 课程“日期和时间基础”中。您将在标题为“日历时间与瞬时时间”的剪辑的第二个模块“上下文很重要”中找到相机类比的逐步演练。

于 2013-01-10T22:09:06.603 回答
448

来自微软:

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

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

DateTimeOffset当我们的应用程序处理特定的时间点(例如创建/更新记录时)时,我们几乎可以使用所有内容。附带说明一下,我们DATETIMEOFFSET也在 SQL Server 2008 中使用。

DateTime当您只想处理日期、时间或一般意义上的处理时,我认为这很有用。例如,如果您有一个闹钟希望每天早上 7 点响起,那么您可以将其存储在DateTimeusing a DateTimeKindof 中,Unspecified因为您希望它在早上 7 点响起而不管 DST。但是,如果您想表示警报发生的历史记录,您可以使用DateTimeOffset.

混合使用时要小心DateTimeOffsetDateTime尤其是在类型之间分配和比较时。此外,仅比较DateTime相同的实例,DateTimeKind因为DateTime在比较时忽略时区偏移。

于 2013-01-09T19:42:01.917 回答
103

DateTime 只能存储两个不同的时间,本地时间和 UTC。Kind属性指示哪个。

DateTimeOffset 通过能够存储来自世界任何地方的本地时间对此进行了扩展。它还存储本地时间和 UTC 之间的偏移量。请注意 DateTime 无法做到这一点,除非您向您的班级添加一个额外的成员来存储该 UTC 偏移量。或者只使用 UTC。顺便说一句,这本身就是一个好主意。

于 2010-12-02T17:47:08.860 回答
35

There's a few places where DateTimeOffset makes sense. One is when you're dealing with recurring events and daylight savings time. Let's say I want to set an alarm to go off at 9am every day. If I use the "store as UTC, display as local time" rule, then the alarm will be going off at a different time when daylight savings time is in effect.

There are probably others, but the above example is actually one that I've run into in the past (this was before the addition of DateTimeOffset to the BCL - my solution at the time was to explicitly store the time in the local timezone, and save the timezone information along side it: basically what DateTimeOffset does internally).

于 2010-12-02T02:59:58.997 回答
32

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

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

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

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

于 2015-08-05T23:00:02.093 回答
30

Microsoft的这段代码解释了一切:

// Find difference between Date.Now and Date.UtcNow
  date1 = DateTime.Now;
  date2 = DateTime.UtcNow;
  difference = date1 - date2;
  Console.WriteLine("{0} - {1} = {2}", date1, date2, difference);

  // Find difference between Now and UtcNow using DateTimeOffset
  dateOffset1 = DateTimeOffset.Now;
  dateOffset2 = DateTimeOffset.UtcNow;
  difference = dateOffset1 - dateOffset2;
  Console.WriteLine("{0} - {1} = {2}", 
                    dateOffset1, dateOffset2, difference);
  // If run in the Pacific Standard time zone on 4/2/2007, the example
  // displays the following output to the console:
  //    4/2/2007 7:23:57 PM - 4/3/2007 2:23:57 AM = -07:00:00
  //    4/2/2007 7:23:57 PM -07:00 - 4/3/2007 2:23:57 AM +00:00 = 00:00:00
于 2018-10-19T09:32:00.947 回答
13

TLDR,如果您不想阅读所有这些出色的答案 :-)

显式

使用DateTimeOffset是因为时区被强制为 UTC+0。

隐式

使用DateTime希望每个人都遵守时区始终为 UTC+0 的不成文规则的地方。


(开发人员的旁注:显式总是比隐式好!)

(Java 开发人员的旁注,C# DateTimeOffset== Java OffsetDateTime,请阅读:https ://www.baeldung.com/java-zoneddatetime-offsetdatetime )

于 2020-08-18T13:28:55.927 回答
8

一个主要区别是DateTimeOffset可以与 结合使用TimeZoneInfo以转换为当前时区以外的本地时间。

这在由不同时区的用户访问的服务器应用程序(例如 ASP.NET)上很有用。

于 2010-12-02T13:03:40.060 回答
3

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

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

我说继续使用 DateTimeOffset 和 TimeZoneInfo 是因为有所有好处,只是在创建将或可能序列化到 XML 或从 XML 序列化的实体时要小心(然后是所有业务对象)。

于 2014-04-09T11:11:41.753 回答
0

DateTime.Now
12 月 21 日星期五 18:40:11

DateTimeOffset.Now
12 月 21 日星期五 18:40:11 +02:00

因此,DateTimeOffset存储有关时间如何与 UTC 相关的信息,基本上是时区。

于 2021-12-03T17:41:48.280 回答