That are the most important words I remembered from my journey into making our application ready for users all over the world.
The question was simple: we now save datetimes in local format, but when we plan on implementing our application outside our own country, we have to change that.
So after a short discussion we decided to follow the best practice: save the dates in UTC format.
So when data is imported or entered via the screen, check where it comes from, calculate the right UTC time from it, and then save the data to the database.
And also: when a user asks for data for august 15th, first determine where the user is located, calculate the right UTC time for ‘his’ august 15th, and then get the data from the database with the UTC time as selection criteria.
So where to start?
First I’ve checked the DateTime class, and it’s got a ToLocalTime() method and a ToUniversalTime() method.
That looked promising , but it doesn’t take the users time zone into account.
It doesn’t allow me to ask: what is the UTC time for a user in a time zone which has a UTC offset of -9 (when you live in a place with UTC – 9, your local time is the UTC time (which is located in England) minus 9 hours. So when it’s 14.00 hours in the UTC area / England, you’re probably sleeping at 5 AM. I’ll come back at the summertime / wintertime differences later)
The better solution is the .Net TimeZoneInfo class.
This is designed for the kind of questions I want to ask.
So, how do I determine what the UTC time is for ‘his August 15th, 0.00 AM’?
First I want to know in which time zone he is in. Does he live in -9 or +9, that makes quite a difference. We decided to store that as a user preference in his profile.
For data import, we had to know the time zone the data supplier uses for our data imports.
Then, we have to know if the user’s local time was in Daylight Saving Time (DST) or not. Not all countries use DST, but for every different time zone there are different ones if there are DST and none-DST countries in it.
So there are multiple +1 time zones, some which have DST and some have not.
So how to start with your TimeZoneInfo?
First create one with the TimeZoneInfo.FindSystemTimeZoneById("W. Europe Standard Time") method. The name of the timezones can be retrieved like this:
TimeZoneInfo.GetSystemTimeZones() This returns a collection of TimeZoneInfo objects with an id property you can use.
Then you can ask whether this timezone supports / has DST:
timeZone.SupportsDaylightSavingTime() (this time timeZone is an object instantiated via TimeZoneInfo.FindSystemTimeZoneById())
So, then we can check if August 15th, 0.00 AM exists (when going from wintertime to summertime, the clock goes from 1.59.59 to 3.00.00, so on that day a time of 02.15 is not valid):
If it is a valid date, we have to check if it is an ambiguous date (when going from summertime to wintertime, the clock goes from 02.59.59 to 02.00.00, and then we have the hour from 02.00.00 to 02.59.59 twice).
Because if you get an ambiguous time, we have to know: was it the ‘first’ 02.15 or the ‘second’ 02.15.
In the latter case we are in wintertime and we don’t have to worry about the summertime extra hour.
When it is not an ambiguous time, we can ask if he is in summertime or not:
Then we have to know what the base offset for this time zone is timeZoneInfo.BaseUtcOffset.Hours
Let’s say he is in -9: the UTC time is 0.00.00 + 9 hours = 0.09.00
Let’s say he is in summertime so he is actually in (-9 + 1) = -8.
So the UTC time is 0.00.00 + 8 hours = 0.08.00
Finally we got the UTC date, and we can get the data out of the database with this new UTC date.
The other way around (you have a UTC time, and want to know what time it is in ‘his’ time zone)?
It’s simpler, just use