关于c#:DateTime vs DateTimeOffset

DateTime vs DateTimeOffset

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

这很好,但是我一直在读关于DateTimeOffset以及它如何捕获对象本身的本地和UTC时间。所以问题是,使用DateTimeOffset与我们已经做的相比,有什么好处呢?


DateTimeOffset表示瞬时时间(也称为绝对时间)。我指的是对每个人来说都是普遍存在的时间点(不包括闰秒或时间膨胀的相对论效应)。另一种表示瞬时时间的方法是使用DateTime,其中.KindDateTimeKind.Utc。好的。

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

那么,为什么用DateTimeOffset而不是UTC DateTime?一切都是关于透视的。让我们用一个类比——我们会假装成摄影师。好的。

想象你站在一个日历时间线上,用相机对着摆在你面前的即时时间线上的一个人。您根据时区规则排列您的相机-由于夏令时或时区法律定义的其他更改,时区规则会定期更改。(你的手不稳,所以你的相机不稳。)好的。

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

所以如果你把你的相机标为"东部时间",有时你指向-5,有时你指向-4。全世界都有摄像头,都贴上了不同的标签,都从不同的角度指向同一个即时时间线。其中一些是紧挨着(或在上面),所以仅仅知道偏移量还不足以确定时间与哪个时区相关。好的。

那UTC呢?好吧,只有一台相机可以保证有一只稳定的手。它在三脚架上,牢牢地固定在地上。哪儿也去不了。我们称它的视角为零偏移。好的。

Instantaneous Time vs Calendar Time Visualization好的。

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

  • 如果您表示的是相对于某个特定位置的时间,请使用DateTime以日历时间表示。只要确保你不会把一个日历和另一个混淆。你的假设应该是UnspecifiedLocal只对DateTime.Now有用。例如,我可能会得到DateTime.Now并将其保存在数据库中——但是当我检索它时,我必须假设它是Unspecified。我不能相信我的本地日历和它原来的日历是一样的。好的。

  • 如果你必须总是确定时刻,确保你代表的是瞬时时间。使用DateTimeOffset强制执行,或按约定使用utc DateTime。好的。

  • 如果您需要跟踪瞬间时间,但您还想知道"用户认为它在他们的本地日历上是什么时间?"-然后您必须使用DateTimeOffset。这对于计时系统非常重要,例如,对于技术和法律问题。好的。

  • 如果您需要修改以前记录的DateTimeOffset—您在偏移量中没有足够的信息来确保新偏移量仍然与用户相关。你还必须存储一个时区标识符(想想——我需要那个摄像头的名称,这样即使位置改变了,我也可以拍一张新照片)。好的。

    还应该指出的是,noda time有一个称为ZonedDateTime的表示,而.NET基类库没有类似的内容。您需要同时存储DateTimeOffsetTimeZoneInfo.Id值。好的。

  • 有时,您会希望表示一个日历时间,它是"任何人都在看它"的本地时间。例如,在定义今天的含义时。今天总是从午夜到午夜,但这些都代表了瞬时时间线上几乎无限多的重叠范围。(实际上,我们有有限的时区,但您可以将偏移量减到刻度)因此,在这些情况下,请确保您了解如何限制"谁在问?"把问题缩小到一个时区,或者在适当的时候把它们转换回即时时间。好的。

下面是一些关于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,这是一个巨大的错误。(imho——应该有例外——但事实并非如此。)好的。

无耻插头:好的。

许多人和我分享,他们发现这个类比非常有价值,所以我把它包括在我的多元课程,日期和时间的基础上。您将在题为"日历时间与即时时间"的剪辑中的第二个模块"上下文关系"中找到相机类比的逐步演练。好的。好啊。


微软:

These uses for DateTimeOffset values are much more common than those for DateTime values. As a result, DateTimeOffset should be considered the default date and time type for application development.

source:"Chooseing between datetime,datetimeoffset,timespan,and timezoneinfo",msdn

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

我认为DateTime在你只想处理日期、时间或一般意义上的事务时是有用的。例如,如果你有一个每天早上7点要响的警报,你可以使用UnspecifiedDateTimeKind把它存储在DateTime中,因为你想让它在早上7点响,而不考虑DST。但是,如果您想表示报警事件的历史记录,您可以使用DateTimeOffset

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


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

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


在一些地方,DateTimeOffset是有意义的。一个是你处理经常性事件和夏令时。假设我想设置一个闹钟,每天早上9点响。如果我使用"存储为UTC,显示为本地时间"规则,那么当夏令时生效时,警报将在其他时间关闭。

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


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

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

例如,如果使用json.net和iso日期格式序列化kind=local的日期时间值,则会得到类似2015-08-05T07:00:00-04的字符串。注意最后一部分(-04)与日期时间或用于计算它的任何偏移量无关…这只是服务器的时区偏移量。

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


大多数答案都很好,但是我想添加更多的msdn链接以获取更多信息。

  • 日期时间简史-由BCL团队安东尼·摩尔撰写
  • 选择日期时间和日期时间偏移量-按msdn
  • 不要忘记,SQL Server 2008以后版本的数据类型为datetimeoffset
  • .NET框架包括日期时间、日期时间偏移量和TimeZoneInfo类型,所有这些类型都可用于构建应用程序这与日期和时间有关。
  • 使用日期和时间msdn执行算术运算


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

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


这段来自微软的代码解释了一切:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 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的唯一负面一面是,微软"忘记"(按设计)在其xmlserializer类中支持它。但后来它被添加到了xmlconvert实用程序类中。

xmlconvert.todatetimeoffset

xmlconvert.to字符串

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