时间戳vs时间戳

0号和1号有什么区别,什么时候用?


目前,我们以TimeZone感知的方式处理. NETDateTime的标准方法:每当我们生成DateTime时,我们都会在UTC中执行(例如使用DateTime.UtcNow),每当我们显示一个时,我们都会从UTC转换回用户的本地时间。

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

339947 次浏览

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

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

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

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

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

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

来自微软:

DateTimeOffset值的这些用法比DateTime值的用法要常见得多。因此,DateTimeOffset应被视为应用程序开发的默认日期和时间类型。

来源:

DateTimeOffset瞬时时间的表示(也称为绝对时间)。我的意思是对每个人都通用的时间时刻(不包括闰秒,或时间膨胀的相对论效应)。另一种表示瞬时时间的方法是使用DateTime,其中.KindDateTimeKind.Utc

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

那么,为什么DateTimeOffset而不是UTCDateTime呢?这都是关于视角的。让我们打个比方——我们假装是摄影师。

想象一下,你正站在日历时间线上,用相机对着面前即时时间线上的一个人。你根据自己所在时区的规则排列相机——时区会因为夏令时或时区法律定义的其他变化而定期变化。(你的手不稳,所以相机会摇晃。)

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

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

那么UTC呢?嗯,它是唯一能保证手部稳定的相机。它在三脚架上,牢牢地固定在地上。它哪儿也去不了。我们称它的视角为零偏移。

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

那么,这个类比告诉我们什么呢?它提供了一些直观的指导——

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

  • 如果您必须始终确定时刻,请确保您表示的是瞬时时间。使用DateTimeOffset强制执行它,或按照惯例使用UTCDateTime

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

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

    还应该指出的是,野田时间有一个名为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-这应该抛出异常-但它没有。)

无耻插头:

很多人和我分享,他们发现这个类比非常有价值,所以我把它包括在我的Pluralsight课程日期和时间基础中。你会在第二个模块“上下文事项”中找到相机类比的逐步走查,在标题为“日历时间与瞬时时间”的剪辑中。

我看到的DateTimeOffset的唯一负面影响是Microsoft“忘记”(通过设计)在他们的XmlSerializer类中支持它。但此后它已被添加到XmlConversion实用程序类中。

XmlConversion. ToDateTimeOffset

XmlConvert. ToString转换为空。

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

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

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

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

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

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

// Find difference between Date.Now and Date.UtcNowdate1 = DateTime.Now;date2 = DateTime.UtcNow;difference = date1 - date2;Console.WriteLine("{0} - {1} = {2}", date1, date2, difference);
// Find difference between Now and UtcNow using DateTimeOffsetdateOffset1 = 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

TLDR如果你不想阅读所有这些伟大的答案:-)

明确

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

隐式

使用DateTime,其中希望每个人都坚持时区的不成文规则始终是UTC+0。


(开发人员的补充说明:显性总是比隐性好!)

(Java开发者的旁注,C#DateTimeOffset==JavaOffsetDateTime,阅读:https://www.baeldung.com/java-zoneddatetime-offsetdatetime

DateTime.Now
Fri 03 Dec 21 18:40:11

DateTimeOffset.Now
Fri 03 Dec 21 18:40:11+02:00

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