DateTime和DateTimeOffset之间的区别是什么,什么时候应该使用?
目前,我们有一种以时区感知的方式处理. net DateTime的标准方法:每当我们生成一个DateTime时,我们都使用UTC(例如使用DateTime. utcnow),每当我们显示一个DateTime时,我们从UTC转换回用户的本地时间。
这很好,但我已经阅读了DateTimeOffset以及它如何捕获对象本身的本地时间和UTC时间。
DateTime和DateTimeOffset之间的区别是什么,什么时候应该使用?
目前,我们有一种以时区感知的方式处理. net DateTime的标准方法:每当我们生成一个DateTime时,我们都使用UTC(例如使用DateTime. utcnow),每当我们显示一个DateTime时,我们从UTC转换回用户的本地时间。
这很好,但我已经阅读了DateTimeOffset以及它如何捕获对象本身的本地时间和UTC时间。
当前回答
微软的这段代码解释了一切:
// 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
其他回答
DateTime只能存储两个不同的时间,本地时间和UTC。Kind属性指明是哪一种。
DateTimeOffset在此基础上进行了扩展,它能够存储世界上任何地方的本地时间。它还存储本地时间与UTC之间的偏移量。请注意DateTime无法做到这一点,除非您向类中添加一个额外的成员来存储UTC偏移量。或者只使用UTC。顺便说一句,这本身就是个好主意。
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值的这些用法比DateTime值的用法更常见。因此,应该将DateTimeOffset视为应用程序开发的默认日期和时间类型。
来源:“选择DateTime, DateTimeOffset, TimeSpan和TimeZoneInfo”,MSDN
当我们的应用程序处理特定的时间点时(例如,当一个记录被创建/更新时),我们几乎所有的事情都使用DateTimeOffset。顺便说一句,我们在SQL Server 2008中也使用了DATETIMEOFFSET。
我认为,当您只想处理日期、时间或在一般意义上处理这两种情况时,DateTime非常有用。例如,如果您有一个闹钟,希望每天早上7点响,您可以使用未指定的DateTimeKind将其存储在DateTime中,因为您希望它在早上7点响,而不考虑夏令时。但是,如果希望表示告警发生的历史,则可以使用DateTimeOffset。
在混合使用DateTimeOffset和DateTime时要谨慎,特别是在类型之间进行分配和比较时。同样,只比较DateTimeKind相同的DateTime实例,因为DateTime在比较时忽略了时区偏移。
日期时间.现在 免费 03 12月 21 18:40:11
DateTimeOffset.Now 免费 03 12月 21 18:40:11 +02:00
因此,DateTimeOffset存储有关时间如何与UTC(基本上是时区)相关的信息。
最重要的区别是DateTime不存储时区信息,而DateTimeOffset存储时区信息。
尽管DateTime区分UTC和Local,但绝对没有与之相关的显式时区偏移。如果执行任何类型的序列化或转换,将使用服务器的时区。即使您通过添加分钟来偏移UTC时间来手动创建本地时间,您仍然可以在序列化步骤中获得bit,因为(由于DateTime中没有任何显式偏移)它将使用服务器的时区偏移。
例如,如果你使用Json用Kind=Local序列化一个DateTime值。Net和ISO日期格式,你会得到一个像2015-08-05T07:00:00-04这样的字符串。注意最后一部分(-04)与你的DateTime或你用来计算它的任何偏移无关…这仅仅是服务器的时区偏移。
同时,DateTimeOffset显式地包含偏移量。它可能不包括时区的名称,但至少包括偏移量,如果序列化它,您将在值中获得显式包含的偏移量,而不是服务器的本地时间。