What’s The Best Date Format?

When it comes to displaying dates and times, there’s no single format that works for every situation. With such wide international and regional variation in formatting, and so many different situations in which dates and times are used, every case is unique — from verbose and lyrical sentences like Monday the 21st of March at 12:18am (EST), to enigmatic and compact output like Today.

But when it comes to working with dates programatically — storing, comparing and sorting with them — there’s really no need for such a wide variety of formats. Indeed, it makes much more sense to unify the use of programatic dates, and use the same format for everything.

So what’s the best format to use?

It would need to tick several boxes — think of these as ‘must-have’ features:

  • it stores a complete date and time, providing all the raw information we need to create any other format
  • we can use it as the input for creating and converting dates — eg. it must be compatible with the timestamp argument to JavaScript’s new Date constructor
  • it always use the same, fixed timezone, to provide a frame of reference for comparison

Over and above those core requirements, there’s a few ‘would-be-nice’ features:

  • its format is natively sortable without additional conversion (ie. if used as a filename, it would create a chronological sort-order)
  • it has no internal whitespace — to avoid parsing ambiguities such as excessive contiguous space, or tab/space difference
  • it provides at least some level of human-readability, chiefly for programmers’ sake
  • it should give us at least some ‘free’ information, ie. values such as the year or the hour being easily obtained by string parsing, without having to create and parse new date objects every time

What about timezones?

It’s not really necessary to store dates and times in different timezones — every given timestamp has a UTC/GMT equivalent. So the simplest thing to do is record all timestamps in UTC, converting them to local-time only when outputting to the client. This gives us a common frame of reference for programatically comparing dates, without losing any information we need for internationalization.

JavaScript dates are always local

Since JavaScript evaluates on the client, it uses the client’s clock as a time reference. So any timestamp passed through the Date constructor is automatically converted to the user’s local time.

My ultimate recommendation

In the past, until I actually sat down and though this out seriously, I generally used the RFC 2822 date format (eg. "Mon, 21 Mar 2011 00:18:56 +0000") — mostly because it’s so easy to generate in PHP (defined by the formatting character "r"), and it ticks all the must-have boxes. But it’s not natively sortable, and not the choice I ultimately arrived at:

My ultimate recommendation is ISO 8601 (2004) in UTC.

There are several variations of this format, and the one I’ve specifically chosen uses the date-time delimiter "T" and the timezone designator "Z" (which designates UTC); other variations use a space as the delimiter, or designate the timezone as "+00:00", but I don’t recommend either of those — the space makes it incompatible with new Date in Firefox, and the longer more complex timezone designator is simply unnecessary.

So the timestamps we’re talking about look like this: "2011-03-21T00:18:56Z"

Although this format is not as easy to generate as others (see How to use the ISO format below), it does tick all the other boxes, both the must-haves and would-be-nices. And it also provides a number of additional benefits — bonus features, you might say, which solidify its status as the ideal choice:

  • it’s always a constant length
  • it’s the format used natively by JSON (eg. in JavaScript, if you pass a Date object through JSON.stringify, it’s converted to this format)
  • it’s the format and profile recommended by the W3C
  • it’s compatible with the DATESTAMP column-type in SQL, albeit as ‘relaxed’ syntax
MySQL DATESTAMP gotcha

An issue I’ve discovered with PHP and MySQL, is that when a timestamp in this format stored in a DATESTAMP column, its "T" delimiter is replaced with a space, and its "Z" token is removed, converting it to the strict DATESTAMP format. But this is not what we want, and not acceptable.

All I do to fix this is re-convert the output, but I’d love to hear of a more permanent solution — a MySQL config option or something?

How to use the ISO format

PHP doesn’t have a pre-defined constant or token for creating this precise format, although the formatting-token "c" creates one very similar (same standard, different variant). We could then convert the differences with string-replacement; but I reckon the simplest thing to do is just compile it manually, from individual date and time components, using the gmdate function to get UTC:

$timestamp = gmdate('Y-m-dTH:i:sZ');

Once we have our timestamp, we can use it on the client as the timestamp argument to new Date. For example, we could first output the raw timestamp to static HTML:

<dl class="listing">
    <dt>Date created:</dt>
    <dd id="listing-timestamp"><?php echo gmdate('Y-m-dTH:i:sZ'); ?></dd>
</dl>

And then use JavaScript to convert it to a re-formatted locale-specific date and time, eg:

var dd = document.getElementById("listing-timestamp");
    
