简体   繁体   English

日期时间与日期时间偏移

[英]DateTime vs DateTimeOffset

Currently, we have a standard way of dealing with .NET DateTime 's in a TimeZone aware way: Whenever we produce a DateTime we do it in UTC (eg using DateTime.UtcNow ), and whenever we display one, we convert back from UTC to the user's local time.目前,我们有一种标准的方式来处理 .NET DateTime的 TimeZone 感知方式:每当我们生成DateTime我们都使用 UTC(例如使用DateTime.UtcNow ),并且每当我们显示一个时,我们从 UTC 转换回来到用户的本地时间。

That works fine, but I've been reading about DateTimeOffset and how it captures the local and UTC time in the object itself.这很好用,但我一直在阅读DateTimeOffset以及它如何捕获对象本身的本地和 UTC 时间。 So the question is, what would be the advantages of using DateTimeOffset compared to what we have already been doing?所以问题是,与我们已经在做的事情相比,使用DateTimeOffset有什么优势?

DateTimeOffset is a representation of instantaneous time (also known as absolute time ). DateTimeOffset瞬时时间(也称为绝对时间)的表示。 By that, I mean a moment in time that is universal for everyone (not accounting for leap seconds , or the relativistic effects of time dilation ).我的意思是每个人都通用的时刻(不考虑闰秒时间膨胀的相对论效应)。 Another way to represent instantaneous time is with a DateTime where .Kind is DateTimeKind.Utc .另一种表示瞬时时间的方法是使用DateTime ,其中.KindDateTimeKind.Utc

This is distinct from calendar time (also known as civil time ), which is a position on someone's calendar, and there are many different calendars all over the globe.这与日历时间(也称为民用时间)不同,后者是某人日历上的一个位置,全球有许多不同的日历。 We call these calendars time zones .我们称这些日历为时区 Calendar time is represented by a DateTime where .Kind is DateTimeKind.Unspecified , or DateTimeKind.Local .日历时间由DateTime表示,其中.KindDateTimeKind.UnspecifiedDateTimeKind.Local And .Local is only meaningful in scenarios where you have an implied understanding of where the computer that is using the result is positioned.并且.Local仅在您对使用结果的计算机的位置有隐含理解的情况下才有意义。 (For example, a user's workstation) (例如,用户的工作站)

So then, why DateTimeOffset instead of a UTC DateTime ?那么,为什么是DateTimeOffset而不是 UTC DateTime呢? It's all about perspective.这都是关于视角的。 Let's use an analogy - we'll pretend to be photographers.让我们打个比方——我们会假装自己是摄影师。

