DateTime 与 DateTimeOffset

DateTime vs DateTimeOffset

提问人:David Reis 提问时间:12/2/2010 最后编辑:TylerHDavid Reis 更新时间:4/18/2022 访问量:397407

问:

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


目前,我们有一种以时区感知方式处理 .NET 的标准方法:每当我们生成 时,我们都以 UTC 进行(例如使用 ),每当我们显示一个时,我们都会从 UTC 转换回用户的本地时间。DateTimeDateTimeDateTime.UtcNow

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

C# .NET 日期时间 时区 datetimeoffset

评论

0赞 Dejan 7/22/2016
说到存储,stackoverflow.com/questions/4715620/...也很有趣。
3赞 Jim Aho 2/23/2021
好奇的人可能还想阅读存储 utc 不是灵丹妙药

答:

36赞 Dean Harding 12/2/2010 #1

有几个地方是有道理的。一种是当您处理重复发生的事件和夏令时时。假设我想设置一个闹钟,每天早上 9 点响起。如果我使用“存储为 UTC,显示为本地时间”规则,则闹钟将在夏令时生效时的不同时间响起。DateTimeOffset

可能还有其他例子,但上面的例子实际上是我过去遇到过的例子(这是在添加到 BCL 之前 - 我当时的解决方案是显式地将时间存储在本地时区中,并将时区信息保存在它旁边:基本上是内部的)。DateTimeOffsetDateTimeOffset

评论

18赞 JarrettV 1/16/2013
DateTimeOffset 无法解决 DST 问题
4赞 Zack Jannsen 3/29/2013
使用 TimeZoneInfo 类确实携带 DST 规则。如果您使用的是 .NET 3.5 或更高版本,则使用 TimeZone 或 TimeZoneInfo 类来处理必须与时区偏移量同时处理夏令时的日期。
1赞 Tony Wall 4/9/2014
是的,异常(闹钟应用程序)的好例子,但是当时间比日期更重要时,您应该真正将其单独存储在应用程序的计划数据结构中,即发生类型 = 每日和时间 = 09:00。这里的重点是开发人员需要了解他们正在记录、计算或呈现给用户的日期类型。特别是应用程序往往更加全球化,现在我们有互联网作为标准,大型应用程序商店可以编写软件。作为侧节点,我还希望看到Microsoft添加一个单独的日期和时间结构。
5赞 Jeremy Cook 11/8/2014
总结 Jarrett 和 Zack 的评论:听起来 DateTimeOffset 单独处理 DST 问题,但将 DateTimeOffset 与 TimeZoneInfo 结合使用将处理它。这与 DateTime 没有什么不同,其中 kind 是 Utc。在这两种情况下,我都必须知道我投影时刻的日历的时区(而不仅仅是偏移量)。(如果可能的话,我可能会将其存储在用户的配置文件中或从客户端(例如 Windows)获取它)。听起来对吗?
3赞 Ronnie Overby 12/6/2018
“DateTimeOffset 在一些地方是有意义的。” --- 可以说,它通常比没有意义更有意义。
8赞 Joe 12/2/2010 #2

一个主要的区别是可以与 转换为当前时区以外的本地时间。DateTimeOffsetTimeZoneInfo

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

评论

3赞 Zack Jannsen 3/29/2013
@Bugeo Bugeo 是真的,但存在风险。您可以通过首先在两个 DateTime 上调用“ToUniversalTime”来比较两个 DateTimes。如果比较中只有一个值,即 DateTimeKind = Unspecined,则策略将失败。当需要转换为本地时间时,这种失败的可能性是考虑 DateTimeOffset 而不是 DateTime 的原因。
0赞 Arwin 4/6/2018
如上所述,我认为在这种情况下,最好存储 TimeZoneId 而不是使用 DateTimeOffset,这最终没有任何意义。
0赞 Manfred 7/25/2020
或者,可以存储 DateTimeOffset 和 TimeZoneId。然后,您不仅要有偏移量,还要有导致偏移量的时区。请记住,多个时区可以共享相同的偏移量。
129赞 Hans Passant 12/3/2010 #3

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

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

评论

4赞 Yann 3/31/2023
到目前为止,这篇文章最简单和最简单的答案。
485赞 Clay 1/10/2013 #4

来自 Microsoft:

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

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

我们几乎将所有内容都用于我们的应用程序处理特定时间点(例如,创建/更新记录的时间)。顺便说一句,我们也在 SQL Server 2008 中使用。DateTimeOffsetDATETIMEOFFSET

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

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

