I'm just starting to think properly about rolling out a webapp that will need to do things to users at the start of their day, say 6am. Also at the end of their days.
Everywhere I've been reading about people saying a lot just to use .ToUniversalTime to store the time in UTC, but when I tried this (as I suspected) it didn't work, and it just moved the time about by an hour (I'm in the UK, so I thought this was to do with some offset from GMT to UTC, although that doesn't make sense to me, as day light saving should be off at the moment).
I have a field in the db that stores the users timezone, and so when I started using ConvertTimeToUtc and fromUtc , it started doing what I was expecting it to do. Although again I'm not sure if I have to build in some logic myself to do daylight saving conversions, or it should do it for me.
I am mainly wondering why everyone was talking about .ToUniversalTime, as it really didn't seem to help me, and I couldn't understand how it could possibly know how much to offset the time to shift it to UTC, whereas the second way made sense.
Could someone explain how each methods could be useful?
There actually is a difference between these two.
In .NET 3.5 and lower, Datetime.ToUniversalTime
is implemented as:
public DateTime ToUniversalTime() {
return TimeZone.CurrentTimeZone.ToUniversalTime(this);
}
Because it used the TimeZone
class, it suffered the same problems mentioned in the MSDN docs:
The
TimeZone
class supports only a single daylight saving time adjustment rule for the local time zone. As a result, theTimeZone
class can accurately report daylight saving time information or convert between UTC and local time only for the period in which the latest adjustment rule is in effect. In contrast, theTimeZoneInfo
class supports multiple adjustment rules, which makes it possible to work with historic time zone data.
In .NET 4.0 and higher, Datetime.ToUniversalTime
is implemented as:
public DateTime ToUniversalTime() {
return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
}
This fixes the problem about not supporting historical adjustment rules, but due to the NoThrowOnInvalidTime
flag, it is not the same as just calling TimeZoneInfo.ConvertimeToUtc
.
The method it calls is an internal overload of ConvertTimeToUtc
that takes a TimeZoneInfoOptions
flag. The public version of the method uses TimeZoneInfoOptions.None
, while this one uses TimeZoneInfoOptions.NoThrowOnInvalidTime
.
The difference can be illustrated as follow. With the time zone set to US Pacific Time:
DateTime dt = new DateTime(2015, 3, 8, 2, 0, 0, DateTimeKind.Local);
DateTime utc = dt.ToUniversalTime();
Console.WriteLine(utc); // "3/8/2015 10:00:00 AM"
Vs:
DateTime dt = new DateTime(2015, 3, 8, 2, 0, 0, DateTimeKind.Local);
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(dt); // throws exception!
Console.WriteLine(utc);
Since on this date, in this time zone, the time skips from 1:59:59 to 3:00:00, supplying a local time of 2:00:00 isn't valid. The correct thing to do is to throw an exception (as in the second case). However since the existing contract of DateTime.ToUniversalTime
from earlier framework versions did not allow this, the framework chooses to return a value instead of throwing.
The value it chooses is calculated based on using the standard time offset, as if the DST transition didn't occur.
If you run the code on a machine in a different timezone, are your calculations still going to work? This is the reason people store and treat all DateTimes as UTC - it removes any ambiguity. You wouldn't need to store the user's timezone. Any machine, anywhere, can pull a date from the database and convert it to and from local times with ease.
If you're storing times in some other timezone, then you have to pull it out, calculate the offset to the desired timezone, including factoring in daylight savings times and international dateline considerations. In your case, you're also storing extra unnecessary information.
Edit: I was wrong. See Matt Johnson's answer for an explanation of the subtle differences between implementations of DateTime.ToUniversalTime
and TimeZoneInfo.ConvertTimeToUtc
.
For anyone else that has the specific question phrased in the title: What is the difference between DateTime.ToUniversalTime and TimeZoneInfo.ConvertTimeToUtc?
The answer is: There is no difference.
Using JustDecompile to inspect the implementation of DateTime.ToUniversalTime
in .NET 4.5, we see that it uses TimeZoneInfo.ConvertTimeToUtc
directly:
public DateTime ToUniversalTime()
{
return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With