Helpful DateTime extension methods for dealing with Time Zones
This post is inspired by Scott Mitchell’s 5 Helpful DateTime Extension Methods. I built a couple extension methods while adding time zone support to our application. Since time zone math is never fun, I figure someone else may benefit from these.
ToStartOfTimeZonesDayInUtc
The name is clunky because its really hard to describe succinctly. Given a time (in UTC) and a time zone, I want to know the time (in UTC) that the day started in that time zone. This is useful, for example, when you want to display a list of events, grouped by day, in the user’s time zone. If an event happened at 3:05am UTC on February 4th, it should be grouped in Feburary 4th when displayed to a user in Western European Time, but grouped in February 3rd for a user in Central Time (US).
/// <summary> /// The time in UTC that the day started in the given time zone for a specific UTC time /// </summary> /// <param name="utcTime">A point in time, specified in UTC</param> /// <param name="timezone">The time zone that determines when the day started</param> /// <returns></returns> public static DateTime ToStartOfTimeZonesDayInUtc(this DateTime utcTime, TimeZoneInfo timezone) { var startOfDayInGivenTimeZone = utcTime.ToLocalTime(timezone).Date; return startOfDayInGivenTimeZone.ToUniversalTime(timezone); }
StartOfTodayInUtc
A convenience method for determining the day, right now, in a given time zone.
/// <summary> /// The time in UTC that the current day started in the given time zone /// </summary> /// <param name="timezone">The time zone that determines when the day started</param> /// <returns></returns> public static DateTime StartOfTodayInUtc(this TimeZoneInfo timezone) { return DateTime.UtcNow.ToStartOfTimeZonesDayInUtc(timezone); }
ToLocalTime and ToUniversalTime
These are pretty straightforward but I’ll include them because the methods above depend on them. DateTime already has ToLocalTime and ToUniversalTime methods, but they are flawed because they do not let you specify a time zone. I’m not sure why they were not added as overloads when the TimeZoneInfo type was introduced.
/// <summary> /// Converts a UTC time to a time in the given time zone /// </summary> /// <param name="targetTimeZone">The time zone to convert to</param> /// <param name="utcTime">The UTC time</param> /// <returns></returns> public static DateTime ToLocalTime(this DateTime utcTime, TimeZoneInfo targetTimeZone) { return TimeZoneInfo.ConvertTimeFromUtc(utcTime, targetTimeZone); } /// <summary> /// Converts a local time to a UTC time /// </summary> /// <param name="sourceTimeZone">The time zone of the local time</param> /// <param name="localTime">The local time</param> /// <returns></returns> public static DateTime ToUniversalTime(this DateTime localTime, TimeZoneInfo sourceTimeZone) { return TimeZoneInfo.ConvertTimeToUtc(localTime, sourceTimeZone); }