8

I have an ASP.NET application and a UTC time stamp on the server. I want to display the time stamp to the user in the right time zone and using local date/time format.

E.g. Jan 2, 2012 14:00:00 UTC should show as 1/2/2012 9:00 AM to a user in New York, United States (UTC -0500) and as 02/01/2012 14:00 to a user in London, UK.

This seemingly simple task proved to be surprisingly difficult. MSDN has an article with the same title, but it talks about parsing user input rather than displaying server-side data, so it does not fully apply.

Time zone offset is easily determined on the client via JavaScript

offset = new Date().getTimezoneOffset();,

but JavaScript provides very poor support for date/time formatting. All you get is toLocaleString() method, which results in ugly long string such as Monday, January 02, 2012 9:00:00 AM. There is no provision for shorter formats, so on the client we're stuck with good time zone info and bad date/time format capabilities.

The situation is exactly opposite on the server. We can leverage Accept-Language HTTP header to get the user locale (not the best choice, but may be good enough), and then tap into the .NET database of known locales, so our code goes along the lines of

CultureInfo userCulture = new CultureInfo(Request.UserLanguages[0]); plus some error handling.

But then we are stuck on the time zone problem. Yes, one can get it via JavaScript and then pass back as a cookie or as postback data, but what if we need to display dates on the very first page of the application? One may argue that the first page is always the login page, but it is not so when user login info is persisted between sessions ("remember me" option). The solution to that could be to save time zone offset as part of user profile, but then it may easily become stale (getting on and off daylight savings time between sessions).

Is there a comprehensive solution to this problem that performs well and does not require writing tons of code? I am very curious, please advise.

Ivan Krivyakov
  • 1,898
  • 1
  • 17
  • 27

1 Answers1

5

You should pass the time string to the client in a standard form, such as ISO8601:

var timeString = '2012-01-02T16:00:00Z';

Some browsers will correctly parse ISO8601 strings and some wont, so parse it manually to be sure. That is pretty simple—create a local date object then set the UTC date and time:

function localDateFromUTC(s) {

  var x = s.split(/[-\s:tz]/i);
  var d = new Date();

  d.setUTCFullYear(x[0], x[1], x[2]);
  d.setUTCHours(x[3], x[4], x[5]);
  return d;
}

var s = '2012-01-02T16:00:00Z';
var d = localDateFromUTC(s);
alert(d); // Shows local date and time for the provided UTC date and time

If you want a specific output, you need to format it manually, e.g.

function formatDate(d) {
  var days = ['Sunday','Monday','Tuesday','Wednesday',
              'Thursday','Friday','Saturday'];
  var months = ['January','February','March','April','May','June','July',
                'August','September','October','November','December'];

  return days[d.getDay()] + ', ' + d.getDate() + ' ' + 
              months[d.getMonth()] + ', ' + d.getFullYear();
} 

You can try using toLocaleString() but results vary greatly across browsers, most seem to ignore local settings anyway.

RobG
  • 142,382
  • 31
  • 172
  • 209
  • _If you want a specific output_ I like the idea of passing date like this, but the problem of determining local date/time format remains unsolved. How do you know what to use: `'01/02/2012 2:00 PM'`, `'2012-01-02 14.00'`, or, perhaps `'02.01.2012 14:00'`? – Ivan Krivyakov Jan 03 '12 at 06:33
  • The usual solution is to write it in an unambiguous format, like "2 February, 2012", which should be clearly understood by anyone, even if they prefer their dates in some other format. If browsers had a decent *toLocaleString* you could use that, but they don't. – RobG Jan 03 '12 at 07:20
  • 1
    I tried to reverse engineer how gmail handles time zones. It is quite clever indeed. They do date formatting and time zone conversion on the server. On the client they try to determine the dates of daylight savings switches (if any) by doing binary search between Jan 1, 2010 and June 30, 2010 and then June 30 and Jan 1, 2011. They encode that information, along with current time zone offset, in a string and (watch my hands!) do `(new Image).src=encoded_string`! This way they can pass the data to the server before the main page finished rendering, without cookies OR postback. – Ivan Krivyakov Jan 03 '12 at 14:35
  • That technique was how AJAX was done without XMLHttpRequest, you can use any element with a src attribute (e.g. iframe, script). But I still don't see how it does local formatting. Timezone offset and daylight saving can be ignored if everything is done in UTC and conversion to local date/time only done for presentation - the date object handles that. – RobG Jan 04 '12 at 00:58
  • It does formatting on the server. To make it local, it has "display language" in the settings with options like English (US), English (UK), French, etc. It changes date format accordingly. I think it needs daylight savings info to correctly show old e-mails (but I am not sure it works 100% right, as it looks only for 2010). When it comes to actually showing the dates and times, gmail does not use JavaScript at all, so no date object. – Ivan Krivyakov Jan 04 '12 at 04:08
  • Google may be doing that because it's getting date and time from the client, so it has to work out the time zone. There are really only two common formats for writing dates: day/month/year (95% of the world population) and month/day/year (5%). Give users an option of which one they want and store it as a preference. You might like to provide year/month/day, which is common in parts of Europe and handy because it sorts nicely. – RobG Jan 04 '12 at 06:35