Translating Date Formats in JavaScript

Urgh, dates.  It’s always bloody dates.  Whether it’s different time zones, localised formats (both long and short) or any one of a hundred other annoying little problems…somehow, whenever you start working with dates, everything goes to hell.

We can all agree on how we should be dealing with dates, right?  ISO 8601 for everything in the backend (transport & database), then a localised format to present to the user in the UI layer. As Randall Munroe recently said/drew:

Date Formats

That’s all well and good, but when your UI layer makes use of JavaScript you have the additional headache of dealing with the JavaScript Date object (though you can avoid real agony by using momentjs).

The Most Important Piece of (Off-Topic) Advice in this Post

If you are not already using momentjs for every date-related scenario then you are almost certainly wasting a lot of time. It’s under 6kb, it will do more-or-less everything you want with dates…go get it now.

The problem with dates in JavaScript gets even worse if you are pulling your date format from somewhere other than the client.  Suppose that you have a “preferred date format” setting for each user on the server - you need to somehow pass that into your JavaScript.

Admittedly momentjs makes this easier: you can parse the dates from the server using ISO format and then display them in whatever crazy format you want.

var dateFromServerInIsoFormat = '2013-01-14',
  preferredFormatFromServer = 'D * MMM * YYYY',
  parsed = moment(dateFromServerInIsoFormat, 'YYYY-MM-DD'),
  formatted = parsed.format(preferredFormatFromServer);

console.log(formatted);
// -> 14 _ Jan _ 2013

This works – it will allow us to format dates within our JavaScript using a date format specified by the server – but it relies on an incorrect assumption: that the server and momentjs share a date format language.

One Format, Many Format Definitions

Perhaps the best way to demonstrate this problem is with an example.  Let’s assume that we are running ASP.NET on our server, using momentjs to handle dates within our JavaScript, and (just to spice things up) we have a couple of third party JavaScript components: jQuery UI DatepickerjqPlot.

We have a user setting stored on the server that defines our “preferred date format” as…

January 15, 2013

Let’s take a look at how we would define that for our four components.

.NET MMMM d, yyyy Nice and simple so far
momentjs MMMM D, YYYY Slightly different, but still pretty close
jQuery UI Datepicker MM d, yy Ok, getting a little bit trickier...
jqPlot %B %#d, %Y Wait, what?!
What’s going on here?!  You can just about see how you could convert from the .NET format to momentjs, or even to the jQuery UI one without too much pain, but jqPlot is just a mess!

Translating Between Formats

After being plagued by numerous variations on this problem on a recent project, I decided to deal with it properly by creating something to translate between (theoretically) any 2 formats.

The dateFormat object contains definitions for the different format languages, and the convert function translates between them:

var formatFromServer = 'MMMM d, yyyy',
  momentFormat = dateFormat.convert(
    formatFromServer,
    dateFormat.dotnet,
    dateFormat.moment
  ),
  jqPlotFormat = dateFormat.convert(
    formatFromServer,
    dateFormat.dotnet,
    dateFormat.jqplot
  );

console.log('moment: ' + momentFormat);
// -> moment: MMMM D, YYYY

console.log('jqPlot: ' + jqPlotFormat);
// -> jqPlot: %B %#d, %Y

Each language definition contains a list of mappings from named tokens (e.g. day-of-week) to their representation in the format:

dateFormat.dotnet = {
  'day-of-month-1': 'd',
  'day-of-month-2': 'dd',
  'day-of-week-abbr': 'ddd',
  'day-of-week': 'dddd'
  //...
};

This allows the convert function to create a mapping from one language to another, then use regular expressions to locate and replace matching tokens in the source string.

The source code is available on GitHub along with the tests and some distributables.  At the moment the library only supports 3 formats (.NET, momentjs and jqplot) as these were the 3 I needed most recently, but that list will expand over time - hopefully with some help from the community.

This jsFiddle has a running example of the conversion so feel free to play around, or grab the source yourself from one of the links below.