日期时间与日期时间偏移 [英] DateTime vs DateTimeOffset
问题描述
目前,我们有一种标准的方式来处理 .NET DateTime
的时区感知方式:每当我们生成一个 DateTime
时,我们都会在 UTC 中进行(例如使用 DateTime.UtcNow
),每当我们显示一个时,我们就会从 UTC 转换回用户的本地时间.
这很好用,但我一直在阅读关于 DateTimeOffset
以及它如何在对象本身中捕获本地和 UTC 时间.所以问题是,与我们已经在做的事情相比,使用 DateTimeOffset
的优势是什么?
DateTimeOffset
是瞬时时间(也称为绝对时间的表示)>).我的意思是每个人都通用的时刻(不考虑 对此有一个称为 ZonedDateTime
的表示,而 .Net 基类库没有类似的东西.您需要同时存储 DateTimeOffset
和 TimeZoneInfo.Id
值.
有时,您会想要表示正在查看它的人"本地的日历时间.例如,在定义 today 的含义时.今天总是从午夜到午夜,但这些代表了瞬时时间线上几乎无限数量的重叠范围.(实际上,我们有有限数量的时区,但您可以将偏移量表示为刻度)因此在这些情况下,请确保您了解如何限制谁在问?"将问题缩小到单个时区,或根据需要将它们转换回瞬时时间.
这里有一些关于 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
,它将假定为 local.该框架基本上是在说,好吧,你让我将日历时间转换为瞬时时间,但我不知道这是从哪里来的,所以我打算使用本地日历."如果您在具有不同时区的计算机上加载未指定的DateTime
,这是一个巨大的问题.(恕我直言 - 这应该抛出异常 - 但它没有.)
无耻的插头:
许多人与我分享,他们发现这个类比非常有价值,因此我将其包含在我的 Pluralsight 课程中,日期和时间基础.您将在名为日历时间与瞬时时间"的剪辑中的第二个模块上下文很重要"中找到有关相机类比的分步演练.
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 (e.g. using DateTime.UtcNow
), and whenever we display one, we convert back from UTC to the user's local time.
That works fine, but I've been reading about DateTimeOffset
and how it captures the local and UTC time in the object itself. So the question is, what would be the advantages of using DateTimeOffset
compared to what we have already been doing?
DateTimeOffset
is a representation of instantaneous time (also known as absolute time). 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
.
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
. And .Local
is only meaningful in scenarios where you have an implied understanding of where the computer that is using the result is positioned. (For example, a user's workstation)
So then, why DateTimeOffset
instead of a 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.
So if you label your camera "Eastern Time", sometimes you are pointing from -5, and sometimes you are pointing from -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? 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
. Just be sure you don't ever confuse one calendar with another.Unspecified
should be your assumption.Local
is only useful coming fromDateTime.Now
. For example, I might getDateTime.Now
and save it in a database - but when I retrieve it, I have to assume that it isUnspecified
. 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 UTCDateTime
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 aDateTimeOffset
and aTimeZoneInfo.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.
Here are a few other little bits about DateTimeOffset
that back up this analogy, and some tips for keeping it straight:
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
and2012-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 anyDateTimeOffset
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 unspecifiedDateTime
on a computer with a different timezone. (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. 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".
这篇关于日期时间与日期时间偏移的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!