DateTime vs DateTimeOffset
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".
Difference between 'DateTime' and 'DateTimeOffset'
DateTimeOffset Represents a point in time, typically expressed as a date and time of day, relative to Coordinated Universal Time (UTC) it provides a greater degree of time zone awareness than the DateTime structure. See it here- http://msdn.microsoft.com/en-us/library/bb546101.aspx.
datetimeoffset vs datetime2 for UTC on SQL Server
The datetimeoffset
data type will allow comparison between different offsets of the same time. e.g.:
SELECT 'equal'
WHERE
CAST('2021-02-12 15:48:11.0677934 -01:00' AS datetimeoffset) = CAST('2021-02-12 16:48:11.0677934 +00:00' AS datetimeoffset).
If you are storing only UTC values (where the offset is always zero), you can save storage space with datetime2
. datetimeoffset
requires 10 bytes of storage whereas datetime
needs 8 bytes for precision 5 or greater, 7 bytes for precision 3-4, and 6 bytes for precision 2 or less.
Difference between System.DateTime and System.DateTimeOffset
A DateTime value defines a particular date and time, it includes a Kind property that provides limited information about the time zone to which that date and time belongs.
The DateTimeOffset structure represents a date and time value, together with an offset that indicates how much that value differs from UTC. Thus, the value always unambiguously identifies a single point in time.
DateTimeOffset should be considered the default date and time type for application development as the uses for DateTimeOffset values are much more common than those for DateTime values.
See more info, code examples at:
http://msdn.microsoft.com/en-us/library/bb384267.aspx
Convert DateTime to DateTimeOffset taking into account SummerTime
You're converting from what I'd call a "local date/time" to a UTC value. Some local date/time values are skipped and some are repeated, due to daylight saving transitions (and other time zone changes).
In the situation you're showing, every local time between 2am inclusive and 3am exclusive happened twice, because at 3am (the first time) the clocks went back to 2am - this line:
10/25/2020 02:00:00 AM +01:00 = 01:00Z
... shows the second mapping of 2am. But at 2020-10-25T00:00:00Z the local time was also 2am due to the clocks going back. Your conversion is ambiguous, in other words.
The TimeZoneInfo
documentation states:
If
dateTime
is ambiguous, or if the converted time is ambiguous, this method interprets the ambiguous time as a standard time.
Here, "standard time" is the second occurrence of any ambiguous time (because it's a transition from daylight time to standard time).
Fundamentally, if you only have local values then you have incomplete information. If you want to treat ambiguous values as daylight time instead of standard time (or flag them up as ambiguous), you can always use TimeZoneInfo.IsAmbiguousTime(DateTime)
to detect that.
When Would You Prefer DateTime Over DateTimeOffset
Sometimes you really just want to represent a "local" (timezone unaware) date and time rather than an instant in time. To be honest it's more often useful to represent just a time - e.g. "wake me up at 8am, regardless of timezone" - but date and time could be useful too.
I agree that for the vast majority of cases, DateTimeOffset
is a better fit. It does strike me as odd that there isn't a DateTimeTimeZone
struct which has both the instant and its timezone though... an offset doesn't actually give you all the information you need. (For instance, given a DateTimeOffset
, you don't know what the time will be 24 hours later, because you don't know when DST might kick in.)
If you want that kind of structure, I have a very crude implementation in another answer. I'm sure it could be improved very easily :)
C# DateTimeOffset LocalDateTime vs ToLocalTime
The docs state the following:
LocalDateTime
:
Gets a DateTime value that represents the local date and time of the current DateTimeOffset object.
ToLocalTime()
:
Converts the current DateTimeOffset object to a DateTimeOffset object that represents the local time.
Looking at the code, we can see that the LocalDateTime
property gets the value it represents as a UTC DateTime
object and then converts it using the DateTime
objects's ToLocalTime()
method.
public DateTime UtcDateTime {
[Pure]
get {
Contract.Ensures(Contract.Result<DateTime>().Kind == DateTimeKind.Utc);
return DateTime.SpecifyKind(m_dateTime, DateTimeKind.Utc);
}
}
public DateTime LocalDateTime {
[Pure]
get {
Contract.Ensures(Contract.Result<DateTime>().Kind == DateTimeKind.Local);
return UtcDateTime.ToLocalTime();
}
}
And we can see that .ToLocalTime()
simply uses the same UtcDateTime
property to retrieve the UTC DateTime
object, again calls ToLocalTime()
and then wraps it in a new DateTimeOffset
object:
public DateTimeOffset ToLocalTime() {
return ToLocalTime(false);
}
internal DateTimeOffset ToLocalTime(bool throwOnOverflow)
{
return new DateTimeOffset(UtcDateTime.ToLocalTime(throwOnOverflow));
}
You can test the results with this code:
DateTimeOffset dto = new DateTimeOffset(2020, 10, 01, 9, 0, 0, 0, TimeSpan.FromHours(9));
Console.WriteLine(dto.ToString(System.Globalization.CultureInfo.InvariantCulture)); // 10/01/2020 09:00:00 +09:00
DateTime localTimeDt = dto.LocalDateTime;
Console.WriteLine(localTimeDt.ToString(System.Globalization.CultureInfo.InvariantCulture)); // 10/01/2020 02:00:00
DateTimeOffset localTimeDto = dto.ToLocalTime();
Console.WriteLine(localTimeDto.ToString(System.Globalization.CultureInfo.InvariantCulture)); // 10/01/2020 02:00:00 +02:00
Naturally, the DateTimeOffset
retains the +2:00 of the system time of Rextester since that's the timezone it's based in.
Link to test
Difference in comparing DateTime and parsed DateTimeOffset ticks
In this case, it seems the problem is the format of the DateTime:
"yyyy-MM-ddThh:mm:ss.fffZ" // lower hh
whereas, it should be:
"yyyy-MM-ddTHH:mm:ss.fffZ" // upper HH
Using 12 hour (hh
) format rather than 24 hour (HH
) format would cause a 12 hour difference 50% of the time!
As Hans mentions in the comments, you should ensure you're using UTC to compare the DateTime values. DateTime.Parse
by default sets the DateTime kind to DateTimeKind.Local
. Is the client sending a UTC DateTime?
Update the client timestamp so it sends UTC DateTime with ToUniversalTime
:
var clientTimestamp = DateTimeOffset.Now.ToUniversalTime();
Instead of DateTime.Parse
consider DateTime.ParseExact
with AdjustToUniversal
:
// convert the date to UTC before saving to database
var utcServerDate = DateTime.ParseExact(jsonDateTime, "yyyy-MM-ddTHH:mm:ss.fffZ",
CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
And when comparing the DateTimeOffset.Now
value use UtcTicks
:
var nowDateTimeOffset = DateTimeOffset.Now.AddMinutes(-30).UtcTicks;
Related Topics
The Request Was Aborted: Could Not Create Ssl/Tls Secure Channel
Processstartinfo Hanging on "Waitforexit" - Why
String Representation of an Enum
Using Xpath With Default Namespace in C#
How to Cancel Task Await After a Timeout Period
Increase Upload File Size in ASP.NET Core
Html Agility Pack - Parsing Tables
How to Enable Cors in ASP.NET Core
How to Open a Chrome Profile Through --User-Data-Dir Argument of Selenium
Is There a Constraint That Restricts My Generic Method to Numeric Types
How to Remove All Event Handlers from an Event
Direct Casting VS 'As' Operator
How to Enable External Request in Iis Express
In C#, Why Can't a List≪String≫ Object Be Stored in a List≪Object≫ Variable
Join/Where With Linq and Lambda
Benefits of Using the Conditional : (Ternary) Operator
How to Find the Text Within a Div in the Source of a Web Page Using C#
Best Practice to Call Configureawait For All Server-Side Code