2006-01-08

AJAX × Date × Time × Time zones - best practices

Are you developing a site or application which relies on javascript, which somewhere on some page or in some view shows some time, or a date, or even a weekday to the visitor? Registration time, for instance. Or an advance notice of your next scheduled downtime? Anything at all! Do you also let visitors from other cities, and maybe even from other countries, visit your web site?

Great! Then this article is for you.

Because if you do, then you have run into the problem of time zones. Or, if you haven't, then your poor visitors have. In a typical scenario, you are in one time zone, your web server in another and the visitor is in some third place you can't even begin to imagine. Azerbadjan, perhaps. Or Tadjmbekistan. (No, I'm kidding; there is no Tadjmbekistan. ...or is there?)

Time zones are our friends, as common people; they make clock readings correspond vaguely to the height of the sun on the sky, letting us wake up almost at the same time in the morning wherever we are on the planet. Very convenient way of organizing work hours with light hours. It's not perfect, though, and in a vain attempt of doing some slight adjustments to the sun's annoying habit of drifting some over our hours, we adopted daylight savings time, DST for short, half of the year, shifting our scale by an hour to compensate. This has been widely regarded as a bad move, and has over the course of centuries in all likelihood given us much more trouble than ever the Y2K problem did. But we're used to it, and it's hard to switch back, even though we now have artificial lighting so we can perform work any time we well please anyway.

What time and day is it right now? You probably have an answer that fits your local reality well. Your web server probably has an idea of its own, too. It might even be in concert with yours; it mostly at least gets the weekday right. But on the other side of the globe, it might still be yesterday. Or already be tomorrow, from your point of view. And you bet you will have visitors from there, who find it at best bothersome that your web site insists on getting these things wrong.

So you append a "PST" to your time readings.

No, you don't. Humans hate performing time zone math. They are not even very good at it. And your visitors from Azerbadjan don't even know what it stands for. And those who do, will wonder if that is Pacific Standards Time, or if it's maybe daylight savings time over there, and in either case, how many longitudes away is PST, anyway? Then they will very likely get an off by one error in their calculation anyway, and you bet they will be pissed at that self centered... Well, let's stop there; we all get the point.

So you introduce a time zone option in your user settings.

No, you don't. Or at least you shouldn't, if this is the problem you are trying to solve. A time zone choice is good if you, for example, want your site to be able to relate time in one visitor's time zone to the time in other visitor's time zones, but for now, we just want to format times for one visitor in her own frame of reference.

Besides, time zones are complicated things, with local political rules, all unlike the rest, and only roughly corresponding with the longitude of the visitor. "Naïve" time zones, like "UTC+1" for central Europe, for instance, will be off by an hour half of the year, and real time zones, like "Europe/Stockholm", where DST rules are taken into account, is a whole lot of work and knowledge to maintain, or have your programming language / environment maintain, and always get right. And was there really no Tadjmbekistan? If there were, would the guys who wrote the functions to handle the math know about their rulesets? And did they also cater for the changes they did on becoming the People's Democratic Republic of Tadjmbekistan last year?

Unlikely.

So, what do you do?

Your visitor's browser knows what time zone it is in. It even knows what time it is. And, it's programmable. See? You probably knew all those things already; after all you are developing applications for it, in it, and the rest is history. So you let the browser do the math for you. And for your visitor.

There is an easy way, and the hard way. And as the result will be the same, I suggest you don't bother much with Javascript's meagre support for time zone arithmetics with date.getTimezoneOffset() and similar trickery, because there is a beautiful time zone called unix time, which all good programming environments handle, including javascript. What time is it in unix time, right this moment?

Click to find out.

Yes. A number. And it isn't even related to time zones; unix time is the same, everywhere; in space, too, actually. And your visitor's browser knows how to convert it to the time zone the computer was set up for. It's as trivial as passing that integer to the constructor of the javascript Date object. Multiplied by a thousand, to get it in milliseconds (which is the native time unit to javascript), rather than seconds. Similarly, if your environment provides nanosecond timestamps, divide by a thousand instead.

Generalizing wildly, I'll assume you pull most times from a MySQL database, in which case you should tell it to give you unix times using SELECT UNIX_TIMESTAMP(modified) AS modified, for instance. Do it as close to the data source as possible to have as little code trying to apply local understanding of date and time mathematics as possible, to avoid unnecessary pitfalls. Pass this integer along to the client, and format it appropriately to the visitor. I would suggest a format for dates which lists month names rather than figures, as variants on the theme X/Y/Z theme offer too many interpretations, forcing the reader to guess which variant you prefer, even if you are kind and foreseeing enough to supply four digit years.

Here, you might actually want to provide a configuration option on how the visitor prefers to read dates and time. But don't try being too clever, bundling the choice of date format with the choice of country, or interface language (as does Google Mail, who move on to downgrading functionality if you opt for a Swedish interface -- for instance, you suddenly can't choose sender addresses if you switch to the Swedish locale). Suggesting default choices based on locale choice is good, assuming what goes with what is not.

