Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between DateTime.ToUniversalTime and TimeZoneInfo.ConvertTimeToUtc

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?

like image 922
Chris Barry Avatar asked Nov 10 '09 00:11

Chris Barry


3 Answers

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, the TimeZone 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, the TimeZoneInfo 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.

like image 100
Matt Johnson-Pint Avatar answered Nov 13 '22 11:11

Matt Johnson-Pint


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.

like image 33
womp Avatar answered Nov 13 '22 11:11

womp


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);
    }
like image 37
Clinton Avatar answered Nov 13 '22 11:11

Clinton