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? ;)