DateTime和DateTimeOffset之间的区别是什么,什么时候应该使用?
目前,我们有一种以时区感知的方式处理. net DateTime的标准方法:每当我们生成一个DateTime时,我们都使用UTC(例如使用DateTime. utcnow),每当我们显示一个DateTime时,我们从UTC转换回用户的本地时间。
这很好,但我已经阅读了DateTimeOffset以及它如何捕获对象本身的本地时间和UTC时间。
DateTime和DateTimeOffset之间的区别是什么,什么时候应该使用?
目前,我们有一种以时区感知的方式处理. net DateTime的标准方法:每当我们生成一个DateTime时,我们都使用UTC(例如使用DateTime. utcnow),每当我们显示一个DateTime时,我们从UTC转换回用户的本地时间。
这很好,但我已经阅读了DateTimeOffset以及它如何捕获对象本身的本地时间和UTC时间。
在一些地方,DateTimeOffset是有意义的。一种是处理重复事件和夏时制时。假设我想设置一个闹钟,让它每天早上9点响。如果我使用“存储为UTC,显示为本地时间”规则,那么当夏令时生效时,闹钟将在不同的时间响起。
可能还有其他的例子,但上面的例子实际上是我过去遇到的一个(这是在将DateTimeOffset添加到BCL之前-我当时的解决方案是显式地将时间存储在本地时区,并将时区信息保存在旁边:基本上是DateTimeOffset内部做的事情)。
一个主要的区别是DateTimeOffset可以与TimeZoneInfo一起使用,以转换为当前时区以外的本地时间。
这对于由不同时区的用户访问的服务器应用程序(例如ASP.NET)非常有用。
DateTime只能存储两个不同的时间,本地时间和UTC。Kind属性指明是哪一种。
DateTimeOffset在此基础上进行了扩展,它能够存储世界上任何地方的本地时间。它还存储本地时间与UTC之间的偏移量。请注意DateTime无法做到这一点,除非您向类中添加一个额外的成员来存储UTC偏移量。或者只使用UTC。顺便说一句,这本身就是个好主意。
从微软:
DateTimeOffset值的这些用法比DateTime值的用法更常见。因此,应该将DateTimeOffset视为应用程序开发的默认日期和时间类型。
来源:“选择DateTime, DateTimeOffset, TimeSpan和TimeZoneInfo”,MSDN
当我们的应用程序处理特定的时间点时(例如,当一个记录被创建/更新时),我们几乎所有的事情都使用DateTimeOffset。顺便说一句,我们在SQL Server 2008中也使用了DATETIMEOFFSET。
我认为,当您只想处理日期、时间或在一般意义上处理这两种情况时,DateTime非常有用。例如,如果您有一个闹钟,希望每天早上7点响,您可以使用未指定的DateTimeKind将其存储在DateTime中,因为您希望它在早上7点响,而不考虑夏令时。但是,如果希望表示告警发生的历史,则可以使用DateTimeOffset。
在混合使用DateTimeOffset和DateTime时要谨慎,特别是在类型之间进行分配和比较时。同样,只比较DateTimeKind相同的DateTime实例,因为DateTime在比较时忽略了时区偏移。
DateTimeOffset表示瞬时时间(也称为绝对时间)。我指的是一个对每个人都适用的时间时刻(不考虑闰秒或时间膨胀的相对论效应)。表示瞬时时间的另一种方法是使用DateTime,其中. kind是DateTimeKind.Utc。
这与日历时间(也称为民用时间)不同,后者是某人日历上的一个位置,全球各地有许多不同的日历。我们称这些日历为时区。日历时间由一个DateTime表示,其中. kind是DateTimeKind。未指定或DateTimeKind.Local。而. local仅在您了解使用结果的计算机的位置的情况下才有意义。(例如用户工作站)
那么,为什么是DateTimeOffset而不是UTC DateTime呢?这都是关于视角的问题。让我们打个比方——我们假装是摄影师。
想象一下,你正站在日历的时间轴上,用相机对准你面前的瞬时时间轴上的一个人。你可以根据你所在时区的规则来排列你的相机——由于夏令时或时区法定定义的其他变化,时区规则会定期变化。(你的手不稳,所以你的相机摇摇晃晃。)
站在照片中的人会看到你相机的拍摄角度。如果其他人在拍照,他们可以从不同的角度。这就是DateTimeOffset的Offset部分所表示的内容。
如果你把相机标为“东部时间”,有时你指向-5点,有时你指向-4点。世界各地都有摄像头,都标记着不同的东西,都从不同的角度指向同一个瞬时时间轴。其中一些是紧挨着(或在上面)的,所以仅仅知道偏移量不足以确定时间与哪个时区相关。
那么UTC呢?好吧,这是唯一一台保证手感稳定的相机。它在一个三脚架上,牢牢地固定在地面上。它哪儿也去不了。我们称它的角度为零偏移。
这个类比告诉我们什么?它提供了一些直观的指导方针
If you are representing time relative to some place in particular, represent it in calendar time with a DateTime. Just be sure you don't ever confuse one calendar with another. Unspecified should be your assumption. Local is only useful coming from DateTime.Now. For example, I might get DateTime.Now and save it in a database - but when I retrieve it, I have to assume that it is Unspecified. I can't rely that my local calendar is the same calendar that it was originally taken from. If you must always be certain of the moment, make sure you are representing instantaneous time. Use DateTimeOffset to enforce it, or use UTC DateTime by convention. If you need to track a moment of instantaneous time, but you want to also know "What time did the user think it was on their local calendar?" - then you must use a DateTimeOffset. This is very important for timekeeping systems, for example - both for technical and legal concerns. If you ever need to modify a previously recorded DateTimeOffset - you don't have enough information in the offset alone to ensure that the new offset is still relevant for the user. You must also store a timezone identifier (think - I need the name of that camera so I can take a new picture even if the position has changed). It should also be pointed out that Noda Time has a representation called ZonedDateTime for this, while the .Net base class library does not have anything similar. You would need to store both a DateTimeOffset and a TimeZoneInfo.Id value. Occasionally, you will want to represent a calendar time that is local to "whomever is looking at it". For example, when defining what today means. Today is always midnight to midnight, but these represent a near-infinite number of overlapping ranges on the instantaneous timeline. (In practice we have a finite number of timezones, but you can express offsets down to the tick) So in these situations, make sure you understand how to either limit the "who's asking?" question down to a single time zone, or deal with translating them back to instantaneous time as appropriate.
下面是其他一些关于DateTimeOffset的小细节,可以支持这个类比,以及一些保持直线的技巧:
If you compare two DateTimeOffset values, they are first normalized to zero offset before comparing. In other words, 2012-01-01T00:00:00+00:00 and 2012-01-01T02:00:00+02:00 refer to the same instantaneous moment, and are therefore equivalent. If you are doing any unit testing and need to be certain of the offset, test both the DateTimeOffset value, and the .Offset property separately. There is a one-way implicit conversion built in to the .Net framework that lets you pass a DateTime into any DateTimeOffset parameter or variable. When doing so, the .Kind matters. If you pass a UTC kind, it will carry in with a zero offset, but if you pass either .Local or .Unspecified, it will assume to be local. The framework is basically saying, "Well, you asked me to convert calendar time to instantaneous time, but I have no idea where this came from, so I'm just going to use the local calendar." This is a huge gotcha if you load up an unspecified DateTime on a computer with a different timezone. (IMHO - that should throw an exception - but it doesn't.)
无耻的插头:
很多人告诉我,他们觉得这个类比非常有价值,所以我把它包括在我的Pluralsight课程《日期和时间基础》中。您将在第二个模块“上下文问题”中找到相机类比的逐步演练,在题为“日历时间vs.瞬时时间”的剪辑中。
我认为DateTimeOffset唯一的缺点是微软“忘记”(故意)在他们的XmlSerializer类中支持它。但是它已经被添加到XmlConvert实用程序类中。
XmlConvert。ToDateTimeOffset
XmlConvert。ToString
我建议继续使用DateTimeOffset和TimeZoneInfo,因为它们都有好处,只是在创建将或可能序列化到XML或从XML序列化的实体时要小心(那时都是业务对象)。
最重要的区别是DateTime不存储时区信息,而DateTimeOffset存储时区信息。
尽管DateTime区分UTC和Local,但绝对没有与之相关的显式时区偏移。如果执行任何类型的序列化或转换,将使用服务器的时区。即使您通过添加分钟来偏移UTC时间来手动创建本地时间,您仍然可以在序列化步骤中获得bit,因为(由于DateTime中没有任何显式偏移)它将使用服务器的时区偏移。
例如,如果你使用Json用Kind=Local序列化一个DateTime值。Net和ISO日期格式,你会得到一个像2015-08-05T07:00:00-04这样的字符串。注意最后一部分(-04)与你的DateTime或你用来计算它的任何偏移无关…这仅仅是服务器的时区偏移。
同时,DateTimeOffset显式地包含偏移量。它可能不包括时区的名称,但至少包括偏移量,如果序列化它,您将在值中获得显式包含的偏移量,而不是服务器的本地时间。
微软的这段代码解释了一切:
// 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
如果你不想读这些很棒的答案,TLDR:-)
明确:
使用DateTimeOffset,因为时区被强制为UTC+0。
隐式:
使用DateTime,希望每个人都遵守不成文的时区规则,始终为UTC+0。
(开发者注意:显式总是比隐式更好!)
(对于Java开发人员,c# DateTimeOffset == Java OffsetDateTime,请阅读此:https://www.baeldung.com/java-zoneddatetime-offsetdatetime)
日期时间.现在 免费 03 12月 21 18:40:11
DateTimeOffset.Now 免费 03 12月 21 18:40:11 +02:00
因此,DateTimeOffset存储有关时间如何与UTC(基本上是时区)相关的信息。