DateTime vs DateTimeOffset

目前,我们有一个标准的方式来处理.net DateTimes的TimeZone意识到的方式:当我们产生一个DateTime我们做它在UTC(例如使用DateTime.UtcNow),当我们显示一个,我们从UTC转换回用户当地时间。

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

DateTimeOffset瞬时时间的表示(也称为绝对时间 )。 因此,我的意思是每个人都有一个普遍的时间(不是闰秒 ,也不是时间膨胀的相对论效应)。 另一种表示瞬间时间的方法是用DateTimeKind.Utc

这与日历时间 (也称为“ 民间时间” )有所不同, 日历时间是某人日历上的位置,全球有许多不同的日历。 我们称这些日历时区 。 日历时间由DateTime表示,其中.KindDateTimeKind.UnspecifiedDateTimeKind.Local 。 而且。 .Local只有在您对使用结果的计算机所在的位置有深刻理解的情况下才有意义。 (例如,用户的工作站)

那么,为什么DateTimeOffset而不是UTC DateTime呢? 这完全是关于视angular。 我们用一个比喻 – 我们假装是摄影师。

想象一下,你站在日历的时间轴上,在你面前的瞬间时间线上指着一个人的照相机。 根据时区的规则排列摄像机,由于夏令时会定期更改,或由于其他时区法律定义的更改而导致相机出现故障。 (你没有一个稳定的手,所以你的相机抖动。)

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

因此,如果您在“东部时间”标注相机,有时您指的是-5,有时您指的是-4。 世界各地都有摄像机,都标有不同的东西,都从不同的angular度指向同一瞬间的时间线。 他们中的一些正好在彼此的旁边(或者在彼此之上),所以只知道偏移量不足以确定时间与哪个时区相关。

那么UTC呢? 那么,这是一个相机,保证有一个稳定的手。 它在三脚架上,牢牢地固定在地面上。 它不会去任何地方。 我们把它的视angular称为零点偏移。

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

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

  • 如果您是特定时间相对某个地方的时间,则用date时间表示日历时间。 只要确保你永远不会混淆一个日历与另一个日历。 Unspecified应该是你的假设。 Local只是从DateTime.Now有用。 例如,我可能会得到DateTime.Now并将其保存在数据库中 – 但是当我检索它时,我必须假定它是Unspecified 。 我不能相信我的本地日历是原来的日历。

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

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

  • 如果您需要修改以前logging的DateTimeOffset ,则仅在偏移量中没有足够的信息来确保新的偏移量对用户仍然有用。 您必须存储时区标识符(想想 – 我需要该摄像机的名称,以便即使位置已更改,也可以拍摄新的图像)。

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

  • 偶尔,你会想要代表一个日历时间,是“谁在看它”的本地时间。 例如,当定义今天的含义时。 今天一直是午夜到午夜,但是这些代表了即时时间线上几乎无限多的重叠范围。 (实际上我们的时区数量是有限的,但是你可以把偏移量表示为tick)所以在这种情况下,要确保你知道如何限制“谁在问”? 质疑到一个单一的时区,或者将其翻译成适当的瞬间。

这里有一些关于DateTimeOffset其他一些备份这个比喻,以及保持直线的一些提示:

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

  • 如果您正在进行任何unit testing并需要确定偏移量,请分别testingDateTimeOffset值和.Offset属性。

  • .Net框架中内置了一个单向隐式转换,可以将DateTime传递到任何DateTimeOffset参数或variables中。 这样做时, .Kind重要 。 如果你传递一个UTCtypes,它将带有一个零偏移量,但是如果你通过.Unspecified或者.Unspecified ,它将被认为是本地的 。 这个框架基本上是这样说的:“嗯,你让我把日历时间转换成瞬间时间,但是我不知道这是从哪里来的,所以我只打算使用本地日历。” 如果您在具有不同时区的计算机上加载未指定的DateTime ,这是一个巨大的问题。 (恕我直言 – 这应该抛出一个例外 – 但事实并非如此)。