Imagine you are standing on a calendar timeline, pointing a camera at a person on the instantaneous timeline laid out in front of you.想象一下,您正站在日历时间轴上,将相机对准您面前即时时间轴上的一个人。 You line up your camera according to the rules of your timezone - which change periodically due to daylight saving time, or due to other changes to the legal definition of your time zone.您可以根据时区的规则排列相机 - 由于夏令时或时区法律定义的其他更改,时区规则会定期更改。 (You don't have a steady hand, so your camera is shaky.) (你的手不稳,所以你的相机摇晃。)

The person standing in the photo would see the angle at which your camera came from.站在照片中的人会看到您的相机来自的角度。 If others were taking pictures, they could be from different angles.如果其他人在拍照,他们可能从不同的角度。 This is what the Offset part of the DateTimeOffset represents.这就是DateTimeOffsetOffset部分所代表的。

So if you label your camera "Eastern Time", sometimes you are pointing from -5, and sometimes you are pointing from -4.因此,如果您将相机标记为“东部时间”,有时您会从 -5 指向,有时会从 -4 指向。 There are cameras all over the world, all labeled different things, and all pointing at the same instantaneous timeline from different angles.世界各地都有摄像机,都标记着不同的事物,并且都从不同的角度指向同一个瞬时时间线。 Some of them are right next to (or on top of) each other, so just knowing the offset isn't enough to determine which timezone the time is related to.它们中的一些紧邻(或重叠)彼此,因此仅知道偏移量不足以确定时间与哪个时区相关。

And what about UTC? UTC 呢? Well, it's the one camera out there that is guaranteed to have a steady hand.嗯,这是一款可以保证手稳定的相机。 It's on a tripod, firmly anchored into the ground.它在三脚架上,牢牢地固定在地面上。 It's not going anywhere.它不会去任何地方。 We call its angle of perspective the zero offset.我们将其视角称为零偏移。

瞬时时间与日历时间可视化

So - what does this analogy tell us?那么——这个比喻告诉我们什么? It provides some intuitive guidelines-它提供了一些直观的指南——

  • If you are representing time relative to some place in particular, represent it in calendar time with a DateTime .如果您特别表示相对于某个地方的时间,请使用DateTime以日历时间表示。 Just be sure you don't ever confuse one calendar with another.请确保您永远不会将一个日历与另一个混淆。 Unspecified should be your assumption. Unspecified应该是您的假设。 Local is only useful coming from DateTime.Now . Local只有来自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 .例如,我可能会获取DateTime.Now并将其保存在数据库中 - 但是当我检索它时,我必须假设它是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.使用DateTimeOffset来强制执行它,或者按照约定使用 UTC DateTime

  • 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 . - 那么你必须使用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.如果您需要修改以前记录的DateTimeOffset - 仅在偏移量中就没有足够的信息来确保新的偏移量仍然与用户相关。 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.还应该指出的是, Noda Time对此有一个称为ZonedDateTime的表示,而 .Net 基类库没有类似的东西。 You would need to store both a DateTimeOffset and a TimeZoneInfo.Id value.您需要同时存储DateTimeOffsetTimeZoneInfo.Id值。

  • 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.将问题缩小到单个时区,或根据需要将它们转换回瞬时时间。

Here are a few other little bits about DateTimeOffset that back up this analogy, and some tips for keeping it straight:这里有一些关于DateTimeOffset其他小点来支持这个类比,以及一些保持直截了当的提示:

  • If you compare two DateTimeOffset values, they are first normalized to zero offset before comparing.如果比较两个DateTimeOffset值,则在比较之前首先将它们归一化为零偏移量。 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.换句话说, 2012-01-01T00:00:00+00:002012-01-01T02:00:00+02:00指的是同一个瞬间,因此是等价的。

  • 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.如果你正在做的任何单元测试,需要一定的偏移量,同时测试的DateTimeOffset值, .Offset分别财产。

  • 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. .Net 框架内置了一种单向隐式转换,可让您将DateTime传递给任何DateTimeOffset参数或变量。 When doing so, the .Kind matters .这样做时, .Kind重要 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 .如果你传递一个UTC一种,它将搭载与零偏移,但如果你通过其中.Local.Unspecified ,就会认为是本地的 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.如果您在具有不同时区的计算机上加载未指定的DateTime ,这是一个巨大的问题。 (IMHO - that should throw an exception - but it doesn't.) (恕我直言 - 这应该抛出异常 - 但它没有。)

Shameless Plug:无耻的插头:

Many people have shared with me that they find this analogy extremely valuable, so I included it in my Pluralsight course, Date and Time Fundamentals .许多人与我分享,他们发现这个类比非常有价值,因此我将其包含在我的 Pluralsight 课程中,日期和时间基础 You'll find a step-by-step walkthrough of the camera analogy in the second module, "Context Matters", in the clip titled "Calendar Time vs. Instantaneous Time".您将在名为“日历时间与瞬时时间”的剪辑的第二个模块“上下文问题”中找到相机类比的分步演练。

From Microsoft:来自微软:

These uses for DateTimeOffset values are much more common than those for DateTime values. DateTimeOffset 值的这些用途比 DateTime 值的用途更常见。 As a result, DateTimeOffset should be considered the default date and time type for application development.因此,DateTimeOffset 应被视为应用程序开发的默认日期和时间类型。

source: "Choosing Between DateTime, DateTimeOffset, TimeSpan, and TimeZoneInfo" , MSDN来源: “在 DateTime、DateTimeOffset、TimeSpan 和 TimeZoneInfo 之间进行选择”MSDN

We use DateTimeOffset for nearly everything as our application deals with particular points in time (eg when a record was created/updated).当我们的应用程序处理特定的时间点(例如,创建/更新记录时)时,我们几乎对所有事情都使用DateTimeOffset As a side note, we use DATETIMEOFFSET in SQL Server 2008 as well.作为旁注,我们也在 SQL Server 2008 中使用DATETIMEOFFSET

I see DateTime as being useful when you want to deal with dates only, times only, or deal with either in a generic sense.我认为DateTime在您只想处理日期、时间或一般意义上的处理时很有用。 For example, if you have an alarm that you want to go off every day at 7 am, you could store that in a DateTime utilizing a DateTimeKind of Unspecified because you want it to go off at 7am regardless of DST.例如,如果您有一个想要每天早上 7 点响起的闹钟,您可以使用UnspecifiedDateTime将其存储在DateTimeKind ,因为您希望它在早上 7 点响起,而不考虑夏令时。 But if you want to represent the history of alarm occurrences, you would use DateTimeOffset .但是,如果要表示警报发生的历史记录,则可以使用DateTimeOffset

Use caution when using a mix of DateTimeOffset and DateTime especially when assigning and comparing between the types.混合使用DateTimeOffsetDateTime时要小心,尤其是在类型之间分配和比较时。 Also, only compare DateTime instances that are the same DateTimeKind because DateTime ignores timezone offset when comparing.此外,仅比较具有相同DateTimeKind DateTime实例,因为DateTime在比较时会忽略时区偏移。

DateTime is capable of storing only two distinct times, the local time and UTC. DateTime 只能存储两个不同的时间,本地时间和 UTC。 The Kind property indicates which. Kind属性指示哪个。

DateTimeOffset expands on this by being able to store local times from anywhere in the world. DateTimeOffset 通过能够存储来自世界任何地方的本地时间来扩展这一点。 It also stores the offset between that local time and UTC.它还存储本地时间和 UTC 之间的偏移量 Note how DateTime cannot do this unless you'd add an extra member to your class to store that UTC offset.请注意 DateTime 无法执行此操作,除非您向类添加额外成员以存储该 UTC 偏移量。 Or only ever work with UTC.或者只使用 UTC。 Which in itself is a fine idea btw.顺便说一句,这本身就是一个好主意。

There's a few places where DateTimeOffset makes sense.有几个地方DateTimeOffset有意义。 One is when you're dealing with recurring events and daylight savings time.一种是当您处理重复事件和夏令时时。 Let's say I want to set an alarm to go off at 9am every day.假设我想设置一个闹钟,每天早上 9 点响。 If I use the "store as UTC, display as local time" rule, then the alarm will be going off at a different time when daylight savings time is in effect.如果我使用“存储为 UTC,显示为当地时间”规则,那么当夏令时生效时,警报将在不同的时间响起。

There are probably others, but the above example is actually one that I've run into in the past (this was before the addition of DateTimeOffset to the BCL - my solution at the time was to explicitly store the time in the local timezone, and save the timezone information along side it: basically what DateTimeOffset does internally).可能还有其他示例,但上面的示例实际上是我过去遇到的示例(这是在将DateTimeOffset到 BCL 之前 - 我当时的解决方案是将时间显式存储在本地时区中,并且将时区信息保存在它旁边:基本上是DateTimeOffset在内部所做的)。

The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.最重要的区别是 DateTime 不存储时区信息,而 DateTimeOffset 存储。

Although DateTime distinguishes between UTC and Local, there is absolutely no explicit time zone offset associated with it.尽管 DateTime 区分了 UTC 和 Local,但绝对没有与之关联的明确时区偏移。 If you do any kind of serialization or conversion, the server's time zone is going to be used.如果您进行任何类型的序列化或转换,将使用服务器的时区。 Even if you manually create a local time by adding minutes to offset a UTC time, you can still get bit in the serialization step, because (due to lack of any explicit offset in DateTime) it will use the server's time zone offset.即使您通过添加分钟来偏移 UTC 时间手动创建本地时间,您仍然可以在序列化步骤中获得位,因为(由于 DateTime 中缺少任何显式偏移)它将使用服务器的时区偏移。

For example, if you serialize a DateTime value with Kind=Local using Json.Net and an ISO date format, you'll get a string like 2015-08-05T07:00:00-04 .例如,如果您使用 Json.Net 和 ISO 日期格式使用 Kind=Local 序列化 DateTime 值,您将获得类似2015-08-05T07:00:00-04的字符串。 Notice that last part (-04) had nothing to do with your DateTime or any offset you used to calculate it... it's just purely the server's time zone offset.请注意,最后一部分 (-04) 与您的 DateTime 或用于计算它的任何偏移量无关……它只是纯粹的服务器时区偏移量。

Meanwhile, DateTimeOffset explicitly includes the offset.同时,DateTimeOffset 显式包含偏移量。 It may not include the name of the time zone, but at least it includes the offset, and if you serialize it, you're going to get the explicitly included offset in your value instead of whatever the server's local time happens to be.它可能不包括时区的名称,但至少它包括偏移量,如果你序列化它,你将在你的值中得到明确包含的偏移量,而不是服务器的本地时间。

This piece of code from Microsoft explains everything: 微软的这段代码解释了一切:

// 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 if you don't want to read all these great answers :-)如果您不想阅读所有这些很棒的答案,请使用TLDR :-)

Explicit :显式

Using DateTimeOffset because the timezone is forced to UTC+0.使用DateTimeOffset因为时区被强制为 UTC+0。

Implicit :隐式

Using DateTime where you hope everyone sticks to the unwritten rule of the timezone always being UTC+0.使用DateTime ,您希望每个人都遵守时区始终为 UTC+0 的不成文规则。


(Side note for devs: explicit is always better than implicit !) (开发人员的旁注:显式总是比隐式更好!)

(Side side note for Java devs, C# DateTimeOffset == Java OffsetDateTime , read this: https://www.baeldung.com/java-zoneddatetime-offsetdatetime ) (Java 开发人员的旁注,C# DateTimeOffset == Java OffsetDateTime ,请阅读: https : OffsetDateTime

Currently, we have a standard way of dealing with .NET DateTime 's in a TimeZone aware way: Whenever we produce a DateTime we do it in UTC (eg using DateTime.UtcNow ), and whenever we display one, we convert back from UTC to the user's local time.目前,我们有一种标准方式来处理 .NET DateTime的 TimeZone 感知方式:每当我们生成DateTime我们都使用 UTC(例如使用DateTime.UtcNow ),并且每当我们显示时,我们从 UTC 转换回来到用户的本地时间。

That works fine, but I've been reading about DateTimeOffset and how it captures the local and UTC time in the object itself.这很好用,但我一直在阅读DateTimeOffset以及它如何在对象本身中捕获本地和 UTC 时间。 So the question is, what would be the advantages of using DateTimeOffset compared to what we have already been doing?所以问题是,与我们已经在做的事情相比,使用DateTimeOffset有什么优势?

A major difference is that DateTimeOffset can be used in conjunction with TimeZoneInfo to convert to local times in timezones other than the current one.一个主要区别是DateTimeOffset可以与TimeZoneInfo结合使用以转换为当前时区以外的本地时间。

This is useful on a server application (eg ASP.NET) that is accessed by users in different timezones.这对于由不同时区的用户访问的服务器应用程序(例如 ASP.NET)很有用。

The only negative side of DateTimeOffset I see is that Microsoft "forgot" (by design) to support it in their XmlSerializer class.我看到的 DateTimeOffset 唯一的负面影响是微软“忘记”(按设计)在他们的 XmlSerializer 类中支持它。 But it has since been added to the XmlConvert utility class.但它已被添加到 XmlConvert 实用程序类中。

XmlConvert.ToDateTimeOffset XmlConvert.ToDateTimeOffset

XmlConvert.ToString XmlConvert.ToString

I say go ahead and use DateTimeOffset and TimeZoneInfo because of all the benefits, just beware when creating entities which will or may be serialized to or from XML (all business objects then).我说继续使用 DateTimeOffset 和 TimeZoneInfo 因为所有的好处,只是在创建实体时要小心,这些实体将或可能从 XML 序列化或从 XML(然后是所有业务对象)序列化。

DateTime.Now
Fri 03 Dec 21 18:40:11周五 12 月 21 日 18:40:11

DateTimeOffset.Now
Fri 03 Dec 21 18:40:11 +02:00周五 12 月 21 日 18:40:11 +02:00

So, DateTimeOffset stores information about how the time relates to UTC, basically the time zone.因此, DateTimeOffset存储有关时间与 UTC 的关系的信息,基本上是时区。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM