.NET’s TimeZone API
Handling dates properly is something that nearly every sufficiently complex software system will have to eventually take into account. Having your application parse and generate dates properly is a real problem that you as a developer must have a game plan for, and your solution must account for an entire world that handles time differently. Whether it’s to keep track of when content timestamps such as time of creation or determine how much time has passed between events, properly handling dates is fundamentally important to writing maintainable and robust software systems. In most cases, developers can rely on standard libraries available to them in their language of choice to handle these situations. Among the various APIs available to us through the .NET framework, we have a few ways to approach date handling.
There's a whole world out there
It’s important to know that not every country recognizes daylight savings time on the same date. In fact, every region of the world recognizes daylight savings time on different days. A good list of these dates can be found here. As a result, we have to be mindful of the rules of other regions and countries when writing date dependent software.
Let’s take this following bit of code as an example of something that might seem to be correct at first, but in fact is simply not robust enough to handle all the cases we may encounter. Say we want to make a DateTimeOffset in EST. Assuming the current day is May 7, 2012, we know that EST is -5 hours from UTC. Thus, we make our DateTimeOffset with the following code:
DateTimeOffset localDTO = new DateTimeOffset(DateTime.Now, new TimeSpan(0, Offset, 0, 0));
The variables here are as follows:
- localDTO – a pointer to the DateTimeOffset we’re generating
- Offset – an integer with a value of -5.
If you have this code and run it, depending on the current day, it’ll either run fine or give you the following error:
Exception type: ArgumentException
Exception message: The UTC Offset of the local dateTime parameter does not match the offset argument.
To try it out, set your system clock to before March 11th, 2012 to have it run correctly. To have it fail, set your system clock to anytime between March 11th and March 25th.
What this error tells us that we can’t generate a new instance of the DateTimeOffset class if the TimeSpan that is input doesn’t match the offset value of the DateTime parameter. From the official documentation, we can see that the DateTimeOffset constructor will throw an ArgumentException because of this following case:
dateTime.Kind equals Local and offset does not equal the offset of the system's local time zone.
That case, along with the other cases that raise exceptions in the DateTimeOffset constructor can be found here.
For situations like this, we turn to the TimeZone API.
Enter the TimeZone API
Let’s say that we want a way to generate a DateTimeOffset whose TimeZone is EST. TimeZone has a neat method TimeZone.GetUtcOffset. This method would actually solve our problem in this instance, but there is one major issue with it – it pulls the offset from the DateTime, and if we have generated a DateTime from DateTime.Now, we’ve created a dependency on the machine we’re running on to have its system clock set to EST. Yuck!
Since our code should be able to run on any machine regardless of location, we need a system independent way of getting the TimeZone. Fortunately, support for this functionality exists. Since we’d like to use this functionality in multiple spots, and because it would be nice to generate EST DateTimeOffset objects from both DateTime objects and not necessarily EST DateTimeOffset objects, we can write the following two helper methods to handle any spot in our code that has to be converted to EST while nicely abstracting this behavior, using the ultra-handy FindSystemTimeZoneById method:
public static DateTimeOffset ConvertToESTDateTimeOffset(DateTime dateTime)
{
TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
return TimeZoneInfo.ConvertTime(dateTime, est);
}
public static DateTimeOffset ConvertToESTDateTimeOffset(DateTimeOffset dateTimeOffset)
{
TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
return TimeZoneInfo.ConvertTime(dateTimeOffset, est);
}
The list of supported IDs can be found here.
Stellar, eh? Now all of the dates we have can be converted into EST times using these methods.
In the end
Take great care in how you handle dates. C# gives you, the developer, the necessary tools to handle dates properly. Make sure you take advantage of the great resources in .NET and make that code robust.