JavaScript <time> to time ago / future

A small JS library to convert HTML 5 <time> tags to an x time ago / in future. For example:

<time datetime="2016-08-03">3 Aug 2016</time>

Will be displayed as instead of .

Why re-invent the wheel? The only JS libraries I could find were either jQuery based (I’m not using jQuery so don’t want the extra bloat) or did far more than I actually wanted. So I decided to create this one.

Date parsing

The hardest part is parsing the date. According to the HTML5 W3 spec the <time> tag’s datetime attribute should be in RFC 3339 format. The RFC 3339 is a subset of ISO 8601 with the exception that it allows -00:00 timezones.

The Date.parse method will parse a subset of ISO 8601 which, as far as I can tell, covers all the RFC 3339 subset. So by removing any trailing -00:00 we should be able to parse any RFC 3339 date:

var date = Date.parse(dateTime.replace(/\-00:?00$/, ''));

Browser support

This works in Chrome, Firefox, Safari, Opera, IE 9+ and Edge.

In IE < 9 the Date.parse method doesn’t support ISO 8601. You could add a polyfill for it but, for my purposes, I decided to just let IE < 9 fallback to the default date instead.

Source Code

/**
 * Take an RFC 3339 or ISO 8601 date and returns
 * the date in human readable form.
 *
 * Will return undefined if lacks browser support
 * or it cannot parse the date.
 *
 * @param  {string} time
 * @param  {object} [lang] Optional language object
 * @return {string|undefined}
 * @license MIT
 * @author Sam Clarke <sam@samclarke.com>
 */
function timeToWords(time, lang) {
  lang = lang || {
    postfixes: {
      '<': ' ago',
      '>': ' from now'
    },
    1000: {
      singular: 'a few moments',
      plural: 'a few moments'
    },
    60000: {
      singular: 'about a minute',
      plural: '# minutes'
    },
    3600000: {
      singular: 'about an hour',
      plural: '# hours'
    },
    86400000: {
      singular: 'a day',
      plural: '# days'
    },
    31540000000: {
      singular: 'a year',
      plural: '# years'
    }
  };

  var timespans = [1000, 60000, 3600000, 86400000, 31540000000];
  var parsedTime = Date.parse(time.replace(/\-00:?00$/, ''));

  if (parsedTime && Date.now) {
    var timeAgo = parsedTime - Date.now();
    var diff = Math.abs(timeAgo);
    var postfix = lang.postfixes[(timeAgo < 0) ? '<' : '>'];
    var timespan = timespans[0];

    for (var i = 1; i < timespans.length; i++) {
      if (diff > timespans[i]) {
        timespan = timespans[i];
      }
    }

    var n = Math.round(diff / timespan);

    return lang[timespan][n > 1 ? 'plural' : 'singular']
      .replace('#', n) + postfix;
  }
}

Example usage

document.addEventListener('DOMContentLoaded', function () {
  var elements = document.getElementsByTagName('time');
  for (var i = 0; i < elements.length; i++) {
    var elm = elements[i];
    // The date should be either in the datetime attribute
    // or in the text contents if no datetime attribute
    var date = elm.getAttribute('datetime') || elm.textContent;

    var dateInWords = timeToWords(date);
    if (dateInWords) {
      elm.textContent = dateInWords;
    }

    // you could use setInterval to automatically update
    // the timestamps every so often if wanted
  }
});

The timeToWords function also accepts a language object so the output can be translated. For example:

var norwegian = {
  // Postfixes - ago / from now
  postfixes: {
    '<': ' siden',
    '>': ' fra nå'
  },
  // Seconds
  1000: {
    singular: 'et øyeblikk',
    plural: 'et øyeblikk'
  },
  // Minutes
  60000: {
    singular: 'omtrent et minutt',
    plural: '# minutter'
  },
  // Hours
  3600000: {
    singular: 'omtrent en time',
    plural: '# timer'
  },
  // Days
  86400000: {
    singular: 'en dag',
    plural: '# dager'
  },
  // Years
  31540000000: {
    singular: 'et år',
    plural: '# år'
  }
};

// Will set dateInWords to a Norwegian translation
var dateInWords = timeToWords(date, norwegian);

Demo

View on CodePen

You need JavaScript enabled to see comments.