dd.innerHTML = new Date(dd.innerHTML).toLocaleString();

Of course the mechanics of passing timestamps around between the client and server will vary tremendously with the application. A PHP script may output the timestamp directly to server-generated JavaScript; it might be the response of an Ajax request, or it might be written to static HTML, like the example above. However it’s done, the most common use-case is likely to be server-generated timestamps which output to the client for conversion, and the processes I’ve described so far will do that. But what of other cases?

You might be working entirely on the server, and only outputting fully-formatted, user-ready dates. PHP provides numerous ways of internally converting one date to another, but the one I find most straightforward is to pass the timestamp through strtotime (to convert it to a numeric value), and then use that as the second argument to date:

$rfcdate = date('r', strtotime($timestamp));

Conversely, you might be generating and converting entirely on the client, and in that case you’ll need a way of generating the timestamp in JavaScript. The following function will do the job:

function getTimestamp()
{
    var dateobj = new Date();
    dateobj.setTime(dateobj.getTime() + (dateobj.getTimezoneOffset() * 60000));
    
    var datetime = {
        date : [
            dateobj.getFullYear(),    
            dateobj.getMonth() + 1,    
            dateobj.getDate()
            ],
        time : [
            dateobj.getHours(),        
            dateobj.getMinutes(),        
            dateobj.getSeconds()
            ]
        }; 
    
    for(var key in datetime)
    {
        if(!datetime.hasOwnProperty(key)) { continue; }
        
        for(var i in datetime[key])
        {
            if(!datetime[key].hasOwnProperty(i)) { continue; }
            
            var n = datetime[key][i];
            datetime[key][i] = (n < 10 ? '0' : '') + n; 
        }
    }
    
    return datetime.date.join('-') + 'T' 
         + datetime.time.join(':') + 'Z';
}

The timestamp it produces can then be passed to new Date, and processed in locale-time from there:

var timestamp = getTimestamp();
    
...
    
