D3 Tips and Tricks v4

Saturday, 15 December 2012

Formatting the Date / Time on a D3.js Graph

One of the glorious things about the World is that we all do things a bit differently. One of those things is how we refer to dates and time.

In my neck of the woods, it's customary to write the date as day - month – year. E.g 23-12-2012. But in the United States the more common format would be 12-23-2012. Likewise, the data may be in formats that name the months or weekdays (E.g. January, Tuesday) or combine dates and time together (E.g. 2012-12-23 15:45:32). So, if we were to attempt to try to load in some data and to try and get D3 to recognise it as date / time information, we really need to tell it what format the date / time is in.

Now, you might be asking yourself what's the point? All you want to do is give it a number and it can sort it out somehow. Well, that is true, but if you want to really bring out the best in your data and to keep maximum flexibility in representing it on the screen, you will want to D3 to play to it's strengths. And one of those is being able to adjust dynamically with variable time values.

Time for a little demonstration.

I will change our data.tsv file that we use to load data to graph so that it only includes two points. The first one and the last one with a separation of a month and a bit.
date    close
1-May-12    58.13
26-Mar-12   606.98
The graph now looks like this;
So, nothing too surprising here, a very simple graph (note the time scale on the x axis).

But now I will change the later date in the data.tsv file so that it is a lot closer to the starting date;
date    close
29-Mar-12   58.13
26-Mar-12   606.98
So, just a three day difference. Let's see what happens.
Ahh.... Not only did we not have to make any changes to our JavaScript code, but it was able to recognise the dates were closer and filled in the intervening gaps with appropriate time / day values.

Now, one more time for giggles.

This time we'll stretch the interval out by a few years.
date    close
29-Mar-21   58.13
26-Mar-12   606.98
and the result is...
Hopefully that's enough encouragement to impress upon you that formatting the time is a REALLY good thing to get right and trust me, it will never fail to impress :-).

So, back to formatting.

The line in the JavaScript that parses the time is the following;
var parseDate = d3.time.format("%d-%b-%y").parse;

This line is used when the data.forEach(function(d) portion of the code that we looked at a couple of pages back used d.date = parseDate(d.date) as a way to take a date in a specific format and to get it recognised by D3. In effect it said take this value that is supposedly a date and make it into a value I can understand.

The function used is the d3.time.format(specifier) function where the specifier in this case is the mysterious combination of characters '%d-%b-%y'. The good news is that these are just a combination of directives specific for the type of date we are presenting.

The '%' signs are used as prefixes to each separate format type and the '-' signs are literals for the actual '-' signs that appear in the date to be parsed.

The 'd' refers to a zero-padded day of the month as a decimal number [01,31].

The 'b' refers to an abbreviated month name.

And the 'y' refers to the year with century as a decimal number.

If we look at a subset of the data from the data.tsv file we see that indeed, the dates therein are formatted in this way.
1-May-12 58.13
30-Apr-12 53.98
27-Apr-12 67.00
26-Apr-12 89.70
25-Apr-12 99.00

So that's all well and good, but what if your data isn't formatted exactly like that?

Good news. There area a heap of different formatters for different ways of telling time and you get to pick and choose what you want. Check out the Time Formatting page on the D3 Wiki for a the authoritative list and some great detail, but the following is the list of currently available formatters (from the d3 wiki);
  • %a - abbreviated weekday name.
  • %A - full weekday name.
  • %b - abbreviated month name.
  • %B - full month name.
  • %c - date and time, as "%a %b %e %H:%M:%S %Y".
  • %d - zero-padded day of the month as a decimal number [01,31].
  • %e - space-padded day of the month as a decimal number [ 1,31].
  • %H - hour (24-hour clock) as a decimal number [00,23].
  • %I - hour (12-hour clock) as a decimal number [01,12].
  • %j - day of the year as a decimal number [001,366].
  • %m - month as a decimal number [01,12].
  • %M - minute as a decimal number [00,59].
  • %p - either AM or PM.
  • %S - second as a decimal number [00,61].
  • %U - week number of the year (Sunday as the first day of the week) as a decimal number [00,53].
  • %w - weekday as a decimal number [0(Sunday),6].
  • %W - week number of the year (Monday as the first day of the week) as a decimal number [00,53].
  • %x - date, as "%m/%d/%y".
  • %X - time, as "%H:%M:%S".
  • %y - year without century as a decimal number [00,99].
  • %Y - year with century as a decimal number.
  • %Z - time zone offset, such as "-0700".
  • %% - a literal "%" character.
As an example, if you wanted to input date / time formatted as a generic MySQL 'YYYY-MM-DD HH:MM:SS' TIMESTAMP format the D3 parse script would look like;
parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;

12 comments:

  1. convert datetime to 12 hours AM/PM format
    http://allinworld99.blogspot.in/2014/07/get-am-pm-from-datetime-javascript.html

    ReplyDelete
  2. convert datetime to 12 hours AM/PM format
    AM/PM Converter

    ReplyDelete
  3. Or you can convert it to TIMESTAMP in JS the time is in milliseconds so just multiply by 1000 and parse it with %x

    ReplyDelete
  4. Hello,
    know somebody the meaning of this:
    var formatYear = d3.format("02d")
    ?

    ReplyDelete
    Replies
    1. Hmm... I don't see any indication in the specifiers for the format function (https://github.com/mbostock/d3/wiki/Time-Formatting#format) for that syntax. Are you able to provide a link to a page with working code that uses this?

      Delete
    2. Thank you for your answer. I found this function in: http://bl.ocks.org/mbostock/4679202

      Delete
    3. Ahh... Apologies. I mistook it for d3.time.format. The line you're referring to is a format specifier that takes a number as the only argument, and returns a string representing the formatted number (https://github.com/mbostock/d3/wiki/Formatting#d3_format).
      In the case of the example here, the formatYear function is always passed the last two digits of a full year. e.g. 2014 = 14 or 1998 = 98 by the following line (formatYear(d.getFullYear() % 100)). So in the '02d' part the 0 instructs the returned string to use a '0' in the value(Called Zero Padding) (Otherwise the function would change '08' to '8'. The '2' sets the returned minimum field width, so it will always be 2 characters and the 'd' ensures that the number is converted to a string.
      Hopefully that helps. Thanks for the question. I certainly learned something :-)

      Delete
  5. Thank you very much for your detailed answer. It's really helpful :-)

    ReplyDelete
  6. hi how to show the name of the day for (dd-mm-yyyy) on that format please give me the answer

    ReplyDelete
    Replies
    1. Check out the detail here https://leanpub.com/D3-Tips-and-Tricks/read#leanpub-auto-format-a-date--time-axis-with-specified-values and to return the name of the day, per the formatting information above, use %A - full weekday name.

      Delete