目前,我们有一种以时区感知方式处理.NET日期时间的标准方法:每当我们生成一个DateTime时,我们都使用UTC(例如,使用DateTime.UtcNow时),每当我们显示一个日期时,我们都将从UTC转换回用户的本地时间。
这很好,但是我一直在读关于DateTimeOffset以及它如何捕获对象本身的本地和UTC时间。所以问题是,使用DateTimeOffset与我们已经做的相比,有什么好处呢?
- 下面有一些很好的答案。但我仍然想知道,如果有什么,能说服你开始使用datetimeoffset。
- 还可以看看什么时候您更喜欢datetime而不是datetimeoffset
- 说到存储,stackoverflow.com/questions/4715620/…也很有趣。
- 对于那些不知道的人来说,这个电脑迷视频很好地介绍了导致处理日期复杂化的一些(许多)问题:youtube.com/watch?v=-5wpm gesoy[fwiw我们使用与操作相同的方法。显示前一切都是UTC]
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强制执行,或按约定使用utc DateTime。好的。
如果您需要跟踪瞬间时间,但您还想知道"用户认为它在他们的本地日历上是什么时间?"-然后您必须使用DateTimeOffset。这对于计时系统非常重要,例如,对于技术和法律问题。好的。
如果您需要修改以前记录的DateTimeOffset—您在偏移量中没有足够的信息来确保新偏移量仍然与用户相关。你还必须存储一个时区标识符(想想——我需要那个摄像头的名称,这样即使位置改变了,我也可以拍一张新照片)。好的。
还应该指出的是,noda time有一个称为ZonedDateTime的表示,而.NET基类库没有类似的内容。您需要同时存储DateTimeOffset和TimeZoneInfo.Id值。好的。
有时,您会希望表示一个日历时间,它是"任何人都在看它"的本地时间。例如,在定义今天的含义时。今天总是从午夜到午夜,但这些都代表了瞬时时间线上几乎无限多的重叠范围。(实际上,我们有有限的时区,但您可以将偏移量减到刻度)因此,在这些情况下,请确保您了解如何限制"谁在问?"把问题缩小到一个时区,或者在适当的时候把它们转换回即时时间。好的。
下面是一些关于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,这是一个巨大的错误。(imho——应该有例外——但事实并非如此。)好的。
无耻插头:好的。
许多人和我分享,他们发现这个类比非常有价值,所以我把它包括在我的多元课程,日期和时间的基础上。您将在题为"日历时间与即时时间"的剪辑中的第二个模块"上下文关系"中找到相机类比的逐步演练。好的。好啊。
- @马特约翰逊:同意。回答得很好。您建议将这些值存储在SQL中作为datetimeoffset()或datetime2()吗?对于在UTC中创建大多数时间戳,我认为,如果不需要单击时间,您可以在数据库端使用sysutcdatetime()的存储过程中执行此操作,并避免许多条目令人头疼。那么您只需要知道timezoneinfo.id就可以为本地用户解码,对吗?如果我们正在处理一个基于浏览器的应用程序,在处理日光节约方面有什么建议吗?我认为最好的方法是让用户设置时区并保持会话状态。
- @Zackjannsen如果您在C中有一个DateTimeOffset,那么您应该在SQL Server中将它持久化为DateTimeOffset。对于常规的DATETIME值,DATETIME2或只是DATETIME(取决于所需的范围)是可以接受的。是-您可以从任何时区+DTO或UTC对中解析本地时间。区别在于——您总是想用每个解决方案计算规则,还是想预先计算规则?在许多情况下(有时出于法律考虑),DTO是更好的选择。
- @扎克詹森的问题的第二部分,我建议尽可能多地做服务器端。对于时区计算来说,javascript并不是那么好。如果必须这样做,请使用其中一个库。但服务器端是最好的。如果您还有其他更详细的问题,请为他们开始一个新的S.O.问题,如果可以,我会回答。谢谢。
- @马特约翰逊:这是我更详细的问题-stackoverflow.com/questions/15671675/…
- @马特约翰逊:谢谢。只是为了澄清-您建议对处理utctime+timezoneoffset+datetimeoffset=localtime的时区进行计算或预先计算的增量。如果使用"预先计算的",我想他们可以将它保存在基于时区ID/名称的记录数据库中。或者只使用应用程序中的功能(TimeZoneInfo或ASP.NET 3.5+中的TimeZone类)更简单?
- @扎克詹森-如果您只是在谈论时间戳(例如数据库行上的createdDate或updatedDate),那么您可能只想存储UTC。您可以在退出时将其转换为用户显示的时区。将UTC日期时间传递到控制器或Web窗体,并在离开Web服务器之前将其转换为用户本地时间。如果您正在呈现HTML,那么您应该在那里决定如何为您的用户文化设置它的格式。如果您将其作为数据(XML、JSON等)传递,那么您可能希望将其作为ISO8601传递,并稍后使用MOMENTJS对其进行格式化。
- 让我们在聊天中继续讨论
- 根据您的回答"用户认为它在他们的本地日历上是什么时候?-然后,您必须使用datetimeoffset"似乎c datetimeoffset保存了本地偏移量,但ti不保存,而是始终保存服务器偏移量,因此您将不知道"用户认为它在其本地日历上的时间是多少?"
- @乔奥莱姆-这取决于你从哪里得到的。您是正确的,如果您在服务器上说DateTimeOffset.Now,您确实会得到服务器的偏移量。关键是,DateTimeOffset类型可以保留该偏移量。您可以在客户机上很容易地做到这一点,将其发送到服务器,然后您的服务器就会知道客户机的偏移量。
- 真的很喜欢照相机的类比。
- 所以我不明白的是,如果DateTimeOffset在某种类型的UTC中提供了超过DateTime的附加功能,或者使用起来更方便。
- @不管是谁,你都能及时得到一个准确的时刻。但只有使用DateTimeOffset,你才能知道观察者认为它是在他们当地的时钟上。
- @你认为这是什么意思?我有一个以UTC为单位的日期,客户告诉我他们对UTC的偏移量是多少,我给他们调整的日期。所以我很困惑为什么还有其他的事情和这个行动有关。
- @themuffinman-客户知道他们当前对UTC的偏移量是多少。他们不一定知道当时的情况,除非他们也提供了一个完整的时区。另外,不同的客户机可能正在查看相同的数据,并且他们可能位于不同的时区。还要考虑客户机可能不是一个真实的人,而是一个算法。例如,您可以有一个业务规则,即在上午6:00之前开始工作的员工可以获得加班费。现在想象一下,你在全国或全世界的时区都有员工。
- 如果您只记录了UTC时间,则必须使用员工的时区来确定本地时间,并且查找可能会很昂贵。现在假设一个员工可以在多个时区工作。这就使得问题更加复杂了,因为你必须保存一份员工变动的历史记录表。如果我们最初存储了DateTimeOffset,那么就不需要这些,因为我们在一个数据值中记录了本地时间和转换为UTC所需的值。这只是一个例子,我相信你能想到其他人。
- @因此,听起来使用DateTimeOffset可以让您以不同的方式使用日期时间。如果我在当地时间早上6点打卡,对我来说这是UTC下午2点偏移量-8,那么根据使用情况,我可以将该UTC时间转换为-8偏移量,这样每个人都可以看到我在早上6点打卡,但是如果我想以另一种方式使用它,那么我仍然有UTC,并且可以根据客户提供的偏移量给他们当地时间(忽略第8点中的-8)。OSE病例)。对吗?
- 是的,没错。除了DTO存储为(本地时间、偏移量)对,而不是(UTC时间、偏移量)对。换句话说,与UTC的偏差已经反映在当地时间中。要转换回UTC,请反转偏移量的符号并将其应用于本地时间。
- 这是一个非常好的答案,对于混乱的日期时间库构造函数和方法来说,这真的很有意义。
- @马特约翰逊——我们能不能打开一个聊天室/讨论室,这样我就可以钻你的大脑一会儿?
- @greencabbage chat.stackoverflow.com/rooms/info/50244/datetime
- 我认为时区和UTC与相机和摄影师的角度关系不大。带着你的孩子穿越时区,即使是7岁的孩子也能理解。
- 如果一些不常见的(datetimeoffset)与另一个常见(datetime)的东西相比,需要这样的差异解释,我怀疑它甚至是出于任何好的原因而需要的。
- 我知道,由于系统要求,不可能在任何地方都存在这种情况;但我喜欢StackOverflow如何通过记录UTC DateTime,在适当的情况下呈现相对时间(例如"27秒前"),并以其他方式转储UTC值来简化事情。简单!而且人类处理近场相对时间的认知负荷远小于任何邻近区域的绝对时间,所以它也是很棒的用户体验!
- 看完照片后我现在更困惑了
- @mattjohnson("但只有使用datetimeoffset,你才能知道观察者认为它是在他们的本地时钟上的时间。")这与这个答案(尤其是图片)所表明的恰恰相反。我想我会给出第二个答案(用于决策),因为它实际上指出了每个问题的正确用例。
- @ NH。-在照片中,我省略了日期,也许我不应该这样做。但其理念是,左侧仅具有其本地时间(如DateTime),右侧同时传递本地时间,并与UTC(如DateTimeOffset)进行偏移。
- "午夜"的24:00的现状是什么?en.wikipedia.org/wiki/iso_
- 这是一个惊人的类比,并且解释得非常清楚。伟大的回答!
微软:
These uses for DateTimeOffset values are much more common than those for DateTime values. As a result, DateTimeOffset should be considered the default date and time type for application development.
source:"Chooseing between datetime,datetimeoffset,timespan,and timezoneinfo",msdn
当我们的应用程序处理特定的时间点(例如创建/更新记录的时间)时,我们几乎对所有内容都使用DateTimeOffset。另外,我们在SQL Server 2008中也使用了DateTimeOffset。
我认为DateTime在你只想处理日期、时间或一般意义上的事务时是有用的。例如,如果你有一个每天早上7点要响的警报,你可以使用Unspecified的DateTimeKind把它存储在DateTime中,因为你想让它在早上7点响,而不考虑DST。但是,如果您想表示报警事件的历史记录,您可以使用DateTimeOffset。
在混合使用DateTimeOffset和DateTime时要小心,尤其是在分配和比较类型时。另外,只比较相同的DateTime实例,因为DateTime在比较时忽略时区偏移。
- 公认的答案太长了,类比也很紧张,这是一个更好更简洁的答案。
- 我只想说我也喜欢这个答案,而且被否决了。虽然在最后一部分——即使确保Kind是相同的,比较也可能是错误的。如果双方都有DateTimeKind.Unspecified,你不知道他们来自同一时区。如果双方都是DateTimeKind.Local,大多数比较都会很好,但是你仍然可能会有错误,因为一方在本地时区是模糊的。实际上,只有DateTimeKind.Utc比较是非常简单的,是的,DateTimeOffset通常是首选的。(干杯!)
日期时间只能存储两个不同的时间,即本地时间和UTC。Kind属性指示哪个。
DateTimeOffset通过能够从世界任何地方存储本地时间来扩展此功能。它还存储本地时间和UTC之间的偏移量。请注意,除非向类中添加一个额外的成员来存储该UTC偏移量,否则datetime无法执行此操作。或者只与UTC合作。这本身就是个好主意。
在一些地方,DateTimeOffset是有意义的。一个是你处理经常性事件和夏令时。假设我想设置一个闹钟,每天早上9点响。如果我使用"存储为UTC,显示为本地时间"规则,那么当夏令时生效时,警报将在其他时间关闭。
可能还有其他的例子,但上面的例子实际上是我在过去遇到的(这是在将DateTimeOffset添加到bcl之前——当时我的解决方案是在本地时区显式存储时间,并将时区信息保存在它旁边:基本上就像DateTimeOffset在内部所做的那样)。
- DateTimeOffset无法解决DST问题
- 使用TimeZoneInfo类不携带DST规则。如果您使用的是.NET 3.5或更高版本,那么可以使用TimeZone或TimeZoneInfo类来处理必须与时区偏移一起处理夏令时的日期。
- 是的,一个很好的例外例子(报警应用程序),但是当时间比日期更重要时,你应该将它单独存储在应用程序的日程数据结构中,即事件类型=每日,时间=09:00。这里的重点是开发人员需要知道他们正在记录、计算或向用户展示的日期类型。尤其是现在的应用程序越来越全球化,我们有了互联网作为标准和大型应用程序商店来编写软件。作为一个侧节点,我还希望看到微软添加一个单独的日期和时间结构。
- 总结Jarrett和Zack的评论:听起来单靠datetimeoffset并不能解决DST问题,但是将datetimeoffset与timezoneinfo结合使用可以解决这个问题。这与日期时间没有什么不同,日期时间的类型是UTC。在这两种情况下,我都必须知道我将时间投影到的日历的时区(不仅仅是偏移)。(如果可能的话,我可以将其存储在用户的配置文件中,或者从客户机(如Windows)获取它)。听起来正确吗?
- "有几个地方datetimeoffset是有意义的。"——可以说,它通常比没有意义的多。
最重要的区别是datetime不存储时区信息,而datetimeoffset存储时区信息。
尽管datetime区分了UTC和本地时间,但绝对没有与之相关的显式时区偏移量。如果您进行任何类型的序列化或转换,将使用服务器的时区。即使通过添加分钟来偏移UTC时间手动创建本地时间,您仍然可以在序列化步骤中获得位,因为(由于在日期时间中没有任何显式偏移量)它将使用服务器的时区偏移量。
例如,如果使用json.net和iso日期格式序列化kind=local的日期时间值,则会得到类似2015-08-05T07:00:00-04的字符串。注意最后一部分(-04)与日期时间或用于计算它的任何偏移量无关…这只是服务器的时区偏移量。
同时,datetimeoffset显式地包括偏移量。它可能不包括时区的名称,但至少包括偏移量,如果序列化它,您将在值中获得显式包含的偏移量,而不是服务器的本地时间。
- 有了以上所有的答案,我想知道为什么没有人费心写下你的一句话来总结这一切。
- DateTimeOffset不存储时区信息。标题为"在日期时间、日期时间偏移量、时间跨度和时区信息之间选择"的MS Doc规定:"日期时间偏移量值不绑定到特定时区,但可以源自各种时区中的任何一个"。这就是说,date time offset是时区感知的,它包含了与UTC的偏移量,这就产生了所有的差异,这也是为什么在处理处理日期信息的应用程序开发时,它是MS推荐的默认类的原因。如果您真正关心数据来自哪个特定时区,则必须将其单独保存。
- 是的,但正如许多地方所显示的那样,+或-小时对你所处的时区毫无意义,而且最终是无用的。根据您需要做的,您可以将日期时间存储为"种类"。未指定日期时间,然后存储其时区的ID,我认为您实际上会过得更好。
大多数答案都很好,但是我想添加更多的msdn链接以获取更多信息。
- 日期时间简史-由BCL团队安东尼·摩尔撰写
- 选择日期时间和日期时间偏移量-按msdn
- 不要忘记,SQL Server 2008以后版本的数据类型为datetimeoffset
- .NET框架包括日期时间、日期时间偏移量和TimeZoneInfo类型,所有这些类型都可用于构建应用程序这与日期和时间有关。
- 使用日期和时间msdn执行算术运算
- 还有-blogs.msdn.microsoft.com/davidrickard/2012/04/06/…
一个主要的区别是,DateTimeOffset可以与TimeZoneInfo结合使用,在时区内转换为本地时间,而不是当前时区。
这在不同时区的用户访问的服务器应用程序(如ASP.NET)上很有用。
- TimeZoneInfo还可以与datetime一起使用
- @Bugeo Bugeo是真的,但存在风险。您可以通过在每个日期上首先调用"touniversaltime"来比较两个日期时间。如果比较中只有一个值,即datetimekind=未指定,则策略将失败。当需要转换到本地时间时,这种失败的可能性是考虑日期时间偏移量超过日期时间的原因。
- 如上所述,我认为在这个场景中,存储TimeZoneID比使用DateTimeOffset更好,这最终意味着什么都没有。
这段来自微软的代码解释了一切:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // 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 |
我看到的datetimeoffset的唯一负面一面是,微软"忘记"(按设计)在其xmlserializer类中支持它。但后来它被添加到了xmlconvert实用程序类中。
xmlconvert.todatetimeoffset
xmlconvert.to字符串
我说,继续使用datetimeoffset和timezoneinfo,因为它有所有的好处,所以在创建将要或可能序列化到XML或从XML序列化的实体时要小心(然后是所有业务对象)。