Javascript library for human-friendly relative date formatting

76,930

Solution 1

Since I wrote this answer, a well known library available is moment.js.


There are libraries available, but it is trivial to implement it yourself. Just use a handful of conditions.

Assume date is an instantiated Date object for the time you want to make a comparison against.

// Make a fuzzy time
var delta = Math.round((+new Date - date) / 1000);

var minute = 60,
    hour = minute * 60,
    day = hour * 24,
    week = day * 7;

var fuzzy;

if (delta < 30) {
    fuzzy = 'just then.';
} else if (delta < minute) {
    fuzzy = delta + ' seconds ago.';
} else if (delta < 2 * minute) {
    fuzzy = 'a minute ago.'
} else if (delta < hour) {
    fuzzy = Math.floor(delta / minute) + ' minutes ago.';
} else if (Math.floor(delta / hour) == 1) {
    fuzzy = '1 hour ago.'
} else if (delta < day) {
    fuzzy = Math.floor(delta / hour) + ' hours ago.';
} else if (delta < day * 2) {
    fuzzy = 'yesterday';
}

You would need to adapt this to handle future dates.

Solution 2

I wrote moment.js, a date library that does this. It's about 5KB (2011) 52KB (2019), and works in browsers and in Node. It's also probably the most popular and famous date library for JavaScript.

It supports timeago, formatting, parsing, querying, manipulating, i18n, etc.

Timeago (relative time) for dates in the past is done with moment().fromNow(). For example, to display January 1, 2019 in the timeago format:

let date = moment("2019-01-01", "YYYY-MM-DD");
console.log(date.fromNow());
<script src="https://momentjs.com/downloads/moment.min.js"></script>

The timeago strings are customizable with moment.updateLocale(), so you can change them how you see fit.

The cutoffs are not what the question requests ("5 weeks" vs. "1 month"), but it is documented as to which strings are used for what time range.

Solution 3

Here's something from the John Resig - http://ejohn.org/blog/javascript-pretty-date/

EDIT (6/27/2014): Following up on the comment from Sumurai8 - though the linked page still works, here is the excerpt for the pretty.js linked to from the article above:

pretty.js

/*
 * JavaScript Pretty Date
 * Copyright (c) 2011 John Resig (ejohn.org)
 * Licensed under the MIT and GPL licenses.
 */

// Takes an ISO time and returns a string representing how
// long ago the date represents.
function prettyDate(time) {
    var date = new Date((time || "").replace(/-/g, "/").replace(/[TZ]/g, " ")),
        diff = (((new Date()).getTime() - date.getTime()) / 1000),
        day_diff = Math.floor(diff / 86400);

    if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31) return;

    return day_diff == 0 && (
    diff < 60 && "just now" || diff < 120 && "1 minute ago" || diff < 3600 && Math.floor(diff / 60) + " minutes ago" || diff < 7200 && "1 hour ago" || diff < 86400 && Math.floor(diff / 3600) + " hours ago") || day_diff == 1 && "Yesterday" || day_diff < 7 && day_diff + " days ago" || day_diff < 31 && Math.ceil(day_diff / 7) + " weeks ago";
}

// If jQuery is included in the page, adds a jQuery plugin to handle it as well
if (typeof jQuery != "undefined") jQuery.fn.prettyDate = function() {
    return this.each(function() {
        var date = prettyDate(this.title);
        if (date) jQuery(this).text(date);
    });
};

Usage:

prettyDate("2008-01-28T20:24:17Z") // => "2 hours ago"
prettyDate("2008-01-27T22:24:17Z") // => "Yesterday"
prettyDate("2008-01-26T22:24:17Z") // => "2 days ago"
prettyDate("2008-01-14T22:24:17Z") // => "2 weeks ago"
prettyDate("2007-12-15T22:24:17Z") // => undefined

Excerpt from the article on usage:

Example Usage

