Published by breki on 07 Apr 2008
UTC Time And .NET: A Constant Hassle
I keep finding new ways to loose half a day in debugging and troubleshooting when working with timezones and UTC time in .NET Framework.
A little background into my problem: we’re trying to use UTC exclusively in our application. So, for example, whenever we ask for current time, we use DateTime.UtcNow property instead of DateTime.Now. And also we are strict on a policy of naming all variables that hold UTC time with an Utc suffix, example: currentTimeUtc. This is just in case nobody gets confused of the timezone used in a variable. This is especially useful when our application communicates with the world, since some external services work with local time only, so there needs to be a conversion. And I recommend using this policy on database columns too, for the same reason.
The latest hassle has to do with converting UTC time to and from a string. As we discovered, we naively belived that all that was necessary was to use DateTime.ToString ("u", CultureInfo.InvariantCulture) to convert the UTC timestamp into the string. Later if we wanted to convert it back into an UTC time, we simply called DateTime.Parse (timeString, CultureInfo.InvariantCulture) to get the DateTime value back.
Off course this doesn’t work. The ToString() method converts the time into "2002-12-10 22:13:50Z" format. But the Parse() method (well actually the override we used) first parses it into UTC time and then automatically converts it into the local time. So we end up with a variable marked as UTC but actually holding the local time, which is no good.
The solution (or what I hope to be the solution) is to use
DateTime.Parse (timeString, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal) override. Let me quote the MSDN documentation on the AdjustToUniversal enumeration value:
Indicates that the date and time will be returned as a Coordinated Universal Time (UTC). If the input string denoted a Local time (through a time zone specifier or AssumeLocal) then the date and time is converted from the local time zone to UTC. If the input string denoted a UTC time (through a time zone specifier or AssumeUniversal) then no conversion occurs. If the input string did not denote a Local or UTC time (no time zone specifier, and neither AssumeLocal nor AssumeUniversal was included), then no conversion occurs and the Kind of the resulting DateTime is Unspecified. Cannot be used with RoundTripKind.
Just another reminder that if you want to use UTC times in your applications, you shouldn’t assume anything. At least when the .NET Framework is concerned