【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
目前,我们有一种以TimeZone感知方式处理.net DateTimes的标准方法:每当我们生成一个DateTime
我们用UTC(例如使用DateTime.UtcNow
)来做,每当我们显示一个时,我们就会从UTC转换回用户的当地时间。
这工作正常,但我一直在阅读有关DateTimeOffset
以及它如何捕获对象本身的本地和UTC时间。 所以问题是,使用DateTimeOffset
与我们已经做的事情有什么好处?
#1楼
来自微软:
DateTimeOffset值的这些用法比DateTime值的用法更常见。 因此,应将DateTimeOffset视为应用程序开发的默认日期和时间类型。
来源: “在DateTime,DateTimeOffset,TimeSpan和TimeZoneInfo之间选择” , MSDN
我们使用DateTimeOffset
几乎所有内容,因为我们的应用程序处理特定时间点(例如,创建/更新记录时)。 另外,我们也在SQL Server 2008中使用DATETIMEOFFSET
。
当你想要只处理日期,只处理时间或者在一般意义上处理时,我认为DateTime
是有用的。 例如,如果你有,你要每天都去送行上午7时报警,你可以存储在一个DateTime
利用DateTimeKind
的Unspecified
,因为你希望它在早上7点,无论DST的熄灭。 但是,如果要表示警报发生的历史记录,则可以使用DateTimeOffset
。
使用DateTimeOffset
和DateTime
的混合时要特别小心,尤其是在分配和比较类型时。 此外,只有比较DateTime
是相同的情况下DateTimeKind
因为DateTime
忽略时区比较时偏移。
#2楼
DateTimeOffset
是瞬时时间的表示(也称为绝对时间 )。 通过这种方式,我的意思是对每个人都是普遍的时刻(不考虑闰秒 ,或时间膨胀的相对论效应)。 另一种表示瞬时时间的方法是使用DateTime
,其中.Kind
是DateTimeKind.Utc
。
这与日历时间 (也称为民用时间 )不同,后者是某人日历上的位置,全球有许多不同的日历。 我们将这些日历称为时区 。 日历时间由DateTime
表示,其中.Kind
是DateTimeKind.Unspecified
,或DateTimeKind.Local
。 并且.Local
仅在您隐含了解使用结果的计算机位置的情况下才有意义。 (例如,用户的工作站)
那么,为什么DateTimeOffset
而不是UTC DateTime
? 这完全取决于观点。 让我们用一个类比 - 我们假装是摄影师。
想象一下,您正站在日历时间轴上,将摄像机对准在您面前的瞬时时间线上的人。 您根据时区规则排列相机 - 由于夏令时,或由于您所在时区的法律定义的其他更改而定期更改。 (你没有稳定的手,所以你的相机不稳定。)
站在照片中的人会看到相机来自的角度。 如果其他人正在拍照,他们可能来自不同的角度。 这就是DateTimeOffset
的Offset
部分所代表的内容。
因此,如果您将相机标记为“东部时间”,有时您指向-5,有时您指向-4。 世界各地都有摄像机,所有摄像机都标有不同的东西,并且从不同角度指向同一瞬时时间轴。 其中一些是紧挨着(或在彼此之上),因此只知道偏移量不足以确定时间与哪个时区相关。
UTC怎么样? 嗯,这是一台相机,保证有稳定的手。 它在三脚架上,牢固地固定在地面上。 它不会去任何地方。 我们将其视角称为零偏移。