In the following examples I make all the anchors on the site, that have a title with a date in it, have a pretty date as their inner text. Additionally, I continue to update the links every 5 seconds after the page has loaded.

With JavaScript:

function prettyLinks(){
    var links = document.getElementsByTagName("a");
    for ( var i = 0; i < links.length; i++ )
        if ( links[i].title ) {
            var date = prettyDate(links[i].title);
            if ( date )
                links[i].innerHTML = date;
        }
}
prettyLinks();
setInterval(prettyLinks, 5000);

With jQuery:

$("a").prettyDate();
setInterval(function(){ $("a").prettyDate(); }, 5000);

Faiz: Made some changes to the original code, bug fixes and improvements.

function prettyDate(time) {
    var date = new Date((time || "").replace(/-/g, "/").replace(/[TZ]/g, " ")),
        diff = (((new Date()).getTime() - date.getTime()) / 1000),
        day_diff = Math.floor(diff / 86400);
    var year = date.getFullYear(),
        month = date.getMonth()+1,
        day = date.getDate();

    if (isNaN(day_diff) || day_diff < 0 || day_diff >= 31)
        return (
            year.toString()+'-'
            +((month<10) ? '0'+month.toString() : month.toString())+'-'
            +((day<10) ? '0'+day.toString() : day.toString())
        );

    var r =
    ( 
        (
            day_diff == 0 && 
            (
                (diff < 60 && "just now")
                || (diff < 120 && "1 minute ago")
                || (diff < 3600 && Math.floor(diff / 60) + " minutes ago")
                || (diff < 7200 && "1 hour ago")
                || (diff < 86400 && Math.floor(diff / 3600) + " hours ago")
            )
        )
        || (day_diff == 1 && "Yesterday")
        || (day_diff < 7 && day_diff + " days ago")
        || (day_diff < 31 && Math.ceil(day_diff / 7) + " weeks ago")
    );
    return r;
}

Solution 4

sugar.js has great date formatting functions.

Not only that, it also provides common general purpose functions like string formatting, number formatting, etc. that are convenient to use.

Solution 5

here an example of sugar vs moment: for a calendar that displays weeks, I needed the last monday value:

moment.js

https://momentjs.com/

var m = moment().subtract("days", 1).sod().day(1) // returns a "moment"

sugar.js

var d = Date.past("monday") // returns a js Date object

I much prefer sugar and after some months with moment.js now switch to sugar.js. it is clearer and integrates nicely with Javascripts' Date class.

OP cases are covered by both libs, for sugar.js see http://sugarjs.com/dates

Share:
76,930

Related videos on Youtube

rampion
Author by

rampion

Mathematician, programmer, and researcher; with interests in algorithmic design, software engineering theory, and massively parallel computing. I'm also a keyboard junkie and an aspiring language nerd.

Updated on March 25, 2021