无耻插头:

很多人和我分享他们觉得这个比喻非常有价值,所以我把它加进了我的Pluralsight课程, date和时间基础 。 您将在标题为“日历时间与瞬时时间”的剪辑中的第二个模块“上下文事项”中find相机类比的逐步演练。

来自微软:

DateTimeOffset值的这些用法比DateTime值的用法要普遍得多。 因此,应将DateTimeOffset视为应用程序开发的默认date和时间types。

源: “在DateTime,DateTimeOffset,TimeSpan和TimeZoneInfo之间select” , MSDN

我们使用DateTimeOffset几乎所有的应用程序处理特定的时间点(例如,当创build/更新logging时)。 另外,我们在SQL Server 2008中也使用了DATETIMEOFFSET

我发现DateTime在你只想处理date,只处理时间,或者处理一般意义上是有用的。 例如,如果您在上午7点每天都要发出警报,则可以使用DateTimeKindUnspecified将其存储在DateTimeKind ,因为您希望它在早上7点closures,而不考虑DST。 但是,如果要表示警报发生的历史logging,则可以使用DateTimeOffset

在使用DateTimeOffsetDateTime的混合时要特别小心,特别是在types之间进行赋值和比较时。 此外,只比较DateTime相同的DateTime实例,因为DateTime在比较时会忽略时区偏移量。

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

DateTimeOffset通过能够从世界任何地方存储本地时间来进一步扩展。 它还存储当地时间和UTC之间的偏移量 。 注意DateTime不能做到这一点,除非你添加一个额外的成员到你的类来存储UTC偏移量。 或者只能使用UTC。 这本身就是一个好主意btw。

有几个地方DateTimeOffset是合理的。 一个是当你处理周期性事件和夏令时。 假设我想设置一个闹钟,每天早上9点起飞。 如果我使用“以UTC格式存储,按照当地时间显示”规则,则夏令时生效时,报警将在不同的时间进行。

也许还有其他的,但是上面的例子实际上是我以前遇到过的(这是在BCL中joinDateTimeOffset之前的 – 我当时的解决scheme是明确地将时间存储在本地时区中,保存时区信息:基本上是什么DateTimeOffset内部)。

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

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

例如,如果使用Json.Net和ISOdate格式将KindTime与DateTime值序列化,则会得到类似于2015-08-05T07:00:00-04的string。 请注意,最后一部分(-04)与您的date时间或您用来计算它的任何偏移量无关……它仅仅是服务器的时区偏移量。

同时,DateTimeOffset明确包含了偏移量。 它可能不包含时区的名称,但至less包含偏移量,如果将其序列化,则将获取显式包含的偏移量,而不是服务器的本地时间。

大多数的答案是好的,但我想添加更多的MSDN链接的更多信息

  • DateTime的简要历史 – 由BCL队由安东尼Moore
  • date时间和date时间偏移之间select – 由MSDN
  • 不要忘记SQL Server 2008以后有一个新的数据types为DateTimeOffset
  • .NET Framework包含DateTimeDateTimeOffsetTimeZoneInfotypes,所有这些types都可以用来构build使用date和时间的应用程序。
  • 用date和时间MSDN执行算术运算

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

这对于用户在不同时区访问的服务器应用程序(例如ASP.NET)非常有用。

我看到的DateTimeOffset唯一的负面的一面是微软“忘了”(通过devise)在它们的XmlSerializer类中支持它。 DataContractSerializer没有问题(也许其他人需要validation)。 无论如何有一个解决方法,我在官方MS连接问题在这里解释:

https://connect.microsoft.com/VisualStudio/feedback/details/288349/datetimeoffset-is-not-serialized-by-a-xmlserializer

正如你所看到的,即使它是一个内置types,微软也不会解决这个问题,他们build议我们默认使用它。 疯狂而真实! 我仍然说,继续使用DateTimeOffset和TimeZoneInfo,因为所有的好处,只是要小心,当创build实体将或可能序列化为XML(所有业务对象)。