Simple Date and Time Localization With JavaScript

One of the many challenges we’ve encountered during our work on SitePoint Contests and Marketplace is deciding the best way to present dates and times to our users.

This sounds simple, but there’s quite a few considerations that we need to keep in mind.

Easily Readable by Humans

“Started 2 hours ago” and “Ends in 2 days” are much easier to understand than “Started Mon, 4 June 2007, 10:04am +1000″ and “Ends Wed, 6 June 2007, 9:28am +1000″.

Cachable by Search Engines

“Started 2 hours ago” or “Ends in 2 days” are meaningless when looking at a snippet or full copy of a page cached by a search engine 2 days ago. Likewise, “Started Mon, 4 June 2007, 10:04am +1000″ is difficult to understand for a person in a completely different time zone.

Cachable for Performance

We like to be able to allocate cache lifetimes to as many parts of our pages as possible. The text “Started Mon, 4 June 2007, 10:04am +1000″ needs no cache expiry, while “Started less than a minute ago” could only be reliably cached for 1 second.

Local Time

While most people can figure out what “Started Mon, 4 June 2007, 10:04am GMT” means in their local time zone, it would be most valuable if we could do the sums for them, so they don’t need to spend 30 seconds figuring out that the auction ended… 1 second ago.

Stale Pages

Because life wasn’t complicated enough already, tabbed browsing was invented so that we can force even more input into our heads at once. Tabs also makes it easy to get side tracked for hours before coming back to that SitePoint Marketplace listing. But currently there’s no way of knowing that the auction which says “Ends in 28 minutes” is actually long gone.

A Micro Solution

To solve all these problems in one fell swoop, we’ve created a very simple in-house microformat and some clever JavaScript. Together, they let us present dates and times in a way which is useful, meaningful and accessible to all users.

We start with some basic HTML markup:


<span class="sitepoint-datetime">Mon, 28 May 2007 01:30:49 GMT</span>

This will be seen by users without JavaScript, and some users of assistive technology. It may not be as pretty as “7 days ago”, but it’s the most correct, meaningful and cachable format when we don’t know how, when or where the page is actually being viewed. We’ve prefixed the class name with “sitepoint-” to make it clear that this is not a standard microformat.

We’ve used RFC 2822 formatting for the date/time, which is easily read by humans, and can also be passed straight into the constructor of a JavaScript Date object.

Our JavaScript finds all instances of this microformat, and converts them to the users local time, displaying it in a friendly format without an ugly time zone identifier hanging off the end.

We can then add additional information to the class attribute of the span. The JavaScript code uses these to decide the best way to display the information. For example, with an extra class of “endtime”, the JavaScript will convert the time to a countdown, displaying it in a format like “1 day, 3 hours”. The time remaining is recalculated every 30 seconds, so there’s no more stale information on left-open browser tabs. The script will even visually mark contests and auctions as having ended once the countdown reaches zero.

We’re looking forward to getting these and many other enhancements online over at the Design Contests and Marketplace.