var rfcdate = new Date(timestamp).toString();

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Mike

    Well thought through.

    Could you just have MySQL store these dates in a DATETIME column, and train your application to ‘know’ that they mean UTC?

    • Mal Curtis

      This is my favourite. It’s how we do it at Learnable, and it’s how Rails does it by default. Timestamps are great because there’s no confusion but working with timestamps in a db is a fucking hassle when you’re viewing data. I always forget whether 1300257922 is in February or March…

      Every web developer should have a thorough understanding of their languages DateTime classes, as they can be really powerful and a little knowledge goes a long way to reducing stupid localized bugs.

  • Dan

    I generally just store the result of the time() function in php, (seconds since jan 1st 1970) as its easy to convert, compare and store. While it isn’t easy to read unformatted, I find it preferable over a string format due to its flexibility.

    • Michal

      Dan, I prefer two formats UTC ISO and unix timestamp (just when necessary). If it’s not required by the application design to add/deduct or perform other operations with time (where unix timestamps suits better), I use ISO date.

      It’s also better in general because of date of birth and other dates which unix timestamp can’t cover.

  • Stormrider

    What is this latest trend to output machine readable things into your HTML and then use JavaScript to make them human readable all about? It’s ridiculous – why is the conversion to a human readable format and timezone done in JavaScript, when PHP is perfectly capable of doing it? Just seems like laziness not to output it properly in the first place

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

      Nah it’s not just a trend, and it’s anything but laziness (how could it be lazy to add an abstraction layer and do more work?)

      There are two important reasons why it’s better to output fixed timestamps and parse them on the client:

      1 – you get conversion to user-locale without having to know the user’s locale; so you can provide timezone-specific dates and times for non-logged users as well as logged users

      2 – the pages become cacheable; if you output user-specific dates and times, then the page isn’t cacheable because it’s different for every user (or for chunks of users anyway). For a large, high-traffic site, the difference is significant

      It’s true though that the static HTML example I gave is not perfect, because it’s not human-readable; so if scripting is not available, it’s not great fallback content. So probably the best thing to do in that use-case, is to output the static timestamp in RFC format, and pass that to the Date constructor; this is what we do in the listings tables for Flippa and the SitePoint Market.

      There’s also another advantage to client-side dates:

      3 – they can be dynamic; so you can update phrases like “3 hours ago” or “yesterday” continually to keep them accurate without the need for page refreshes

  • Anonymous

    Another danger with using JavaScript to create human readable dates is that not everybody allows JS. I realize this is less prevalent than it used to be, but there are still people out there who won’t allow scripts. PHP is perfectly capable of converting the dates, and the location can be picked up with a function in php – sure this will not always result in the correct time zone, but you can always add the catchall “Not the right time zone?” and get them to select it, if indeed they care.

  • Anonymous

    Is is possible to get an insight on what date format facebook uses?

    They must be using industry standard, and I bet they have built sume pretty sweet libraries to handle dates and time.

  • BPM

    Not only programming, but how about real life? I’m a huge user of the Y-m-d format everywhere I can. It sorts naturally, which is so nice. I name reports, folders, and anything else dated this way.

    Do you want…
    2010-02-11 (Some Title)
    2010-04-12 (Some Other Title)
    2011-03-21 (Some Title)
    2011-03-22 (Some Other Title)
    …or…
    02-11-2010 (Some Title)
    03-21-2011 (Some Title)
    03-22-2011 (Some Other Title)
    04-12-2010 (Some Other Title)

    Sometimes it takes time to convince people, but using m-d-Y is as goofy as measuring things by the shoe size of some other country’s dead king.

    • Mike

      natural sort order is good, but I’d put the name first.
      Some Title 2010-02-11

      Then you can press S on the keyboard to jump to the files starting with S and begin your scan there.

      • Anonymous

        If you do that, the files will not sort in date order.

    • stupid yanks

      m-d-Y is goofy no matter which way you look at it. 3.2 billion people use d-m-Y, but of course the US had to be different. Only the US uses m-d-y exclusively, although there are many countries using both formats.

  • Louis Simoneau

    Another advantage to ISO 8601 is that it’s the format used by the HTML5 date and time input types.

    • Anonymous

      You have that backwards. HTML5 uses it because it is the standard.

  • Lukas Rosenthaler

    This is a nice way – but: not all dates are given in the same calendar. Especially dating historic material, You have to know if the date is given in the Julian (in use until 1582) or the Gregorian calendar. An easy way to store the date is the Julian Day Count (JDC) as used in Astronomy (The JDC counts the days since the year -4712, January 1st, 12h GMT, see http://quasar.as.utexas.edu/BillInfo/JulianDatesG.html for more information) The JDC makes it especially easy to calculate periods (just take the difference and You get the number of days).There are several routines available in PHP and JS to convert from/to JDC into different calendars.

  • Wil Moore III

    Agreed…especially the point about storing in UTC.

    Also, you will find that using ISO 8601 allows the dateTime values to be used directly (without conversion) in an XML document and validate using XSD (xsd:dateTime).

  • Dathan

    I use Excell to sort items by date, Excell has specific formats it recognizes and it coverts that format when you sort by date. Therefore, I stick to Excell’s format, i.e. Mar 22, 2011, Apr 3, 2011 etc. Having the date available to copy off copy in this format is easier because then I don’t have to manually reformat it to be sortable.

  • AnilG

    Why does it always have to be PHP and MySQL?

    What’s wrong with Python and DB2?

  • Cmdr. Beavis

    I’m fine with the ISO format suggested.. except for the dashes. Surely we’re not storing 3 “-” chars ?! (I’ve been using YYYYMMDD date format since my Clipper days for ease of sorting.) I’m also a bit suprised at the colon’s in the time part. What’s wrong with just plain HHMMSS? I mean, for DISPLAY, ok, of course, but for storage!?

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

      The use of delimiters makes it easier and cheaper to parse.

      A date like “2011-03-24″ can be converted to its component parts with a single split (or explode) operation; but to extract the parts from “20110324″ you would need either a regex match (which is much more expensive) or three separate substring extractions.

  • Andrew

    Does the ISO format allow for milliseconds? My brain defaults to DB2 format (due to my background) which is very similar to that – except for the milliseconds. I know sometimes a timestamp, along with other information, is used to create unique random ids…
    I don’t mind how the database physically stores it – as long as it’s usable. Oh, and I found out the hard way that having a desktop application mark timestamps with the local time only (no timezone, no UTC) can create interesting scenarios – such as work being “completed” before it’s “submitted” when you work across date/time zones…

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

      Yes, the format allows for milliseconds, expressed as 3 digits after a decimal point.

      PHP’s microtime() function is really weird though — the format it outputs is not at all intuitive. If I want a timed-based unique-id in PHP, I generally just use time() and then add some random digits on the end.

      In ISO format (at any rate), if a timezone is not specified, it implies “local time”, which I guess implies “server local time”; but you’re right, that’s a recipe for confusion; much better to specify UTC.