提问人:David Reis 提问时间:12/2/2010 最后编辑:TylerHDavid Reis 更新时间:4/18/2022 访问量:397407
DateTime 与 DateTimeOffset
DateTime vs DateTimeOffset
问:
a 和 a 有什么区别,什么时候应该使用?DateTime
DateTimeOffset
目前,我们有一种以时区感知方式处理 .NET 的标准方法:每当我们生成 时,我们都以 UTC 进行(例如使用 ),每当我们显示一个时,我们都会从 UTC 转换回用户的本地时间。DateTime
DateTime
DateTime.UtcNow
这很好用,但我一直在阅读它如何在对象本身中捕获本地和 UTC 时间。DateTimeOffset
答:
有几个地方是有道理的。一种是当您处理重复发生的事件和夏令时时。假设我想设置一个闹钟,每天早上 9 点响起。如果我使用“存储为 UTC,显示为本地时间”规则,则闹钟将在夏令时生效时的不同时间响起。DateTimeOffset
可能还有其他例子,但上面的例子实际上是我过去遇到过的例子(这是在添加到 BCL 之前 - 我当时的解决方案是显式地将时间存储在本地时区中,并将时区信息保存在它旁边:基本上是内部的)。DateTimeOffset
DateTimeOffset
评论
一个主要的区别是可以与 转换为当前时区以外的本地时间。DateTimeOffset
TimeZoneInfo
这在不同时区的用户访问的服务器应用程序(例如 ASP.NET)上很有用。
评论
DateTime 只能存储两个不同的时间,即本地时间和 UTC。Kind 属性指示哪个。
DateTimeOffset 通过能够存储来自世界任何地方的本地时间来扩展这一点。它还存储该本地时间和 UTC 之间的偏移量。请注意,除非向类添加额外的成员来存储该 UTC 偏移量,否则 DateTime 无法执行此操作。或者只使用 UTC。顺便说一句,这本身就是一个好主意。
评论
来自 Microsoft:
DateTimeOffset 值的这些用途比 DateTime 值的这些用途更常见。因此,应将 DateTimeOffset 视为应用程序开发的默认日期和时间类型。
来源:“在 DateTime、DateTimeOffset、TimeSpan 和 TimeZoneInfo 之间进行选择”,MSDN
我们几乎将所有内容都用于我们的应用程序处理特定时间点(例如,创建/更新记录的时间)。顺便说一句,我们也在 SQL Server 2008 中使用。DateTimeOffset
DATETIMEOFFSET
我认为当您只想处理日期、时间或在一般意义上处理其中任何一个时,这是有用的。例如,如果您有一个闹钟,您想在每天早上 7 点响起,您可以将其存储在利用中,因为您希望它在早上 7 点响起,而不管 DST 如何。但是,如果要表示警报发生的历史记录,则可以使用 .DateTime
DateTime
DateTimeKind
Unspecified
DateTimeOffset
混合使用类型时要小心,尤其是在类型之间分配和比较时。此外,请仅比较相同的实例,因为在比较时会忽略时区偏移量。DateTimeOffset
DateTime
DateTime
DateTimeKind
DateTime
评论
Kind
DateTimeKind.Unspecified
DateTimeKind.Local
DateTimeKind.Utc
DateTimeOffset
DateTimeOffset
是瞬时时间(也称为绝对时间)的表示形式。我的意思是,一个对每个人都是普遍的时刻(不考虑闰秒,也不考虑时间膨胀的相对论效应)。表示瞬时时间的另一种方法是使用 where 是 。DateTime
.Kind
DateTimeKind.Utc
这与日历时间(也称为民事时间)不同,后者是某人日历上的一个位置,全球有许多不同的日历。我们称这些日历为时区。日历时间用 where 表示,即 或 。并且仅在您隐含了解使用结果的计算机的位置的情况下才有意义。(例如,用户的工作站)DateTime
.Kind
DateTimeKind.Unspecified
DateTimeKind.Local
.Local
那么,为什么不是UTC呢?这一切都与视角有关。让我们打个比方——我们会假装是摄影师。DateTimeOffset
DateTime
想象一下,你站在日历时间线上,将相机对准摆在你面前的瞬时时间线上的人。您可以根据时区规则对齐相机 - 由于夏令时或时区法定定义的其他更改,这些规则会定期更改。(你的手不稳,所以你的相机摇晃。
站在照片中的人会看到相机的角度。如果其他人在拍照,他们可能会从不同的角度拍照。这就是部分所代表的。Offset
DateTimeOffset
因此,如果您将相机标记为“东部时间”,则有时您从 -5 指向,有时您从 -4 指向。世界各地都有摄像机,都标有不同的东西,并且都从不同的角度指向同一个瞬时时间线。其中一些彼此相邻(或重叠),因此仅知道偏移量不足以确定时间与哪个时区相关。
那么UTC呢?嗯,这是唯一一台保证手稳定的相机。它放在一个三脚架上,牢牢地固定在地面上。它不会去任何地方。我们称它的视角为零偏移。
那么 - 这个类比告诉我们什么?它提供了一些直观的指南-
如果要表示相对于某个特定地点的时间,请使用 .只要确保你永远不会将一个日历与另一个日历混淆。 应该是你的假设。 只有来自 的有用。例如,我可能会获取它并将其保存在数据库中 - 但是当我检索它时,我必须假设它是 .我不能相信我的本地日历与它最初取自的日历相同。
DateTime
Unspecified
Local
DateTime.Now
DateTime.Now
Unspecified
如果你必须始终确定时刻,请确保你代表的是瞬时时间。用于强制执行它,或按约定使用 UTC。
DateTimeOffset
DateTime
如果您需要跟踪瞬时时间的某个时刻,但您还想知道“用户认为这是他们本地日历上的时间?”,那么您必须使用 .例如,这对于计时系统非常重要 - 无论是出于技术问题还是法律问题。
DateTimeOffset
如果您需要修改以前记录的偏移量 - 仅靠偏移量中没有足够的信息来确保新的偏移量仍然与用户相关。您还必须存储时区标识符(想想 - 我需要该相机的名称,以便即使位置已更改,我也可以拍摄新照片)。
DateTimeOffset
还应该指出的是,Noda Time 有一个为此调用的表示形式,而 .Net 基类库没有类似的东西。您需要同时存储 a 和 a 值。
ZonedDateTime
DateTimeOffset
TimeZoneInfo.Id
有时,您需要表示“正在查看它的人”的本地日历时间。例如,在定义今天的含义时。今天总是午夜到午夜,但这些代表了瞬时时间轴上近乎无限数量的重叠范围。(在实践中,我们的时区数量有限,但您可以将偏移量表示为刻度)因此,在这些情况下,请确保您了解如何将“谁在问”问题限制在单个时区,或者根据需要将它们转换回瞬时。
以下是其他一些支持这个类比的小细节,以及一些保持直截了当的技巧:DateTimeOffset
如果比较两个值,则在比较之前,首先将它们归一化为零偏移量。换言之,和指相同的瞬时时刻,因此是等价的。
DateTimeOffset
2012-01-01T00:00:00+00:00
2012-01-01T02:00:00+02:00
如果要进行任何单元测试,并且需要确定偏移量,请分别测试值和属性。
DateTimeOffset
.Offset
.Net 框架中内置了单向隐式转换,可用于将 传递到任何参数或变量中。执行此操作时,
.善良
很重要。如果您传递 UTC 类型,它将以零偏移量进发,但如果您传递任何一个 或 ,它将假定是本地的。这个框架基本上是说,“好吧,你让我将日历时间转换为瞬时时间,但我不知道这是从哪里来的,所以我只打算使用本地日历。如果您在具有不同时区的计算机上加载未指定的内容,这是一个巨大的问题。(恕我直言 - 这应该抛出一个例外 - 但事实并非如此。DateTime
DateTimeOffset
.Local
.Unspecified
DateTime
无耻的插头:
许多人与我分享说,他们发现这个类比非常有价值,所以我把它纳入了我的 Pluralsight 课程,日期和时间基础。您将在标题为“日历时间与瞬时时间”的剪辑中的第二个模块“上下文问题”中找到相机类比的分步演练。
评论
DateTimeOffset
DATETIMEOFFSET
DATETIME2
DATETIME
DateTime
DateTimeOffset.Now
DateTimeOffset
我看到的DateTimeOffset的唯一缺点是Microsoft“忘记”(设计)在其XmlSerializer类中支持它。但是它后来被添加到 XmlConvert 实用工具类中。
我说继续使用 DateTimeOffset 和 TimeZoneInfo,因为所有的好处,在创建将或可能序列化为 XML 或从 XML 序列化的实体(然后是所有业务对象)时要小心。
最重要的区别是 DateTime 不存储时区信息,而 DateTimeOffset 存储时区信息。
尽管 DateTime 区分了 UTC 和本地,但绝对没有与之关联的显式时区偏移量。如果进行任何类型的序列化或转换,则将使用服务器的时区。即使您通过添加分钟来偏移 UTC 时间来手动创建本地时间,您仍然可以在序列化步骤中获取位,因为(由于 DateTime 中缺少任何显式偏移量)它将使用服务器的时区偏移量。
例如,如果使用 Json.Net 和 ISO 日期格式将 DateTime 值序列化为 Kind=Local 的 DateTime 值,则会得到一个类似 .请注意,最后一部分 (-04) 与您的 DateTime 或您用于计算它的任何偏移量无关......它只是纯粹的服务器时区偏移量。2015-08-05T07:00:00-04
同时,DateTimeOffset 显式包含偏移量。它可能不包括时区的名称,但至少它包括偏移量,如果你序列化它,你将获得显式包含的偏移量,而不是服务器的本地时间。
评论
The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
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
评论
DateTimeOffset.Now
CreatedDate
DateTimeOffset.Now
DateTimeOffset.Now
+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
TLDR 如果您不想阅读所有这些很棒的答案:-)
显式:
使用,因为时区强制为 UTC+0。DateTimeOffset
隐式:
使用您希望每个人都遵守时区始终为 UTC+0 的不成文规则。DateTime
(开发人员的旁注:显式总是比隐式好!
(Java开发人员的旁注,C#==Java,阅读:https://www.baeldung.com/java-zoneddatetime-offsetdatetimeDateTimeOffset
OffsetDateTime
)
评论
DateTime.Now
星期五 03 12月 21 18:40:11
DateTimeOffset.Now
星期五 03 十二月 21 18:40:11 +02:00
因此,存储有关时间与UTC(基本上是时区)的关系的信息。DateTimeOffset
评论