Working with Dates in any programming language is normally a pretty straightforward task. You save a date in the database and get on your merry way, and display it whenever the need arrives. If you were looking to save a record with the current date let's say, in C# you would do something like the following:
// [C#]
DateTime theTime = DateTime.Now(); // gets the current time and date
DateTime otherTime = DateTime.Today(); // gets the current date starting at midnight
And using a SQL Server database, you would set the appropriate column to the datetime data type.
In the Column Settings tab in SQL Server you would set the default value to the GetDate() pre-defined function and any new rows added with default that field to the current database date and time.
That's how you would normally work with dates anyhow. Sometimes, however, that isn't enough.
DateTime.Now() will return the current time that the server is currently set to. The same goes for SQL Server's GetDate() function. Sometimes that's not really a big deal. For example, news publish times, or comment dates. So what if they're off by a few hours to some users.
Other times, they could be more important values, like a sale's order time and date or activity tracking on your website. Think of applications such as UPS package tracking for example, where an item is moving across timezones in real time. One minute it could be in New York, and the next somewhere overseas.
So What Is UTC
UTC (or Coordinated Universal Time for long) is the time standard that the whole world uses to regulate its clocks. Sounds pretty awesome, I know. Parts of the world are split into their own time zones, for business, commercial, and legal reasons and each timezone is an offset of the UTC time. For example, I live in California, which uses the Pacific Timezone (PST) which is -8 hours offset from UTC.
You might also need to account for Daily Savings Time, if applicable. Some states do not observe it. Most of Arizona doesn't for example, except for the Navajo Nation. For the sake of brevity and my sanity, I will just cover the basics on how to handle UTC. Just remember that "everything that happens now, is happening now". So time isn't as simple as saving "my" current time and showing it to someone in the world, unfortunately.
I need to save "my" time and show it to you in your time.
The Problem
Unless you're on Ubuntu, you can't set the server time to be represented in UTC, without some nifty tweaks to the Windows Registry. And let's assume for this scenario that we don't have access to the registry.
Let's say I have a blog (which...I do) in which I write posts, and each post is given a publish date. My shared hosting server is in New York and I have a reader in Wyoming.
ME
Pacific Time Zone (UTC-08:00)
Published at 8pm. (I wish)
|
READER
Mountain Time Zone (UTC-07:00)
Sees it at 9pm
|
SERVER
Eastern Time Zone (UTC-05:00)
Saved at 11pm
|
a picture is worth a thousand words :]
Stored using your run of the mill GetDate() function, my reader and I would see that my post was published at 11pm (New York Time) even though it is 8pm for me and 9am for them. And if we store it in UTC time, we'd both see that it was published at 4am, since our offset is -8 hours. So how do we display to everyone the correct data based on their locations in the world?
Solution
The answer is to always save the date in UTC format and worry about the display logic afterward. By knowing the UTC time, we can always work our way to the correctly formatted time for anyone in the world. Both SQL Server and .NET have functions for just this type of situation:
//[C#]
//Any of these functions will return you the UTC time
DateTime time = DateTime.Now.ToUniversalTime(); // sets a DateTime object to the current UTC time
DateTime time2 = DateTime.UtcNow; // same as above
DateTime time3 = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now); // problem with this one is that it will use the //local timezone of the server to calculate utc
Lucky for us SQL Server has the function "GetUTCDate()" that returns the current UTC time and the DateTime object in C# does the same.
Which takes care of our needs, server side. Client side we are still going to need the user's timezone offset in order to be able to work with it. We can't do this from the server, as it doesn't have that information available to it. A few approaches on this one are as follows:
1. Ask the users for their timezone
Many large websites take this approach. And it's probably the simplest. Along with the user's account, you'd save their timezone during the registration process. You might need to have a timezone database table, with the offset values, for lookup.
Then you can easily work with all the data you have by offsetting the stored UTC dates on the server itself. Or you can iterate through the Timezone's using the following method:
// [c#]
ReadOnlyCollection<timezoneinfo> tz;
tz = TimeZoneInfo.GetSystemTimeZones(); // returns all timezones on the current local system
for (int i = 0; i < tz.Count; i++)
{
string name = tz[i].StandardName;
int hours = tz[i].BaseUtcOffset.Hours;
int minutes = tz[i].BaseUtcOffset.Minutes;
// some timezones do in fact have minutes associated with them
// India for example is a UTC offset of 5:45
}
Since we have the stored timezone's for users, we can simply add the appropriate offsets whenever we need the local DateTime. Also note that some offsets do in fact use both hours and minutes, so it would be a good idea to store the offsets in terms of total minutes rather than in hours.
function DateTime getLocal(hours, minutes)
{
DateTime local = DateTime.Now.ToUniversalTime().AddHours(hours);
local.AddMinutes(minutes);
return local;
}
2. Grab the clients offset using javascript
If we are not inclined to bother users to enter their timezone info, then luckily we can grab it from the browser using javascripts GetTimezoneOffset() function. This does have its challenges however if we want to use that date on the server. We'll have to do some Ajax work in order to send it back to the server and to bind it to that particular user's session.
// [js]
var d = new Date()
var n = d.getTimezoneOffset();
Note that this will return the offset in terms of total offset minutes.
3. Display the date in UTC
And lastly, because this is really just for display purposes, we don't, in fact, need to do anything with the offset on the server. We can simply just display to the user the correct time format and be done with it. We can do that by injecting some JavaScript into our page.
// [c#]
// why not inject some js onto the page
public static string GetLocalDate(string strDate)
{
string strResult = string.Format("<script>document.write(new Date('{0:MMMM d, yyyy hh:mm:ss tt} UTC').toLocaleString());</script>", strDate);
return strResult;
}
Wherever in your code, you display the date, you will want to replace it with this script. The conversion is handled directly in JavaScript give the appropriate UTC date, as it will render in the local time for the user.
Wherever you have dates displayed on your page, just pass them through this function first in order to convert them to the snippet of JavaScript that does the conversion.
In closing
If you don't have to worry about showing time's in the exact user's location, then you can get by without all of this extra work. And in fact, I ran my blog for years before I decided to worry about timezone's and proper times.
But in my professional work, I typically don't have that freedom to ignore such things. And this is one of the approaches that I have been using for a few years now as well.
So I hope it helps you out in your time conversion challenges.