评论

15赞 Matt Johnson-Pint 4/23/2016
我只想说我也喜欢这个答案,并投了赞成票。尽管在最后一部分 - 即使确保相同,比较也可能是错误的。如果双方都有,你真的不知道他们来自同一个时区。如果双方都是,大多数比较都会很好,但你仍然可能会有错误,因为一方在当地时区是模棱两可的。真的只有比较是万无一失的,是的,通常是首选。(干杯!KindDateTimeKind.UnspecifiedDateTimeKind.LocalDateTimeKind.UtcDateTimeOffset
4赞 MikeTeeVee 7/23/2019
+1 我要补充一点:您选择的 DataType 应该反映您的意图。不要到处使用 DateTimeOffset,只是原因。如果 Offset 对 Calculations 和 Reading-From/Persisting-To the DataBase 很重要,请使用 DateTimeOffset。如果无关紧要,请使用 DateTime,这样您就可以理解(只需查看 DataType)Offset 应该没有方位,并且 Times 应该相对于运行 C# 代码的服务器/计算机的位置保持相对。
1681赞 Matt Johnson-Pint 1/11/2013 #5

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

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

那么,为什么不是UTC呢?这一切都与视角有关。让我们打个比方——我们会假装是摄影师。DateTimeOffsetDateTime

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

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

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

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

Instantaneous Time vs Calendar Time Visualization

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

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

  • 如果你必须始终确定时刻,请确保你代表的是瞬时时间。用于强制执行它,或按约定使用 UTC。DateTimeOffsetDateTime

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

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

    还应该指出的是,Noda Time 有一个为此调用的表示形式,而 .Net 基类库没有类似的东西。您需要同时存储 a 和 a 值。ZonedDateTimeDateTimeOffsetTimeZoneInfo.Id

  • 有时,您需要表示“正在查看它的人”的本地日历时间。例如,在定义今天的含义时。今天总是午夜到午夜,但这些代表了瞬时时间轴上近乎无限数量的重叠范围。(在实践中,我们的时区数量有限,但您可以将偏移量表示为刻度)因此,在这些情况下,请确保您了解如何将“谁在问”问题限制在单个时区,或者根据需要将它们转换回瞬时。

以下是其他一些支持这个类比的小细节,以及一些保持直截了当的技巧:DateTimeOffset

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

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

  • .Net 框架中内置了单向隐式转换,可用于将 传递到任何参数或变量中。执行此操作时.善良很重要。如果您传递 UTC 类型,它将以零偏移量进发,但如果您传递任何一个 或 ,它将假定是本地的。这个框架基本上是说,“好吧,你让我将日历时间转换为瞬时时间,但我不知道这是从哪里来的,所以我只打算使用本地日历。如果您在具有不同时区的计算机上加载未指定的内容,这是一个巨大的问题。(恕我直言 - 这应该抛出一个例外 - 但事实并非如此。DateTimeDateTimeOffset.Local.UnspecifiedDateTime

无耻的插头:

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

评论

9赞 Matt Johnson-Pint 3/29/2013
@ZackJannsen 如果 C# 中有 a,则应将其保存到 SQL Server 中。 或者只是(取决于所需的范围)对于常规值来说是可以的。是的 - 您可以从时区 + dto 或 utc 的任何配对解析本地时间。区别在于 - 您总是想计算每个解析的规则,还是想预先计算它们?在许多情况下(有时出于法律考虑),DTO 是更好的选择。DateTimeOffsetDATETIMEOFFSETDATETIME2DATETIMEDateTime
4赞 Matt Johnson-Pint 3/29/2013
@ZackJannsen 对于您问题的第二部分,我建议您尽可能多地在服务器端进行操作。Javascript 对于时区计算来说不是那么好。如果必须这样做,请使用这些库之一。但服务器端是最好的。如果您有其他更详细的问题,请为他们提出一个新的 SO 问题,如果可以的话,我会回答。谢谢。
5赞 Matt Johnson-Pint 8/10/2013
@JoaoLeme - 这取决于你从哪里获得它。你是对的,如果你说在服务器上,你确实会得到服务器的偏移量。关键是类型可以保留该偏移量。你可以很容易地在客户端上做到这一点,把它发送到服务器,然后你的服务器就会知道客户端的偏移量。DateTimeOffset.NowDateTimeOffset
3赞 The Muffin Man 2/28/2014
@MattJohnson 你说的以为是是什么意思?我有一个以 UTC 为单位的日期,客户告诉我他们对 UTC 的偏移量是多少,我给他们调整后的日期。对为什么其他任何事情都与该操作有关感到困惑。
6赞 Matt Johnson-Pint 2/28/2014
是的,没错。除了 DTO 存储为 (local time, offset) 对,而不是 (utc time, offset) 对。换言之,与UTC的偏移量已反映在本地时间中。要转换回 UTC,请反转偏移量的符号并将其应用于本地时间。
4赞 Tony Wall 4/9/2014 #6

我看到的DateTimeOffset的唯一缺点是Microsoft“忘记”(设计)在其XmlSerializer类中支持它。但是它后来被添加到 XmlConvert 实用工具类中。

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

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

37赞 Triynko 8/6/2015 #7

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

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

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

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

评论

28赞 Korayem 5/16/2016
有了以上所有答案,我想知道为什么没有人费心写下你的一句话来总结这一切The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
17赞 stonedauwg 6/15/2017
DateTimeOffset 不存储时区信息。标题为“在 DateTime、DateTimeOffset、TimeSpan 和 TimeZoneInfo 之间进行选择”的 MS 文档指定了这一点:“DateTimeOffset 值不与特定时区相关联,但可以来自各种时区中的任何一个”。也就是说,DateTimeOffset 是时区 AWARE,包含与 UTC 的偏移量,这让一切变得不同,这就是为什么在处理处理日期信息的应用程序开发时,它是 MS 推荐的默认类。如果您真的关心数据来自哪个特定时区,则必须单独保留它
1赞 Arwin 4/6/2018
是的,但正如许多地方所显示的那样,+ 或 - 小时并不能说明您所在的时区,最终是无用的。根据你需要做什么,你也可以像Kind.Unspecified一样存储一个日期时间,然后存储其时区的ID,我认为你实际上更好。
0赞 Corrodias 5/17/2023
的确,偏移量与时区不是一回事,但它更容易理解,而且通常是重要的,远非无用。的确,偏移量为 -5 的时间戳可能发生在东部标准时间中部夏令时间。但是,如果我不在乎它在哪个时区,如果我只想知道事件发生的时间(这是时间戳通常的作用),偏移量更有用,因为我不需要查阅历史时区数据库即可将其转换为 UTC。
50赞 Mojtaba 10/19/2018 #8

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

评论

0赞 Morten_564834 3/30/2021
因此,假设我需要在用户创建某些内容时存储一个 CreatedDate 属性。我是将 DatetimeOffset.Now 还是 UtcNow 传递给服务器?
0赞 Mojtaba 3/30/2021
@Morten_564834我会说,因为您可以进行比较,而不管他们的时区如何。DateTimeOffset.NowCreatedDate
0赞 Wouter 9/27/2022
@Morten_564834 @Mojtaba using 还会告诉您它是在哪个时区创建的。但就我个人而言,我会将 DateTime.UtcNow 用于这些技术领域。(但是,是的,这需要人们使用 UTC),您可以在属性访问器中检查。DateTimeOffset.Now
1赞 nawfal 2/17/2023
@Wouter更准确地说,并没有告诉你它是哪个时区,只是它来自什么偏移量。一个时区可以有多个偏移量,并且相同的偏移量可以在多个时区之间共享。但我明白你在说什么。DateTimeOffset.Now
1赞 Stajs 3/10/2023
@Wouter仅供参考,我可以在 nawfal 的链接中看到示例,例如:+01:00 could belong to the following time zones: (GMT+01:00) Amsterdam (GMT+01:00) Belgrade (GMT+01:00) Brussels (GMT+01:00) Sarajevo (GMT+01:00) West Central Africa
36赞 Blundell 8/18/2020 #9

TLDR 如果您不想阅读所有这些很棒的答案:-)

显式

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

隐式

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


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

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

评论

2赞 Craig W. 9/30/2020
如果你在 Azure 中运行,则不必担心每个人都遵守不成文的规则。DateTime.Now、DateTimeOffset.Now、DateTime.UtcNow 和 DateTimeOffset.UtcNow 都返回完全相同的时间点(UTC)。
37赞 Tessaract 12/4/2021 #10

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

DateTimeOffset.Now
星期五 03 十二月 21 18:40:11 +02:00

因此,存储有关时间与UTC(基本上是时区)的关系的信息。DateTimeOffset

评论

4赞 DexterHaxxor 8/8/2022
被低估的答案,这应该是最重要的