71

How does the conversion to UTC from the standard DateTime format work?

More specifically, if I create a DateTime object in one time zone and then switch to another time zone and run ToUniversalTime() on it, how does it know the conversion was done correctly and that the time is still accurately represented?

Ohad Schneider
  • 36,600
  • 15
  • 168
  • 198
derGral
  • 1,836
  • 4
  • 19
  • 29

4 Answers4

82

There is no implicit timezone attached to a DateTime object. If you run ToUniversalTime() on it, it uses the timezone of the context that the code is running in.

For example, if I create a DateTime from the epoch of 1/1/1970, it gives me the same DateTime object no matter where in the world I am.

If I run ToUniversalTime() on it when I'm running the code in Greenwich, then I get the same time. If I do it while I live in Vancouver, then I get an offset DateTime object of -8 hours.

This is why it's important to store time related information in your database as UTC times when you need to do any kind of date conversion or localization. Consider if your codebase got moved to a server facility in another timezone ;)

Edit: note from Joel's answer - DateTime objects by default are typed as DateTimeKind.Local. If you parse a date and set it as DateTimeKind.Utc, then ToUniversalTime() performs no conversion.

And here's an article on "Best Practices Coding with Date Times", and an article on Converting DateTimes with .Net.

womp
  • 115,835
  • 26
  • 236
  • 269
  • 1
    No. It doesn't care about the current time. It cares about the timezone of the local system. – Joel Coehoorn Jul 29 '09 at 16:17
  • 15
    You don't want to *always* store time-related information in UTC. It entirely depends on what that information really is. Instants in time can usually be represented in UTC, but not everything's an instant. For example, I'm on the 17:18 train from Paddington. It makes sense to store that as "17:18 Europe/London" rather than "16:18 UTC" - because when the clocks go back, the *local* time stays the same. – Jon Skeet Jul 29 '09 at 16:57
  • can i use it in a linq query? – Armance Sep 16 '11 at 15:21
  • 6
    "DateTime objects by default are typed as DateTimeKind.Local". The default is Unspecified (http://msdn.microsoft.com/en-us/library/system.datetime.kind(v=vs.95).aspx) – Vladimir Dorokhov Mar 04 '13 at 11:42
  • That article on "Best practices" was an eyeopener for me. I'm pretty sure there's some new info in there for a lot of devs! – Jowen Nov 12 '14 at 09:35
  • This bit saved me: Timezone.CurrentTimeZone.IsDaylightSavingTime(DateTimeInstance) – Adrian K Jun 23 '17 at 21:08
  • Also note that calling ToUniversalTime() again on a datetime which was returned by ToUniversalTime(), will not return a different datetime. It's smart to figure out that's already in UTC. – Ε Г И І И О Feb 27 '19 at 06:10
  • Is there any differnce between DateTimeOffset.Now.ToUniversalTime() & DateTimeOffset.UtcDateTime – Arjun Menon Mar 27 '19 at 00:24
37

Firstly, it checks whether the Kind of the DateTime is known to be UTC already. If so, it returns the same value.

Otherwise, it's assumed to be a local time - that's local to the computer it's running on, and in particular in the time zone that the computer was using when some private property was first lazily initialized. That means if you change the time zone after your application was started, there's a good chance it will still be using the old one.

The time zone contains enough information to convert a local time to a UTC time or vice versa, although there are times that that's ambiguous or invalid. (There are local times which occur twice, and local times which never occur due to daylight saving time.) The rules for handling these cases are specified in the documentation:

If the date and time instance value is an ambiguous time, this method assumes that it is a standard time. (An ambiguous time is one that can map either to a standard time or to a daylight saving time in the local time zone) If the date and time instance value is an invalid time, this method simply subtracts the local time from the local time zone's UTC offset to return UTC. (An invalid time is one that does not exist because of the application of daylight saving time adjustment rules.)

The returned value will have a Kind of DateTimeKind.Utc, so if you call ToUniveralTime on that it won't apply the offset again. (This is a vast improvement over .NET 1.1!)

If you want a non-local time zone, you should use TimeZoneInfo which was introduced in .NET 3.5 (there are hacky solutions for earlier versions, but they're not nice). To represent an instant in time, you should consider using DateTimeOffset which was introduced in .NET 2.0SP1, .NET3.0SP1 and .NET 3.5. However, that still doesn't have an actual time zone associated with it - just an offset from UTC. That means you don't know what local time will be one hour later, for example - the DST rules can vary between time zones which happened to use the same offset for that particular instant. TimeZoneInfo is designed to take historical and future rules into account, as opposed to TimeZone which is somewhat simplistic.

Basically the support in .NET 3.5 is a lot better than it was, but still leaves something to be desired for proper calendar arithmetic. Anyone fancy porting Joda Time to .NET? ;)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Why wouldn't .NET always store dates in UTC and avoid the issue altogether? Or does that warrant another thread? – derGral Jul 29 '09 at 16:32
  • 2
    You don't always want a UTC time. Date/time issues are quite complex, and there *is* no "one size fits all" rule. Always using UTC is fine for timestamps, but it's good in various other situations. – Jon Skeet Jul 29 '09 at 16:37
  • Thanks for that second paragraph. I got some code running to test things in different timezones but it doesn't work because of the lazy initialization. :( – mao47 Apr 26 '16 at 17:12
7

What @womp said, with the addition that it checks the DateTime's Kind property to see if it might already be a UTC date.

Community
  • 1
  • 1
Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • Good point. If the type isn't set explicitly, it assumes a local time, and then calling ToUniversalTime() sets the type to universal on the new date. – womp Jul 29 '09 at 16:18
  • It's worth pointing out SQL Server doesn't store the Kind (http://stackoverflow.com/questions/823313/linq-to-sql-datetime-values-are-local-kind-unspecified-how-do-i-make-it-utc) so if you store a UTC date on a US server, it will be assumed to be local – Chris S Apr 24 '13 at 21:55
4

DateTime.ToUniversalTime removes the timezone offset of the local timezone to normalize a DateTime to UTC. If you then use DateTime.ToLocalTime on the normalized value in another timezone, the timezone offset of that timezone will be added to the normalized value for correct representation in that timezone.

Michael A. McCloskey
  • 2,391
  • 16
  • 19