In the mean time, I’m sure there’s many views out there regarding such use of microformats and JavaScript, and even whether the term “microformats” is applicable to this markup pattern. Please, bring them on.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • Ize

    Microformat Schmicroformat. You just did a very good job in enhancing this site’s usability. In my opinion; there’s no need to call it a “microformat”. :)

    Good job, guys!

  • http://www.saumendra.com Saumendra Swain

    well The naming convention do makes certain indication on the new horizon. A very informative article. Thanks for the effort to bring out here.

  • http://www.lowter.com charmedlover

    You should publish the code. :-)

  • BillyG

    I don’t see an example anywhere, but I do see that the comments in front of me have wrong times in their timestamps. Did you use your code there?

  • BillyG

    fyi: my local time is 10:22 am and it shows 12:22am for the comment timestamp, so I assume you didn’t use the article code there, but I don’t think their times have happened yet

  • BillyG

    nevermind, I’m the 5th, you’re in AusieLand lol

  • http://www.sitepoint.com/ Paul Annesley

    We’re +1000 in the office, and SitePoint.com is hosted in -0500.

    The code I mentioned is still under development, and will be seen first at SitePoint Marketplace and Contests when the new codebase makes it online.

  • http://www.realityedge.com.au mrsmiley

    The code is easy enough. I had to do something similar for an application I wrote once. You can grab the users timezone offset through the standard Javascript objects available to the browser. This should reflect the users regional settings on their computer. I’m assuming it works that way for Macs, does at least for Linux and Windows anyway.

    Then just use the standard date time functions to adjust the timestamp with the users timezone offset.

  • http://www.sitepoint.com/ Paul Annesley

    Actually I believe once you pass the RFC 2822 time string, which is in GMT, into the Date constructor, JavaScript automatically parses/converts it to local time. From then on, display of that date/time defaults to local time.

  • http://www.brothercake.com/ brothercake

    What Paul said :) The Date object can accept an input parameter in the form of an RFC date string, and the toString() method automatically converts to local time. So taking a date string and converting it to the same string in local time as as simple as this:

    new Date(datestring).toString();

    Another nifty bit of JS converts a date string to an object of differences between now and that date (or that date and now, as applicable):

    function get_time_difference(datestring)
    {
    	//create a date object from the date string,
    	//and a date object that represents now
    	var date = new Date(datestring);
    	var now = new Date();
    
    	//create an object of differences, beginning with a mindiff value
    	//which is the total difference in minutes
    	//we can use this to work out if a future time has been reached, or a past time is not in the past
    	//and to modify behavior when something has just started or is about to end
    	var between = { 'mindiff' : (date.getTime() - now.getTime()) / 60000 };
    
    	//the current assumption is that now is earlier than the input date
    	//to give the difference from now to the later date
    	//but if that's not the case, invert the values
    	//so that you'll get the difference from the earlier date to now
    	if (now >= date)
    	{
    		var tmp = date;
    		date = now;
    		now = tmp;
    	}
    
    	//add individual differences to the between object
    	between.year = date.getFullYear() - now.getFullYear();
    	between.month = date.getMonth() - now.getMonth();
    	between.day = date.getDate() - now.getDate();
    	between.hour = date.getHours() - now.getHours();
    	between.minute = date.getMinutes() - now.getMinutes();
    	between.second = date.getSeconds() - now.getSeconds();
    
    	//convert negative second difference to minute difference
    	if (between.second < 0)
    	{
    		between.minute--;
    		between.second += 60;
    	}
    
    	//convert negative minute difference to hour difference
    	if (between.minute < 0)
    	{
    		between.hour--;
    		between.minute += 60;
    	}
    
    	//convert negative hour difference to day difference
    	if (between.hour < 0)
    	{
    		between.day--;
    		between.hour += 24;
    	}
    
    	//convert negative day difference to month difference
    	//this one is more complicated because months aren't all the same length
    	if (between.day < 0)
    	{
    		between.month--;
    		var ynum = date.getFullYear();
    
    		var mlengths = [
    			31,
    			(ynum % 4 == 0 && ynum % 100 != 0 || ynum % 400 == 0) ? 29 : 28,
    			31, 30, 31, 30, 31, 31, 30, 31, 30, 31
    			];
    
    		var mnum = date.getMonth() - 1;
    		if (mnum < 0) { mnum += 12; }
    
    		between.day += mlengths[mnum];
    	}
    
    	//convert negative month difference to year difference
    	if (between.month < 0)
    	{
    		between.year--;
    		between.month += 12;
    	}
    
    	//return the between object
    	return between;
    }
    
  • http://www.realityedge.com.au mrsmiley

    new Date(datestring).toString();

    Hmmm … that would have saved me some trouble. I did all my date formatted server side, so didn’t come across that tidbit.

    BTW, for those wondering, and if its not too obvious why the RFC format is nice for reasons other than semantics, its mainly because there is no ambiguity in the date output.

    Don’t put 1/1/2007 and expect your users to figure out which number is the day and which one is the month. Make sure you either put the word for the month in there (1-Jan-2007), or use a military standard format like YYYY-MM-DD (eg 2007-01-01), or a combination like 2007-Jan-01. :)

    The joys of internationalisation.

  • Nicebutdim

    I have an existing booking system that is written for off-line meetings with real locations, so all times and dates are local time.

    I need to extend this to deal with on-line meetings.

    – Participants will be in different timezones
    – Participants will see info about past (archived), current (happening now) and future meetings
    – Meetings displayed may start or finish before or after the clocks go forward or back for daylight saving time

    My first idea is to store the time and date as UTC/GMT and convert with Javascript when displayed based on the viewer’s timezone.

    This is fine for current meeting, but what about past and future meetings?

    If one of the future (past) times is after (before) a daylight saving time change (or two or more changes) …

    Can I deal with this in Javascript?
    Should I deal with it in Javascript?
    How do I do it?

    (I’m guessing that it’s not done ‘automagically’, but am willing to be wrong.)

  • http://www.calcResult.co.uk omnicity

    Can you explain your reference to RFC2822 ?

    I would have thought that RFC 3339 was more appropriate, or better still the EcmaScript standard Date?

  • http://www.sitepoint.com/ Paul Annesley

    omnicity: an RFC 2822 date like “Tue, 12 Jun 2007 08:53:21 +1000″ is much nicer and easier to read for a human than an RFC 3339 formatted date like “2007-06-12T08:53:21+10:00″. And as mrsmiley pointed out, the RFC 2822 format is non-ambiguous – it’s just as easy and reliable for JavaScript / ECMAScript to parse.

  • http://www.calcResult.co.uk omnicity

    Paul,
    Those were just the first two that came to mind.

    My point was firstly that relying on a single portion of an otherwise un-related RFC is strange (what has SMTP got to do with HTTP ?)
    Secondly, the date is not fully readable, fully internationalised, nor much else. Surely it makes more sense to do this the other way around?

  • http://www.sitepoint.com/ Paul Annesley

    The RFC 2822 date format “Tue, 12 Jun 2007 08:53:21 GMT” met our requirements; an unambiguous date which is easily read by English speakers, and can be passed straight into a JavaScript Date constructor.

    If our requirements change in the future, for example if we provide our content in a language other than English, then we could choose another suitable format. We have the flexibility of using any format which JavaScript Date understands, without having to change any of our JavaScript code.

    I’m perfectly happy to borrow a suitable date format from an unrelated RFC, rather than making up a non-standard format or using a less suitable one. And given the flexibility we have, it’s no drama if our first choice doesn’t turn out to be the best.

  • http://www.calcResult.co.uk omnicity

    So was that a typo in your previous post when you had +1000 for the time zone info?
    That is an extremely ambiguous portion of the date format, more so even than the truncated month.
    (But not quite as bad as PCT and EST or whatever.)

    Again, doesn’t it make more sense to use a truly unambiguous machine-only format that is hidden from the user, coupled with a nicely formatted human-friendly, visible date?

  • Michel Merlin

    omnicity said Thu 14 June 19:53: « So was that a typo in your previous post when you had +1000 for the time zone info? »

    This is the ONLY official (and unambiguous) way to state that the TOG (Time Offset over GMT) is + 10 hour 0 minute (I recall that RFC2822 states to use TOG, not “time zone”). Since used in headers of 95% of the email messages on earth, it is quickly and unambiguously understood by most people anywhere on the planet; as Paul Annesley said on Tue 12 June 8:57: « the RFC 2822 format is non-ambiguous ». Please see details in PST and PDT; TZ and TOG; UTC and GMT; Internet Date & Time; PHP date. I add that a date like “1/2/3″ (or “01/02/03″) is read:

    – by an American, as (Thu) 2 Jan 2003
    – by an European, as (Sat) 1 Feb 2003
    – by a Newzealander, as (Sat) 3 Feb 2001

    omnicity said Wed 13 June 19:09: « …relying on a single portion of an otherwise un-related RFC is strange (what has SMTP got to do with HTTP ?) »

    RFC2822 is titled “Internet Message Format”, its chap 3.3 is “Date and Time Specification”, and is applied by 95% in the ~200 countries on earth, hence looks IMO quite “related” in this discussion titled “Simple Date and Time Localization With JavaScript”.

    Versailles, Tue 19 Jun 2007 11:42:05 +0200

  • http://www.calcResult.co.uk omnicity

    Michel,
    Please try and relax a little, you make it sound like I insulted you personally!

    To make it quite clear, I am not trying to pull this down as a solution to the Author’s problem. However when I first started to read the article, I thought that it might offer a solution to a problem of mine, but I am still not convinced that this is a universal solution.

    I am afraid that your argument is also flawed – this date format may be used in 95% of emails (or whatever the percentage actually is) but it is never read by a human. To me, +1000 is very nearly as ambiguous as using a time zone abbreviation, as it is not clear whether the 10 hours should be added or subtracted to the time as written.

    Similarly, my point about the date format being ambiguous again applies to Human use. By including both the day name and the date number, the possibility of ambiguity is introduced if the two do not match, which is very likely if it is edited by hand.

    Still don’t get what your last para means – what is the relation between SMTP and HTTP ?

  • Michel Merlin

    SMTP (basically a protocol to route messages), HTML (basically a mark-up language), and the present Simple Date and Time Localization With JavaScript discussion, are not directly related; no need IMO to bring the 2 first here. The RFC2822, that was brought in the article, is IMO more related – as some (including me) apparently thought, since they replied about it.

    Versailles, Tue 19 Jun 2007 17:19:50 +0200

  • http://www.calcResult.co.uk omnicity

    I give up.
    Who can argue with someone who quotes Wikipedia ?!

  • clydesan

    Paul, this is a neat solution with the RFC 2822 date, and more than adequate (at least for English speakers). What follows is not a criticism of that.

    Nobody has mentioned the international date-time standard ISO 8601. This is the basis of RFC 3339, the basis for date-times in SQL, and the basis for dates in the iCal calendar standard and thereby in the microformat standard for date-time.

    I can not agree with your statement

    an RFC 2822 date like “Tue, 12 Jun 2007 08:53:21 +1000″ is much nicer and easier to read for a human than an RFC 3339 formatted date like “2007-06-12T08:53:21+10:00″

    It is perhaps a tiny bit nicer and easier to read for an English speaker, only because of its familiarity. The time portion in each case is about the same; the ISO 8601 date format YYYY-MM-DD is easy to understand even for those who never saw it before, and it is standard usage in Asia. Personally, I have been using this date format since 1974.

  • Redoc

    I came across an article that explains how to create a javascript that achieves alot about what is talked about here. It is unobtrusive, and degrades gracefully. Available at:
    http://javascript.codeislogic.com/convert-utc-dates-to-local-timezone-offset-automatically

  • Anonymous