Here is the most trivial date formatting method possible to get you started or for debugging purposes, and one somewhat more sanitized version. Most of you will probably want to cook up something better; browse your options among the many available methods of the Date object for primitives. Just remember to stay clear of the legacy getYear() function; it's getFullYear() you want to use. If you use some library, it probably already has good date formatting methods that will do a good job if you pass them a Date object.
function formatDebug( timeInteger )
{
return (new Date( timeInteger )).toString();
}

function formatTime( time )
{
function zeropad( n ){ return n>9 ? n : '0'+n; }
var t = time instanceof Date ? time : new Date( time );
var Y = t.getFullYear();
var M = t.getMonth(); // month-1
var D = t.getDate();
var d = t.getDay(); // 0..6 == sun..sat
var day = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][d];
var mon = ['Jan','Feb','Mar','Apr','May','Jun',
'Jul','Aug','Sep','Oct','Nov','Dec'][M];
var h = t.getHours();
var m = t.getMinutes();
var s = t.getSeconds();
return day +' '+ D +' '+ mon +', '+ Y +', '+
zeropad(h)+':'+zeropad(m)+':'+zeropad(s);
}

As you are pioneering this field of end-user usability, you may want to state that times and dates are indeed given in the visitor's frame of reference, as people have generally come to expect to see times given in some random and hence typically fairly useless time zone. This can be seen as a not entirely bad reason for actually providing a time zone configuration option, should you want one. I would suggest defaulting it to "auto" or "local time" using the above method, though, as that is most likely what the user would want to see, anyway. This way, the configuration option actually doubles as documentation of what times the site shows, in a place a visitor is likely to look for it. To make it extra apparent that you render proper times, you might equip the page with the setting with a printout of present time, which the visitor will easily recognize as the time her computer clock shows (since they are in fact one and the same).

Similarly, if your web site does not require javascript, neither should you introduce this requirement just for handling date and times. You can do both, though, if you have the time zone configuration option doing time zone math server side, and provide the javascript version for locally rendered times. It might look like this in rendered code:
<noscript>Jan 8, 2006, 14:26</noscript>
<script type="text/javascript">
document.write(formatTime(new Date(1136726801*1000)))
</script>

...That's all we have time for today. Hopefully, you and your visitors will have all the time in the world, and get them properly in sync too. Best of luck to you all!