那么 - 这个类比告诉我们什么? 它提供了一些直观的指导。
如果您特别表示相对于某个地方的时间,请使用
DateTime
在日历时间内表示。 请确保您不会将一个日历与另一个日历混淆。Unspecified
应该是您的假设。Local
仅来自DateTime.Now
。 例如,我可能会获得DateTime.Now
并将其保存在数据库中 - 但是当我检索它时,我必须假设它是Unspecified
。 我无法依赖我的本地日历与最初拍摄的日历相同。如果您必须始终确定当下,请确保您正在表示瞬时时间。 使用
DateTimeOffset
强制执行它,或按惯例使用UTCDateTime
。如果您需要跟踪瞬时时刻,但您还想知道“用户在当地日历上的时间是什么时候?” - 那么你必须使用
DateTimeOffset
。 这对于计时系统非常重要,例如 - 无论是技术问题还是法律问题。如果您需要修改以前记录的
DateTimeOffset
- 仅在偏移量中没有足够的信息以确保新偏移量仍然与用户相关。 您还必须存储时区标识符(想想 - 我需要该摄像机的名称,这样即使位置发生变化,我也可以拍摄新照片)。还应该指出, Noda Time有一个名为
ZonedDateTime
的表示,而.Net基类库没有任何类似的东西。 您需要存储DateTimeOffset
和TimeZoneInfo.Id
值。有时,您会希望表示“任何人正在查看它”的本地日历时间。 例如,在定义今天的意义时。 今天总是午夜到午夜,但这些代表了瞬时时间轴上几乎无限数量的重叠范围。 (实际上我们有一个有限数量的时区,但是你可以将偏移量表示为勾号)所以在这些情况下,请确保你理解如何限制“谁在问?” 向下一个时区提问,或者处理将它们转换回适当的瞬时时间。
以下是关于DateTimeOffset
的一些其他一些DateTimeOffset
这个类比的内容,以及一些保持笔直的提示:
如果比较两个
DateTimeOffset
值,则在比较之前首先将它们标准化为零偏移量。 换句话说,2012-01-01T00:00:00+00:00
和2012-01-01T02:00:00+02:00
指的是相同的瞬时时刻,因此是等效的。如果你正在做的任何单元测试和需要肯定的偏移量, 同时测试的
DateTimeOffset
值,.Offset
财产分开。.Net框架内置了一个单向隐式转换,允许您将
DateTime
传递给任何DateTimeOffset
参数或变量。 这样做时,.Kind
重要 。 如果你传递一个UTC一种,它将搭载与零偏移,但如果你通过其中.Local
或.Unspecified
,就会认为是本地的 。 该框架基本上是在说:“嗯,你让我把日历时间转换为瞬间时间,但我不知道它来自哪里,所以我只想使用本地日历。” 如果您在具有不同时区的计算机上加载未指定的DateTime
,这是一个巨大的问题。 (恕我直言 - 这应该抛出异常 - 但事实并非如此。)
无耻插头:
许多人与我分享他们认为这个类比非常有价值,所以我把它包括在我的Pluralsight课程, 日期和时间基础 。 您将在标题为“日历时间与瞬时时间”的剪辑中找到第二个模块“上下文事项”中相机类比的逐步演练。
#3楼
大多数答案都很好,但我想添加一些MSDN链接以获取更多信息
- 简短的DateTime历史 - 由BCL团队的Anthony Moore撰写
- 在Datetime和DateTime Offset之间选择 - 由MSDN
- 不要忘记SQL Server 2008以后有一个新的数据类型作为DateTimeOffset
- .NET Framework包括DateTime , DateTimeOffset和TimeZoneInfo类型,所有这些类型都可用于构建使用日期和时间的应用程序。
- 使用日期和时间-MSDN执行算术运算
#4楼
我看到的DateTimeOffset的唯一不利方面是微软“忘记”(按设计)在XmlSerializer类中支持它。 但它已被添加到XmlConvert实用程序类中。
我说继续使用DateTimeOffset和TimeZoneInfo是因为所有的好处,只要注意创建将要或可能序列化为XML(或所有业务对象)的实体。
#5楼
最重要的区别是DateTime不存储时区信息,而DateTimeOffset则存储时区信息。
尽管DateTime区分UTC和Local,但绝对没有与之关联的显式时区偏移。 如果进行任何类型的序列化或转换,将使用服务器的时区。 即使您通过添加分钟来手动创建本地时间来抵消UTC时间,您仍然可以在序列化步骤中获得位,因为(由于DateTime中没有任何显式偏移),它将使用服务器的时区偏移量。
例如,如果使用Json.Net和ISO日期格式序列化Kind = Local的DateTime值,您将获得类似2015-08-05T07:00:00-04
的字符串。 请注意,最后一部分(-04)与您的DateTime或您用于计算它的任何偏移无关......它只是服务器的时区偏移量。
同时,DateTimeOffset显式包含偏移量。 它可能不包括时区的名称,但至少它包含偏移量,如果序列化它,您将获得值中明确包含的偏移量,而不是服务器的本地时间。
来源:oschina
链接:https://my.oschina.net/stackoom/blog/3149061