Comments

  • rampion
    rampion about 3 years

    I'd like to display some dates as relative to the current date in a human-friendly format.

    Examples of human-friendly relative dates:

    • 10 seconds ago
    • 20 minutes from now
    • 1 day ago
    • 5 weeks ago
    • 2 months ago

    Basically faithfully preserving the highest order of magnitude (and by preference, only shifting up units when passing 2 of those units - 5 weeks instead of 1 month).

    Though I could live with a library that had less control and even more friendly dates like:

    • yesterday
    • tomorrow
    • last week
    • a few minutes ago
    • in a couple hours

    Any popular libraries for this?

    • RobG
      RobG about 12 years
      Why is "1 day ago" more "human friendly" than simply presenting the actual date and time?
    • wprl
      wprl almost 11 years
      @RobG I would say it's more about avoiding switching contexts e.g. in a page that is mostly text and being read, the context switch to e.g. mm/dd/yy can cause pause. In a table of data, using that format might be more readable. It also depends on what the reader needs to do with the date e.g. whether "this happened n days ago" or "this happened before 1/1/1972" is actionable or otherwise appropriate to the reader's context.
    • RobG
      RobG almost 11 years
      Perhaps, but it is confusing to see a list of events as "Yesterday...3 days ago...10/May...". I still need to convert them all to dates in my head to get a picture of when they occurred. Dates are concise and precise, "time ago" values are conversational, lack precision and are usually only helpful with the associated date. Maybe that's just me, but maybe not. :-)
    • Simon Williams
      Simon Williams about 10 years
      I would say it depends on the context. After all, you wouldn't say "I went fishing on 17th February 2014" if that was actually yesterday. There's much more of a brain pause there. This kind of text is perfect for a list of recent events.
    • Admin
      Admin over 9 years
      @RobG Only nerds like us think like that not normal people.
    • RobG
      RobG over 9 years
      @Dad—I don't consider myself a nerd and "normal" people do think that way when presented with a list of dates.
    • SpYk3HH
      SpYk3HH over 7 years
  • Stephen
    Stephen over 12 years
    Nice lib suggestion. Internationalization definitely a plus.
  • rampion
    rampion over 12 years
    Date.js was my first thought too, but I don't see any way to go from number to format with it - though it might be hidden in the docs somewhere.
  • mxcl
    mxcl over 11 years
    Yesterday is before the last midnight, not between 24 hours and 48 hours ago.
  • user3167101
    user3167101 over 11 years
    @mmaclaurin Mine was never meant to be a complete solution, just a pointer in the right direction. I'll make a note to update it later, or if you want, feel free to edit the answer.
  • citykid
    citykid over 11 years
    agree, sugar.js deserves more attention here.
  • wprl
    wprl almost 11 years
    Kudos for making it work in browser and node!!!!
  • John Zabroski
    John Zabroski almost 11 years
    Date.js is known to have serious bugs, and cannot be trusted in production environments. Many frameworks are switching from Date.js to Moment.js
  • John Bachir
    John Bachir over 10 years
    I think this is the best solution. The library is very actively maintained, it's based off of / inspired by Resig's code, it's very small, it has tons of localizations, it's trivial to integrate.
  • fat fantasma
    fat fantasma over 9 years
    I learned the hard way that datejs does not work on linux :(
  • Faiz
    Faiz about 8 years
    Hi Floyd, I have added some changes (bug fix, improvements) to your answer. Hope you don't mind..
  • Arthur Ronconi
    Arthur Ronconi over 7 years
    Good one! But don't work with timestamp numeric type, maybe need a better filter like if(typeof time == 'string') { time = time.replace(/-/g, "/").replace(/[TZ]/g, " ")); }
  • Askdesigners
    Askdesigners over 7 years
    ha that size update though!
  • mesqueeb
    mesqueeb almost 7 years
    Please also have a look at date-fns! It's a great library if you like to keep your code base small, because it has a much much lower footprint than momentjs!
  • mesqueeb
    mesqueeb almost 7 years
    Please also have a look at date-fns! It's a great library if you like to keep your code base small, because it has a much much lower footprint than momentjs!
  • Code Whisperer
    Code Whisperer almost 7 years
    As good as this library is, the answer does not include an explanation on how to format a number in a human-friendly manner using it
  • pomber
    pomber about 4 years
    I changed this code to make a twitter style getTimeAgo function gist.github.com/pomber/6195066a9258d1fb93bb59c206345b38
  • Ikenna Emman
    Ikenna Emman over 3 years
    Please, how to account for future dates? Like 2 days from now.
  • Juan De la Cruz
    Juan De la Cruz over 3 years
    Are you going to update too with the "deprecation"? momentjs.com/docs/#/-project-status
  • Artur Carvalho
    Artur Carvalho about 3 years
    You missed the week case.
  • A5H1Q
    A5H1Q almost 3 years
    very easy to integrate +1
  • phreakhead
    phreakhead almost 2 years
    If anything, all the edge cases you missed make it clear this isn't actually "trivial to implement yourself". Especially if you want to localize to different countries/langauges.