25 comments:

  1. Interesting thought... but why would you want to plug yet another Javascript library into every page you're sending, instead of having your programming language/framework sort out the fine details (and blame them if your Tadjmbekistan user keeps getting the wrong time)?
    And, more fundamentally: does your reader really want to see the timestamps converted to his own timezone? I for one don't. It means nothing to me that you've posted something at 02:21 local time (local for me). It means I was sleeping at the time you wrote this. On the other hand, if I see you were posting at 02:21 *your* local time, I could deduce that you were fighting against sleep, sitting in the dark of night, when you were posting. If you made a typo, I'm more likely to forgive you -- it was late at your end.

    ReplyDelete
  2. Yet another javascript library? It's one method, that formats a date. (RedHanded's take on it is even nicer, if larger.) And if your client side code already uses some library (Mochikit, Dojo, Prototype), it probably already has the method built in, too.

    The point above is not in placing blame, but to always get this to work right, and without burdening the visitor. In the (hopefully or probably rather rarely encountered in practice) case you picked, when a visitor has configured a time zone that your framework can't handle properly, nobody will think twice about why your site doesn't work; that is not their problem to ponder. Either it works, or it does not, and if it does not, the flaw is reflected back on you, not on libc, or ASP.net, or whatever your staff of programmers would nail it down to if they were alerted of the issue.

    Your more fundamental question is right on the mark -- there are lots of times when you should not format times in the visitor's frame of reference. It largely depends on what the time reading is used for, and how the information is most likely to be used by the visitor, as you conclude.

    There are three time zones at play in that example, though; mine, yours and the web server (in the odd case even a fourth database server) time zone. The naïve implementation will show you server time, which is never ever interesting. In actual cases, that is often the same time zone as that of the narrator, in which cases the flaw is less severe, as that time often has some relevance.

    Anyway, to summarize: listing time will always need reflection about what the time reading is used for, who will read it, and what it will convey to her. In many cases, more than one way of stating time can be useful; "it's midnight now where I am" and "this was posted six hours ago", can both be useful bits of information to a site visitor. "You will most likely find me online for live conversation between 10 and 13, your time" could be another, in some more forum-like setting.

    My article doesn't address when what choice (or choices) is the right one to make; that is all up to you. Many times it will make perfect sense to list multiple times, clearly marking each one with its frame of reference. But I do want to raise the issue of the options at hand, and push for using the technical means on offer for showing visitor time, when that is what we set out to do.

    Because today, many go to great lengths to get it just almost right, and typically at the cost of visitor convenience. The solution isn't very tricky, as I believe I showed above, once you have been made aware of it.

    ReplyDelete
  3. I don't get it... why did you put "Ajax" in the title of your post, if it's got nothing to do with XMLHTTPRequest? Hmmm... anyways, regarding timezone offsets, if your website is hosted on a Unix server then you can use the built-in TZ timezone and daylight saving time data. That's what I did to make this world dates and times page in PHP.

    ReplyDelete
  4. While I admit this has very much wider applicability than XMLHttpRequest, it is nevertheless an issue that typically shows up with AJAX applications, and one addressable by this solution in that context, as they still rely on Javascript availability.

    So it's more of a heads-up for the typical target audience that needs to learn this technique, than a marker for limiting the article scope.

    ReplyDelete
  5. Johan,

    I love this concept.

    I took some of the ideas in this post, as well as RedHanded's code, and put together an unobtrusive and light JavaScript class that makes this entire process fairly transparent, and maintains the semantic quality of the HTML code (I'm simply not a fan of NOSCRIPT tags, nor of inline document.write).

    The class is avaliable here: http://mikewest.org/blog/id/20.

    Thanks for the inspiration!

    ReplyDelete
  6. You're welcome. Your pick of solution is another tradeoff, dropping an ugly noscript for getting a document which instead won't validate (the custom argument name). If I were to go down that route, I would have picked the class name to carry all the data, i e class="PerfectTime 1111396060" or even class="PerfectTime:1111396060". (That would actually even be easier for you to scan for and parse, relieving you of some code baggage.)

    ReplyDelete
  7. Johan, Daniel,

    You're both right, of course. I don't particularly have a problem with non-standard attributed when they're useful, but hCalendar proves that I should have thought a bit harder before deciding on a format.

    I've updated the class to support the hCalendar format, and I'm interested in your feedback regarding my method.

    ReplyDelete
  8. First off: I would have been glad to post this comment to your blog post, had it been possible (feel free to add it yourself when you have found and fixed the problem).

    While I'm no fierce advocate against extending HTML (and its derivates) with useful semantically named attributes, or even tags, my experience with using such derived formats in practice with browsers has been that it has not been possible to get them to render your documents in standards compliance mode, which is what you want to do to maintain sanity while styling your web page.

    Last time I researched that (a few years ago, admittedly) it was not possible, even if you properly defined and linked to a DTD of your own describing the tags and attributes, inheriting the rest from i e XHTML Strict, not to end up in quirks mode.

    ReplyDelete
  9. All good ideas have limited applicability; 1) and 3) for instance, are clearly out of scope for this method, and clearly it's not the be all end all solution to time zone problems; it's a method to convert server time to client readable time.

    Does anyone claim it's more than that? If so, I would suggest enlightening them there rather than here, as people generally tend not to read lengthy comments.

    Regarding 2), I see nothing stopping a good time input widget from using the same method as here to feed a timestamp integer upstream, after having done some browser parsing of date and time in client side javascript. Again, only applicable to javascript enabled browsers and all that, but you get the idea.

    ReplyDelete
  10. I think the article is okay. But there are still times where I think it would be good to display the local user's timezone name (like PST, CEST, CET...).

    Imagine seeing these timestamps:
    2006-02-08 21:00:03 UTC+01
    2006-08-12 22:55:01 UTC+02
    Both calculated at the same time on my local machine with JavaScript... Why is ti that I have +01 and +02? Simply because of Daylight Saving Time and I didn't notice. I thought of a programming issue.

    Would it be
    2006-02-08 21:00:03 CET
    2006-08-12 22:55:01 CEST
    I wouldn't have any problem

    But as of now I haven't found any way to get my local timezone's name :-(

    ReplyDelete
  11. Unfortunately, javascript doesn't offer any exposed programming interfaces to time zones; not even the +/-HHMM ones, less still the mediocre PST/CEST/CET aliases (who occasionally are ambiguous, and in either case are not generally understandable to most humans) and the good America/Chicago, Europe/Vienna or Asia/Novosibirsk zone names.

    The downside is that you need more information than you have to be able to pick the correct zone name/alias, so you need to take the long road via user interaction to get one. :-(

    ReplyDelete
  12. I (sort of) solved it for Mozilla/Firefox with this code (milli contains the timestamp)
    var TZ= milli.toString().replace(/^.*\((.*?)\).*$/, '$1');
    if ( TZ.length > 5 ) {
    TZ= milli.getTimeZoneOffset()/60;
    TZ= 'UTC' + ( TZ < 0 ? '+' : '-' ) + zeropad2(Math.abs(TZ));
    }

    (zeropad2 is just a standard function adding a leading zero for 0-9)

    With this code I get the "mediocre" names that are available through Date.toString() in Mozilla/FireBird. In Safari they are not available and so the if-part to handle that situation.

    ReplyDelete
  13. "Wed Sep 13 2006 14:42:34 GMT+0200" is what the Win32 Firefox I write this with reports for Date.toString(), so it is apparently not pan-Gecko either.

    ReplyDelete
  14. A project I regularly work on has international viewers to overcome the timezone issue we took a slightly different approach.

    We wanted to display all times local to the user as this is in our opinion the easiest way for them to interpret them.

    The solution we came up with is a simple javascript which is called on the first visit to the site using asynchrous calls the script posts the javascript time in seconds to the server, the server does the math to find the offset between server timestamp and client timestamp, a simple php function adds/subtracts from all timestamps around the site as required.

    We also allow registered users to overide by setting their timezone in their profiles.

    I am also a fan of the timezone irrelvant approach many sites are now taking of displaying the offset time i.e. (25 Minutes Ago, 5 days Ago) although this can only be used where accuracy is not important and it is something we do alongside displaying the actual date of approach.

    ReplyDelete
  15. We ran into this exact issue for our home-grown group-ware. The programming lead wrote a very elegant ECMA-script solution to calculate time-zone differences and try to round them into a reasonable GMT offset for the purpose of storing events, tasks, notes, etc. (which are all stored in GMT).

    Another programmer on the project didn't understand the code and proceeded to delete it and add in a time-zone option to the user settings; yuck.

    Of course, while researching this, we came across Israel's DST rules - they are decided each year so as to prevent the switch from interfering with Jewish holidays (which last from sundown to sundown and the DST switch takes place at 2:00 AM).

    ReplyDelete
  16. How daylight saving time change in us will effect javascript Date behaviour.

    ReplyDelete
  17. That should be the beauty of this solution. All US residents would get times reported according to their local DST rules, before and after the switch, and you won't have to update a single line of code anywhere either.

    ReplyDelete
  18. Hello ecmacnaut, I am a novice with js and I'm having some trouble with the script to get the date/time displayed.

    I have your script functions inserted in the HEAD section and I added...

    var timeInteger = parseInt(((new Date).getTime()/1e3));

    I just can't get document.write to work. Am I missing something? Thank you in advance.

    ReplyDelete
  19. Put the document.write() where you want the output (i e not in the head tag), because that is where it will show up. Also, you can't run document.write after the page is fully loaded, because then it will replace the document.

    ReplyDelete
  20. Forget about my reference to creating a var timeIngeter. So what I did was placed formatDebug and formatTime functions in the HEAD.

    And in the BODY, I have a script with the following document.write script:

    document.write(formatTime(parseInt(((new Date).getTime()))));

    But I do not get any output. However, formatDebut(...) works. Is there something wrong with the document.write? Thank you for your response. I really appreciate it!

    ReplyDelete
  21. I also forgot to ask, is there a need for any onload functions in the BODY tag?

    ReplyDelete
  22. The likeliest issue is the typo that used to be in the definition above(!) -- if it doesn't work now, a URL to your test page would be more helpful.

    (And no, there is no need for any onload callbacks.)

    ReplyDelete
  23. Here's a test page that shows the script not working...

    http://www.spiffyd.com/index2.php

    Typo?

    ReplyDelete
  24. getFullYears should read getFullYear.

    ReplyDelete
  25. For WordPress : http://trevorcreech.com/geekery/wordpress/user-relevant-timezones

    ReplyDelete

Limited HTML (such as <b>, <i>, <a>) is supported. (All comments are moderated by me amd rel=nofollow gets added to links -- to deter and weed out monetized spam.)

I would prefer not to have to do this as much as you do. Comments straying too far off the post topic often lost due to attention dilution.

Note: Only a member of this blog may post a comment.