当前,我们有一种DateTime
以TimeZone感知方式处理.NET的标准方法:每当我们生成一个.NET时,DateTime
我们都会在UTC中进行处理(例如,使用DateTime.UtcNow
),并且每当显示一个时,我们都会从UTC转换回用户的本地时间。
效果很好,但是我一直在阅读有关DateTimeOffset
它如何捕获对象本身中的本地时间和UTC时间的信息。所以问题是,DateTimeOffset
与我们已经做过的相比,使用的优势是什么?
当前,我们有一种DateTime
以TimeZone感知方式处理.NET的标准方法:每当我们生成一个.NET时,DateTime
我们都会在UTC中进行处理(例如,使用DateTime.UtcNow
),并且每当显示一个时,我们都会从UTC转换回用户的本地时间。
效果很好,但是我一直在阅读有关DateTimeOffset
它如何捕获对象本身中的本地时间和UTC时间的信息。所以问题是,DateTimeOffset
与我们已经做过的相比,使用的优势是什么?
Answers:
DateTimeOffset
是瞬时时间(也称为绝对时间)的表示。所谓时间,是指每个人都普遍拥有的时刻(不考虑leap秒或时间膨胀的相对论效应)。表示瞬时时间的另一种方法是使用DateTime
where .Kind
是DateTimeKind.Utc
。
这与日历时间(也称为民事时间)不同,后者是某人的日历上的位置,并且全球有许多不同的日历。我们称这些日历为时区。日历时间由a表示DateTime
,其中.Kind
是DateTimeKind.Unspecified
或DateTimeKind.Local
。而.Local
仅仅是在你有其中正在使用该结果的计算机位于一个隐含的理解情景意义。(例如,用户的工作站)
那么,为什么要DateTimeOffset
使用UTC DateTime
? 这都是关于视角的。 让我们用一个比喻-我们假装是摄影师。
想象一下您正站在日历时间轴上,将相机对准摆在您面前的瞬时时间轴上的人。您根据时区的规则为相机排好队-夏令时或其他时区的法律定义会定期更改这些规则。(您的手没有稳固,因此相机抖动。)
站在照片中的人会看到您的相机所成的角度。如果其他人正在拍照,那么他们可能来自不同的角度。这就是代表的Offset
部分DateTimeOffset
。
因此,如果将相机标记为“东部时间”,则有时指向-5,有时指向-4。世界各地都有相机,它们都标有不同的东西,并且都从不同的角度指向同一瞬时时间轴。它们中的一些彼此相邻(或位于彼此顶部),因此仅知道偏移量不足以确定时间与哪个时区相关。
那UTC呢?好吧,这是一台保证手稳固的相机。它在三脚架上,牢固地固定在地面上。它不会去任何地方。我们称其视角为零偏移。
那么-这个比喻告诉我们什么?它提供了一些直观的准则-
如果要特别表示相对于某个地方的时间,请在日历时间中使用来表示DateTime
。只要确保您从未将一个日历与另一个日历混淆即可。 Unspecified
应该是你的假设。 Local
仅来自有用DateTime.Now
。例如,我可能会获取DateTime.Now
并将其保存在数据库中-但是在检索它时,我必须假定它是Unspecified
。我不能相信我的本地日历与最初使用的日历相同。
如果必须始终确定时刻,请确保您代表的是瞬时时间。使用DateTimeOffset
强制执行,或者使用UTC DateTime
通过约定。
如果您需要跟踪瞬时时间,但又想知道“用户认为该时间是他们本地日历中的什么时间?” -那么您必须使用DateTimeOffset
。例如,这对于计时系统非常重要-出于技术和法律方面的考虑。
如果您需要修改以前记录的内容DateTimeOffset
-仅偏移量中没有足够的信息来确保新偏移量仍与用户相关。您还必须存储时区标识符(请考虑-我需要该摄像机的名称,以便即使位置发生了变化也可以拍摄新照片)。
还应该指出的是,Noda Time具有ZonedDateTime
为此的表示形式,而.Net基类库没有类似的表示形式。您将需要同时存储a DateTimeOffset
和一个TimeZoneInfo.Id
值。
有时,您需要代表“ whover在看它”本地的日历时间。例如,在定义今天意味着什么时。今天始终是午夜至午夜,但它们表示瞬时时间轴上几乎无限数量的重叠范围。(实际上,时区是有限的,但是您可以将偏移量表示为滴答声。)因此,在这种情况下,请确保您了解如何限制“谁在问谁”?问题降到单个时区,或酌情将其转换回瞬时时间。
以下是支持该类比的其他一些信息DateTimeOffset
,以及保持类比的一些技巧:
如果比较两个DateTimeOffset
值,则在比较之前先将它们归一化为零偏移。换句话说,2012-01-01T00:00:00+00:00
和2012-01-01T02:00:00+02:00
指的是相同的瞬时力矩,因此是等效的。
如果你正在做的任何单元测试,需要一定的偏移量,测试的两个的DateTimeOffset
值,和.Offset
分别财产。
.Net框架内置了一种单向隐式转换,使您可以将a传递给DateTime
任何DateTimeOffset
参数或变量。在这样做时,该.Kind
事项。如果您传递的是UTC类型,它将以零偏移量进行输入,但是如果您传递.Local
或.Unspecified
,则将假定它是local。该框架基本上是在说:“好吧,您让我将日历时间转换为瞬时时间,但是我不知道这是从哪里来的,所以我将使用本地日历。” 如果DateTime
在具有不同时区的计算机上加载未指定的内容,这将是一个巨大的难题。(恕我直言-这应该引发异常-但事实并非如此。)
无耻的插头:
许多人与我分享了他们发现这种类比的价值,因此我将其包含在我的Pluralsight课程“ 日期和时间基础知识”中。您可以在标题为“日历时间与瞬时时间”的剪辑的第二个模块“上下文问题”中找到有关摄像机类比的逐步介绍。
DateTimeOffset
在C#中有一个,则应该DATETIMEOFFSET
在SQL Server中将其持久化到一个。 DATETIME2
或仅DATETIME
(取决于所需范围)适合常规DateTime
值。是的-您可以通过任何时区+ dto或utc配对来解析本地时间。区别在于-您是否总是要在每个解决方案中计算规则,还是要预先计算它们?在许多情况下(有时出于法律考虑),DTO是更好的选择。
DateTimeOffset.Now
对的,如果您在服务器上说,您确实会得到服务器的偏移量。关键是该DateTimeOffset
类型可以保留该偏移量。您可以轻松地在客户端上执行此操作,然后将其发送到服务器,然后服务器将知道客户端的偏移量。
从Microsoft:
这些对DateTimeOffset值的用法比对DateTime值的用法更为普遍。因此,应将DateTimeOffset视为应用程序开发的默认日期和时间类型。
来源:“在DateTime,DateTimeOffset,TimeSpan和TimeZoneInfo之间选择”,MSDN
DateTimeOffset
当我们的应用程序处理特定的时间点(例如,创建/更新记录的时间)时,我们几乎使用所有内容。附带说明一下,我们DATETIMEOFFSET
也在SQL Server 2008中使用。
DateTime
当您只想处理日期,只处理时间或一般意义上的处理时,我认为这很有用。例如,如果您有一个要每天早上7点关闭的警报,则可以将其存储在DateTime
一个DateTimeKind
of中,Unspecified
因为您希望它在早上7点关闭,而不考虑夏令时。但是,如果要表示警报发生的历史记录,可以使用DateTimeOffset
。
混合使用时尤其要小心DateTimeOffset
,DateTime
尤其是在类型之间进行分配和比较时。另外,仅比较DateTime
相同的实例,DateTimeKind
因为DateTime
比较时会忽略时区偏移。
Kind
相同,但比较可能会出错。如果双方都没有,DateTimeKind.Unspecified
您真的不知道他们来自同一时区。如果双方都是DateTimeKind.Local
,则大多数比较都会很好,但是您可能仍然会出错,因为一方在当地时区中含糊不清。真正只有DateTimeKind.Utc
比较是万无一失的,是的,DateTimeOffset
通常是首选。(干杯!)
在一些地方DateTimeOffset
有意义。一种是您要处理重复发生的事件和夏令时。假设我想设置一个闹钟,每天上午9点响起。如果我使用“存储为UTC,显示为本地时间”规则,则在夏令时生效时,警报将在其他时间响起。
可能还有其他例子,但是上面的示例实际上是我过去遇到的一个示例(这是在添加DateTimeOffset
BCL 之前的示例-当时我的解决方案是将时间明确存储在本地时区,然后保存时区信息:基本上是DateTimeOffset
内部执行的操作)。
最重要的区别是DateTime不存储时区信息,而DateTimeOffset则存储时区信息。
尽管DateTime区分UTC和Local,但是绝对没有与之关联的显式时区偏移。如果您进行任何类型的序列化或转换,将使用服务器的时区。即使您通过添加分钟来偏移UTC时间来手动创建本地时间,您仍然可以在序列化步骤中得到一点好处,因为(由于DateTime中没有任何明确的偏移)它将使用服务器的时区偏移。
例如,如果使用Json.Net和ISO日期格式将Kind = Local的DateTime值序列化,则会得到类似的字符串2015-08-05T07:00:00-04
。请注意,最后一部分(-04)与您的DateTime或您用于计算它的任何偏移量无关...纯粹是服务器的时区偏移量。
同时,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
大多数答案都不错,但是我想添加更多MSDN链接以获得更多信息。
一个主要区别是DateTimeOffset
可以结合使用,TimeZoneInfo
以将时区转换为当前时间以外的时区。
这在不同时区的用户访问的服务器应用程序(例如ASP.NET)上很有用。
我看到的DateTimeOffset唯一不利的一面是,微软(通过设计)“忘记了”在其XmlSerializer类中支持它。但是此后已将其添加到XmlConvert实用程序类中。
我说要继续使用并使用DateTimeOffset和TimeZoneInfo,因为有很多好处,只是在创建将要或可能要序列化到XML或从XML序列化的实体(然后是所有业务对象)时要当心。