How to format time since xxx e.g. “4 minutes ago” similar to Stack Exchange sites
Asked Answered
F

41

372

The question is how to format a JavaScript Date as a string stating the time elapsed similar to the way you see times displayed on Stack Overflow.

e.g.

  • 1 minute ago
  • 1 hour ago
  • 1 day ago
  • 1 month ago
  • 1 year ago
Formant answered 5/7, 2010 at 7:45 Comment(6)
momentjs.comSapheaded
github.com/catamphetamine/javascript-time-agoForeglimpse
Useful for this: Intl.RelativeTimeFormat.prototype.format().Knox
Moment is deprecated, it is a legacy project in maintenance mode. Switch to built-in Intl.RelativeTimeFormat in javascript or use other alternatives.Becht
Since there is no native way to do this, IMO this is the same as asking for an external library, which is off-topic on Stack Overflow.Cloudless
It would be an honor for you to use my tiny script 0.5kbManipulator
F
484

function timeSince(date) {

  var seconds = Math.floor((new Date() - date) / 1000);

  var interval = seconds / 31536000;

  if (interval > 1) {
    return Math.floor(interval) + " years";
  }
  interval = seconds / 2592000;
  if (interval > 1) {
    return Math.floor(interval) + " months";
  }
  interval = seconds / 86400;
  if (interval > 1) {
    return Math.floor(interval) + " days";
  }
  interval = seconds / 3600;
  if (interval > 1) {
    return Math.floor(interval) + " hours";
  }
  interval = seconds / 60;
  if (interval > 1) {
    return Math.floor(interval) + " minutes";
  }
  return Math.floor(seconds) + " seconds";
}
var aDay = 24*60*60*1000;
console.log(timeSince(new Date(Date.now()-aDay)));
console.log(timeSince(new Date(Date.now()-aDay*2)));
Formant answered 5/7, 2010 at 7:45 Comment(12)
@hello - yeah, single point of exit has it's virtues when it doesn't get in the way. those that take it too seriously these days are misunderstanding the origin of the maxim.Formant
Good function, but some remarks. Changed the first line to: var seconds = Math.floor(((new Date().getTime()/1000) - date)) to work with unix timestamps. And needed to change the intval > 1 to intval >=1 otherwise it would show things like 75 minutes (between 1 and 2 hours).Sarad
@Sarad if you just change > to >= then you will end up with times like "1 minutes". I posted a modified version of this answer that conditionally adds the "s": https://mcmap.net/q/92211/-how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-sitesPrier
Never use string concatenation, but String.format if you want a solution that can be internationalizedBlomquist
What if I want to place it in div class? What can I do? Sorry I'm not a pro in javascript. I tried this document.getElementsByTagName('.sampleclass')[0].innerHTML = timeSince(date); and this document.getElementById('idname')[0].innerHTML = timeSince(date); but it's not working. Any help? Thank you.Shelton
@Sarad still facing some issue, when I'm using this function, it returns 63 secs instead of 1 min, 26 hours instead of 1 day.Crews
@SkySanders can you please let me know how can I resolve above issue?Crews
If I needed to mainly display fresh content, I would rewrite this function to have it in reverse order (to avoid useless calculations, floor() calls and ifs).Sherrisherrie
how to calculate future date and time ..like In 5 Days or 3HrsRevive
Additionally, one could add > 2 at all levels to achieve plural hours, days, months, etc like this: interval = seconds / 86400; if (interval > 2) {return Math.floor(interval) + " days"; and interval = seconds / 86400; if (interval > 1) {return Math.floor(interval) + " day";Chyle
This fails to account for plurals and will return "1 minutes ago", "1 hours ago". My answer accounts for plurals https://mcmap.net/q/92211/-how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-sitesProtozoology
@SkySanders this code is fine except: The Problem with this way of calculating the interval will return wrong data at some point because you are not expecting possible time deviations caused by leap years or semi-annual timeshifts. I will post an additinal answer soon to address this problem.Bolden
C
206

Might be an overkill in this case, but if the opportunity shows moment.js is just awesome!

Moment.js is a javascript datetime library, to use it for such scenario, you'd do:

moment(yourdate).fromNow()

http://momentjs.com/docs/#/displaying/fromnow/

2018 addendum: Luxon is a new modern library and might be worth a look!

2022 addendum: Day.js is a newer library that's about 80% lighter than Luxon with similar capabilities.

Cryptozoite answered 16/4, 2013 at 16:50 Comment(5)
Hello, I am using your answer for getting time differene.What can I do if I need only first letters of datelike year as y, month and m and day as d?Deflower
how do I only get D M s h and not days ago months ago using this?Hoi
Not overkill if you already have moment installed. Thanks!Foamflower
Apparently this is outdated and slow.Sparry
The equivalent Luxon method is toRelativeCalendarPlacencia
Z
84

This will show you past and previous time formats like '2 days ago' '10 minutes from now' and you can pass it either a Date object, a numeric timestamp or a date string

function time_ago(time) {

  switch (typeof time) {
    case 'number':
      break;
    case 'string':
      time = +new Date(time);
      break;
    case 'object':
      if (time.constructor === Date) time = time.getTime();
      break;
    default:
      time = +new Date();
  }
  var time_formats = [
    [60, 'seconds', 1], // 60
    [120, '1 minute ago', '1 minute from now'], // 60*2
    [3600, 'minutes', 60], // 60*60, 60
    [7200, '1 hour ago', '1 hour from now'], // 60*60*2
    [86400, 'hours', 3600], // 60*60*24, 60*60
    [172800, 'Yesterday', 'Tomorrow'], // 60*60*24*2
    [604800, 'days', 86400], // 60*60*24*7, 60*60*24
    [1209600, 'Last week', 'Next week'], // 60*60*24*7*4*2
    [2419200, 'weeks', 604800], // 60*60*24*7*4, 60*60*24*7
    [4838400, 'Last month', 'Next month'], // 60*60*24*7*4*2
    [29030400, 'months', 2419200], // 60*60*24*7*4*12, 60*60*24*7*4
    [58060800, 'Last year', 'Next year'], // 60*60*24*7*4*12*2
    [2903040000, 'years', 29030400], // 60*60*24*7*4*12*100, 60*60*24*7*4*12
    [5806080000, 'Last century', 'Next century'], // 60*60*24*7*4*12*100*2
    [58060800000, 'centuries', 2903040000] // 60*60*24*7*4*12*100*20, 60*60*24*7*4*12*100
  ];
  var seconds = (+new Date() - time) / 1000,
    token = 'ago',
    list_choice = 1;

  if (seconds == 0) {
    return 'Just now'
  }
  if (seconds < 0) {
    seconds = Math.abs(seconds);
    token = 'from now';
    list_choice = 2;
  }
  var i = 0,
    format;
  while (format = time_formats[i++])
    if (seconds < format[0]) {
      if (typeof format[2] == 'string')
        return format[list_choice];
      else
        return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ' + token;
    }
  return time;
}

var aDay = 24 * 60 * 60 * 1000;
console.log(time_ago(new Date(Date.now() - aDay)));
console.log(time_ago(new Date(Date.now() - aDay * 2)));
Zagreus answered 18/9, 2012 at 10:35 Comment(5)
Replace the last line return time; with format = time_formats[time_formats.length - 1]; return Math.floor(seconds / format[2]) + ' ' + format[1] + ' ' + token; to return centuries for large time spans instead of the milliseconds.Hurlyburly
Very nice! Though I noticed in iOS, when used with angular as a filter, the browser returns NaN here. This fixes it: time = +new Date(time.replace(/-/g, '/'));Jackass
Great, but the assignment in that while loop is ugly and confusing. Changing to a forEach loop would be betterSoluble
I was having an issue where I wasn't able to see 'Just now', and kept getting 0 seconds ago. So I changed the line to if (seconds < 1) { return 'Just now' } which resolved my issueNoblesse
can you explain why "while (format = time_formats[i++])"Raptor
L
60

I haven't checked (although it wouldn't be hard to), but I think that Stack Exchange sites use the jquery.timeago plugin to create these time strings.


It's quite easy to use the plugin, and it's clean and updates automatically.

Here's a quick sample (from the plugin's home page):

First, load jQuery and the plugin:

<script src="jquery.min.js" type="text/javascript"></script> <script src="jquery.timeago.js" type="text/javascript"></script>

Now, let's attach it to your timestamps on DOM ready:

jQuery(document).ready(function() {
jQuery("abbr.timeago").timeago(); });

This will turn all abbr elements with a class of timeago and an ISO 8601 timestamp in the title: <abbr class="timeago" title="2008-07-17T09:24:17Z">July 17, 2008</abbr> into something like this: <abbr class="timeago" title="July 17, 2008">about a year ago</abbr> which yields: about a year ago. As time passes, the timestamps will automatically update.

Lovieloving answered 5/7, 2010 at 7:59 Comment(2)
Not everybody uses JQuery.Watchman
Makes no sense to have this as a jquery plugin.Bowling
F
37

Yet another take on Intl.RelativeTimeFormat

  • Supports both past and future dates
  • Accepts both String and Date
  • Custom ranges are easy to add (edit ranges)
  • Can be easily translated Intl.RelativeTimeFormat('ua')

console.log(timeAgo('2021-08-09T15:29:01+0000'));

function timeAgo(input) {
  const date = (input instanceof Date) ? input : new Date(input);
  const formatter = new Intl.RelativeTimeFormat('en');
  const ranges = {
    years: 3600 * 24 * 365,
    months: 3600 * 24 * 30,
    weeks: 3600 * 24 * 7,
    days: 3600 * 24,
    hours: 3600,
    minutes: 60,
    seconds: 1
  };
  const secondsElapsed = (date.getTime() - Date.now()) / 1000;
  for (let key in ranges) {
    if (ranges[key] < Math.abs(secondsElapsed)) {
      const delta = secondsElapsed / ranges[key];
      return formatter.format(Math.round(delta), key);
    }
  }
}

https://jsfiddle.net/tv9701uf

UPDATE For TypeScript usage please also consider the following answer https://mcmap.net/q/92211/-how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-sites

Friedcake answered 9/9, 2021 at 18:4 Comment(9)
A very clear solution using modern APIs. Beautiful.Allergic
Please publish this as a npm package - including TypeScript declarations!Wiggs
Sure, will do that in my spare time, glad it came in handyFriedcake
Yep this is very clean and beautiful code ! well doneInterchange
cannot translate into myanmar (burma) . how can I do this , to support in Myanmar language. Currently I replace string like this day = ရက်Chaille
@Chaille Perhaps you could configure it as follows: new Intl.RelativeTimeFormat('my');Friedcake
I tried , when I try Kr-ko , I got Korean . But I don't get myanmar date . If they are not supported , how to contribute or changes to support this language.Chaille
Yeah, it looks like it doesn't work with Myanmar properly for some reason, I suspect it's due to browser implementation :(Friedcake
To make this typescript friendly, just add as keyof typeof ranges to cast key to the thing typescript is being an idiot about.Sparry
P
32

Here is a slight modification on Sky Sander's solution that allows the date to be input as a string and is capable of displaying spans like "1 minute" instead of "73 seconds"

var timeSince = function(date) {
  if (typeof date !== 'object') {
    date = new Date(date);
  }

  var seconds = Math.floor((new Date() - date) / 1000);
  var intervalType;

  var interval = Math.floor(seconds / 31536000);
  if (interval >= 1) {
    intervalType = 'year';
  } else {
    interval = Math.floor(seconds / 2592000);
    if (interval >= 1) {
      intervalType = 'month';
    } else {
      interval = Math.floor(seconds / 86400);
      if (interval >= 1) {
        intervalType = 'day';
      } else {
        interval = Math.floor(seconds / 3600);
        if (interval >= 1) {
          intervalType = "hour";
        } else {
          interval = Math.floor(seconds / 60);
          if (interval >= 1) {
            intervalType = "minute";
          } else {
            interval = seconds;
            intervalType = "second";
          }
        }
      }
    }
  }

  if (interval > 1 || interval === 0) {
    intervalType += 's';
  }

  return interval + ' ' + intervalType;
};
var aDay = 24 * 60 * 60 * 1000;
console.log(timeSince(new Date(Date.now() - aDay)));
console.log(timeSince(new Date(Date.now() - aDay * 2)));
Prier answered 24/4, 2014 at 3:30 Comment(7)
This doesn't work for seconds as interval is left as 0 from interval = Math.floor(seconds / 60);. I added interval = seconds; into the final else and it works fine.Glassworks
If interval is 0, you should also add the "s".Killoran
This is awesome. For TS I had to add a unary operator on let seconds = Math.floor((+new Date() - date) / 1000);Summerville
Why do you check even for interval === 0 in the last if?Mistletoe
@Mistletoe so that it will say "0 seconds" instead of "0 second"Prier
amazing! that is solidHoi
I made a small modification to allow this to work with times in the future. This goes directly below the "var seconds" line. var tense = (seconds >= 0) ? 'ago' : 'from now'; seconds = Math.abs(seconds); then at the bottom: if (Math.abs(interval) > 1 || interval === 0) { intervalType += 's'; } return interval + ' ' + intervalType + ' ' + tense;Vilberg
R
25

So here is my version, it works both with dates in the past and in the future. It uses the Intl.RelativeTimeFormat to provide localized strings, instead of hardcoded strings. You can pass dates as timestamps, Date objects or parseable date strings.

/**
 * Human readable elapsed or remaining time (example: 3 minutes ago)
 * @param  {Date|Number|String} date A Date object, timestamp or string parsable with Date.parse()
 * @param  {Date|Number|String} [nowDate] A Date object, timestamp or string parsable with Date.parse()
 * @param  {Intl.RelativeTimeFormat} [trf] A Intl formater
 * @return {string} Human readable elapsed or remaining time
 * @author github.com/victornpb
 * @see https://mcmap.net/q/92211/-how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-sites
 */
function fromNow(date, nowDate = Date.now(), rft = new Intl.RelativeTimeFormat(undefined, { numeric: "auto" })) {
    const SECOND = 1000;
    const MINUTE = 60 * SECOND;
    const HOUR = 60 * MINUTE;
    const DAY = 24 * HOUR;
    const WEEK = 7 * DAY;
    const YEAR = 365 * DAY;
    const MONTH = YEAR / 12;
    const intervals = [
        { ge: YEAR, divisor: YEAR, unit: 'year' },
        { ge: MONTH, divisor: MONTH, unit: 'month' },
        { ge: WEEK, divisor: WEEK, unit: 'week' },
        { ge: DAY, divisor: DAY, unit: 'day' },
        { ge: HOUR, divisor: HOUR, unit: 'hour' },
        { ge: MINUTE, divisor: MINUTE, unit: 'minute' },
        { ge: 30 * SECOND, divisor: SECOND, unit: 'seconds' },
        { ge: 0, divisor: 1, text: 'just now' },
    ];
    const now = typeof nowDate === 'object' ? nowDate.getTime() : new Date(nowDate).getTime();
    const diff = now - (typeof date === 'object' ? date : new Date(date)).getTime();
    const diffAbs = Math.abs(diff);
    for (const interval of intervals) {
        if (diffAbs >= interval.ge) {
            const x = Math.round(Math.abs(diff) / interval.divisor);
            const isFuture = diff < 0;
            return interval.unit ? rft.format(isFuture ? x : -x, interval.unit) : interval.text;
        }
    }
}
// examples
fromNow('2020-01-01') // 9 months ago
fromNow(161651684156) // 4 days ago
fromNow(new Date()-1) // just now
fromNow(30000 + Date.now()) // in 30 seconds
fromNow(Date.now() + (1000*60*60*24)) // in 1 day
fromNow(new Date('2029-12-01Z00:00:00.000')) // in 9 years

Alternative that doesn't use Intl.RelativeTimeFormat

/**
 * Human readable elapsed or remaining time (example: 3 minutes ago)
 * @param  {Date|Number|String} date A Date object, timestamp or string parsable with Date.parse()
 * @return {string} Human readable elapsed or remaining time
 * @author github.com/victornpb
 * @see https://mcmap.net/q/92211/-how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-sites
 */
function fromNow(date) {
    const SECOND = 1000;
    const MINUTE = 60 * SECOND;
    const HOUR = 60 * MINUTE;
    const DAY = 24 * HOUR;
    const WEEK = 7 * DAY;
    const YEAR = 365 * DAY;
    const MONTH = YEAR / 12;
    const units = [
        { max: 30 * SECOND, divisor: 1, past1: 'just now', pastN: 'just now', future1: 'just now', futureN: 'just now' },
        { max: MINUTE, divisor: SECOND, past1: 'a second ago', pastN: '# seconds ago', future1: 'in a second', futureN: 'in # seconds' },
        { max: HOUR, divisor: MINUTE, past1: 'a minute ago', pastN: '# minutes ago', future1: 'in a minute', futureN: 'in # minutes' },
        { max: DAY, divisor: HOUR, past1: 'an hour ago', pastN: '# hours ago', future1: 'in an hour', futureN: 'in # hours' },
        { max: WEEK, divisor: DAY, past1: 'yesterday', pastN: '# days ago', future1: 'tomorrow', futureN: 'in # days' },
        { max: 4 * WEEK, divisor: WEEK, past1: 'last week', pastN: '# weeks ago', future1: 'in a week', futureN: 'in # weeks' },
        { max: YEAR, divisor: MONTH, past1: 'last month', pastN: '# months ago', future1: 'in a month', futureN: 'in # months' },
        { max: 100 * YEAR, divisor: YEAR, past1: 'last year', pastN: '# years ago', future1: 'in a year', futureN: 'in # years' },
        { max: 1000 * YEAR, divisor: 100 * YEAR, past1: 'last century', pastN: '# centuries ago', future1: 'in a century', futureN: 'in # centuries' },
        { max: Infinity, divisor: 1000 * YEAR, past1: 'last millennium', pastN: '# millennia ago', future1: 'in a millennium', futureN: 'in # millennia' },
    ];
    const diff = Date.now() - (typeof date === 'object' ? date : new Date(date)).getTime();
    const diffAbs = Math.abs(diff);
    for (const unit of units) {
        if (diffAbs < unit.max) {
            const isFuture = diff < 0;
            const x = Math.round(Math.abs(diff) / unit.divisor);
            if (x <= 1) return isFuture ? unit.future1 : unit.past1;
            return (isFuture ? unit.futureN : unit.pastN).replace('#', x);
        }
    }
};
Resonator answered 30/4, 2021 at 17:21 Comment(2)
Your version has a couple of validation errors. Try it in typescriptlang.org/play to validate it.Itinerate
I just thought after putting so much work in it you would like to make it pass ESLint ;)Itinerate
Q
20

A shorter version as used by Lokely:

const intervals = [
  { label: 'year', seconds: 31536000 },
  { label: 'month', seconds: 2592000 },
  { label: 'day', seconds: 86400 },
  { label: 'hour', seconds: 3600 },
  { label: 'minute', seconds: 60 },
  { label: 'second', seconds: 1 }
];

function timeSince(date) {
  const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
  const interval = intervals.find(i => i.seconds < seconds);
  const count = Math.floor(seconds / interval.seconds);
  return `${count} ${interval.label}${count !== 1 ? 's' : ''} ago`;
}
Quarantine answered 29/10, 2017 at 23:40 Comment(3)
The shortest interval has a duration of zero seconds - this results in a division by zero error.Maternity
@Maternity is right. <60 seconds it prints Infinity seconds agoLachellelaches
doesnt' handle future dates.Uncivil
O
15

You might want to look at humanized_time_span: https://github.com/layam/js_humanized_time_span

It's framework agnostic and fully customizable.

Just download / include the script and then you can do this:

humanized_time_span("2011-05-11 12:00:00")  
   => '3 hours ago'

humanized_time_span("2011-05-11 12:00:00", "2011-05-11 16:00:00)  
   => '4 hours ago'

or even this:

var custom_date_formats = {
  past: [
    { ceiling: 60, text: "less than a minute ago" },
    { ceiling: 86400, text: "$hours hours, $minutes minutes and $seconds seconds ago" },
    { ceiling: null, text: "$years years ago" }
  ],
  future: [
    { ceiling: 60, text: "in less than a minute" },
    { ceiling: 86400, text: "in $hours hours, $minutes minutes and $seconds seconds time" },
    { ceiling: null, text: "in $years years" }
  ]
}

humanized_time_span("2010/09/10 10:00:00", "2010/09/10 10:00:05", custom_date_formats) 
  => "less than a minute ago"

Read the docs for more info.

Olenolin answered 11/5, 2011 at 14:30 Comment(4)
Just means that it doesn't rely on jQuery or even having a DOM.Olenolin
It gives me NaN years ago why??Watchman
darn it I got it... your example of its use is wrong. you actually delimit first numbers with slash rather than "-".. like this humanized_time_span("2011/05/11 12:00:00")Watchman
it might depend on your local culture and differ between users :)Odyl
S
15

Changed the function above to

function timeSince(date) {

    var seconds = Math.floor(((new Date().getTime()/1000) - date)),
    interval = Math.floor(seconds / 31536000);

    if (interval > 1) return interval + "y";

    interval = Math.floor(seconds / 2592000);
    if (interval > 1) return interval + "m";

    interval = Math.floor(seconds / 86400);
    if (interval >= 1) return interval + "d";

    interval = Math.floor(seconds / 3600);
    if (interval >= 1) return interval + "h";

    interval = Math.floor(seconds / 60);
    if (interval > 1) return interval + "m ";

    return Math.floor(seconds) + "s";
}

Otherwise it would show things like "75 minutes" (between 1 and 2 hours). It also now assumes input date is a Unix timestamp.

Sarad answered 23/11, 2011 at 18:13 Comment(2)
Divide date by 1000 please.Watchman
I used this where data came from a database with Unix timestamps in seconds. When it is in milliseconds you do need to divide by 1000.Sarad
P
15

An ES6 version of the code provided by @user1012181:

const epochs = [
    ['year', 31536000],
    ['month', 2592000],
    ['day', 86400],
    ['hour', 3600],
    ['minute', 60],
    ['second', 1]
];

const getDuration = (timeAgoInSeconds) => {
    for (let [name, seconds] of epochs) {
        const interval = Math.floor(timeAgoInSeconds / seconds);
        if (interval >= 1) {
            return {
                interval: interval,
                epoch: name
            };
        }
    }
};

const timeAgo = (date) => {
    const timeAgoInSeconds = Math.floor((new Date() - new Date(date)) / 1000);
    const {interval, epoch} = getDuration(timeAgoInSeconds);
    const suffix = interval === 1 ? '' : 's';
    return `${interval} ${epoch}${suffix} ago`;
};

Edited with @ibe-vanmeenen suggestions. (Thanks!)

Papeete answered 2/11, 2015 at 21:28 Comment(3)
You should also include "second: 1" in EPOCHS, otherwise it will break if it's less than 1 minute ago :). The last 3 vars could also be a constant no?Yawmeter
Also, EPOCHS should be an array, as objects do not guarantee properties order. I've stored my changes in gist.github.com/IbeVanmeenen/4e3e58820c9168806e57530563612886. You're welcome to copy them to edit this answer :)Yawmeter
It will also break if you use current date/time e.g. Date.now() since the interval will result to undefined. I recommend to add support for "just now".Boulder
D
11

Much readable and cross browser compatible code:

As given by @Travis

var DURATION_IN_SECONDS = {
  epochs: ['year', 'month', 'day', 'hour', 'minute'],
  year: 31536000,
  month: 2592000,
  day: 86400,
  hour: 3600,
  minute: 60
};

function getDuration(seconds) {
  var epoch, interval;

  for (var i = 0; i < DURATION_IN_SECONDS.epochs.length; i++) {
    epoch = DURATION_IN_SECONDS.epochs[i];
    interval = Math.floor(seconds / DURATION_IN_SECONDS[epoch]);
    if (interval >= 1) {
      return {
        interval: interval,
        epoch: epoch
      };
    }
  }

};

function timeSince(date) {
  var seconds = Math.floor((new Date() - new Date(date)) / 1000);
  var duration = getDuration(seconds);
  var suffix = (duration.interval > 1 || duration.interval === 0) ? 's' : '';
  return duration.interval + ' ' + duration.epoch + suffix;
};

alert(timeSince('2015-09-17T18:53:23'));
Dialectologist answered 17/9, 2015 at 19:10 Comment(1)
Note that this makes some wrongful assumptions, such as every day being 86,400 seconds (unless time zone is set to UTC, this isn't always true thanks to UTC)Tedious
M
9

Simple and readable version:

const relativeTimePeriods = [
    [31536000, 'year'],
    [2419200, 'month'],
    [604800, 'week'],
    [86400, 'day'],
    [3600, 'hour'],
    [60, 'minute'],
    [1, 'second']
];

function relativeTime(date, isUtc=true) {
    if (!(date instanceof Date)) date = new Date(date * 1000);
    const seconds = (new Date() - date) / 1000;
    for (let [secondsPer, name] of relativeTimePeriods) {
        if (seconds >= secondsPer) {
            const amount = Math.floor(seconds / secondsPer);
            return `${amount} ${name}${amount ? 's' : ''}s ago`;
        }
    }
    return 'Just now';
}
Mesencephalon answered 15/10, 2018 at 6:28 Comment(1)
This works well but there is a logical error I had to correct. I changed this return ${amount} ${name}${amount ? 's' : ''}s ago; to return ${amount} ${name}${amount >1 ? 's' : ''} ago; to avoid returning two 's' in front of plural names and displaying the singular without any 's'Concussion
B
8

from now, unix timestamp param,

function timeSince(ts){
    now = new Date();
    ts = new Date(ts*1000);
    var delta = now.getTime() - ts.getTime();

    delta = delta/1000; //us to s

    var ps, pm, ph, pd, min, hou, sec, days;

    if(delta<=59){
        ps = (delta>1) ? "s": "";
        return delta+" second"+ps
    }

    if(delta>=60 && delta<=3599){
        min = Math.floor(delta/60);
        sec = delta-(min*60);
        pm = (min>1) ? "s": "";
        ps = (sec>1) ? "s": "";
        return min+" minute"+pm+" "+sec+" second"+ps;
    }

    if(delta>=3600 && delta<=86399){
        hou = Math.floor(delta/3600);
        min = Math.floor((delta-(hou*3600))/60);
        ph = (hou>1) ? "s": "";
        pm = (min>1) ? "s": "";
        return hou+" hour"+ph+" "+min+" minute"+pm;
    } 

    if(delta>=86400){
        days = Math.floor(delta/86400);
        hou =  Math.floor((delta-(days*86400))/60/60);
        pd = (days>1) ? "s": "";
        ph = (hou>1) ? "s": "";
        return days+" day"+pd+" "+hou+" hour"+ph;
    }

}
Bufford answered 10/11, 2012 at 15:3 Comment(0)
M
7

This should properly handle any valid timestamp, including Date.now(), singular units, and future dates. I left out months, but those should be easy to add in. I tried to keep it readable as possible.

function getTimeInterval(date) {
  let seconds = Math.floor((Date.now() - date) / 1000);
  let unit = "second";
  let direction = "ago";
  if (seconds < 0) {
    seconds = -seconds;
    direction = "from now";
  }
  let value = seconds;
  if (seconds >= 31536000) {
    value = Math.floor(seconds / 31536000);
    unit = "year";
  } else if (seconds >= 86400) {
    value = Math.floor(seconds / 86400);
    unit = "day";
  } else if (seconds >= 3600) {
    value = Math.floor(seconds / 3600);
    unit = "hour";
  } else if (seconds >= 60) {
    value = Math.floor(seconds / 60);
    unit = "minute";
  }
  if (value != 1)
    unit = unit + "s";
  return value + " " + unit + " " + direction;
}

console.log(getTimeInterval(Date.now())); // 0 seconds ago
console.log(getTimeInterval(Date.now() + 1000)); // 1 second from now
console.log(getTimeInterval(Date.now() - 1000)); // 1 second ago
console.log(getTimeInterval(Date.now() + 60000)); // 1 minute from now
console.log(getTimeInterval(Date.now() - 120000)); // 2 minutes ago
console.log(getTimeInterval(Date.now() + 120000)); // 2 minutes from now
console.log(getTimeInterval(Date.now() + 3600000)); // 1 hour from now
console.log(getTimeInterval(Date.now() + 360000000000)); // 11 years from now
console.log(getTimeInterval(0)); // 49 years ago
Monochord answered 22/11, 2019 at 23:58 Comment(0)
D
6

Although the question was asked quite long time ago, writing this answer with hope it will help somebody.

Pass the date you want to start to count from. Using moment().fromNow() of momentjs: (See more information here)

getRelativeTime(date) {
    const d = new Date(date * 1000);
    return moment(d).fromNow();
}

If you want to change information provided for dates fromNow you write your custom relative time for moment.

For example, in my own case I wanted to print 'one month ago' instead of 'a month ago' (provided by moment(d).fromNow()). In this case, you can write something given below.

moment.updateLocale('en', {
    relativeTime: {
        future: 'in %s',
        past: '%s ago',
        s: 'a few seconds',
        ss: '%d seconds',
        m: '1 m',
        mm: '%d minutes',
        h: '1 h',
        hh: '%d hours',
        d: '1 d',
        dd: '%d days',
        M: '1 month',
        MM: '%d months',
        y: '1 y',
        yy: '%d years'
    }
});

NOTE: I wrote my code for project in Angular 6

Deflower answered 10/9, 2018 at 10:52 Comment(1)
@Nima, delighted if it was helpful :))Deflower
A
6

Can also use the dayjs relativeTime plugin to solve this.

import * as dayjs from 'dayjs';
import * as relativeTime from 'dayjs/plugin/relativeTime';

dayjs.extend(relativeTime);
dayjs(dayjs('1990')).fromNow(); // x years ago
Apprehend answered 2/7, 2019 at 18:35 Comment(0)
K
4
function dateToHowManyAgo(stringDate){
    var currDate = new Date();
    var diffMs=currDate.getTime() - new Date(stringDate).getTime();
    var sec=diffMs/1000;
    if(sec<60)
        return parseInt(sec)+' second'+(parseInt(sec)>1?'s':'')+' ago';
    var min=sec/60;
    if(min<60)
        return parseInt(min)+' minute'+(parseInt(min)>1?'s':'')+' ago';
    var h=min/60;
    if(h<24)
        return parseInt(h)+' hour'+(parseInt(h)>1?'s':'')+' ago';
    var d=h/24;
    if(d<30)
        return parseInt(d)+' day'+(parseInt(d)>1?'s':'')+' ago';
    var m=d/30;
    if(m<12)
        return parseInt(m)+' month'+(parseInt(m)>1?'s':'')+' ago';
    var y=m/12;
    return parseInt(y)+' year'+(parseInt(y)>1?'s':'')+' ago';
}
console.log(dateToHowManyAgo('2019-11-07 19:17:06'));
Kleiman answered 7/11, 2019 at 19:50 Comment(0)
P
4

Most of these answers fail to account for plurals (eg. "1 minutes ago" when we want "1 minute ago")

const MINUTE = 60;
const HOUR = MINUTE * 60;
const DAY = HOUR * 24;
const WEEK = DAY * 7;
const MONTH = DAY * 30;
const YEAR = DAY * 365;

function getTimeAgo(date) {
  const secondsAgo = Math.round((Date.now() - Number(date)) / 1000);

  if (secondsAgo < MINUTE) {
    return secondsAgo + ` second${secondsAgo !== 1 ? "s" : ""} ago`;
  }

  let divisor;
  let unit = "";

  if (secondsAgo < HOUR) {
    [divisor, unit] = [MINUTE, "minute"];
  } else if (secondsAgo < DAY) {
    [divisor, unit] = [HOUR, "hour"];
  } else if (secondsAgo < WEEK) {
    [divisor, unit] = [DAY, "day"];
  } else if (secondsAgo < MONTH) {
    [divisor, unit] = [WEEK, "week"];
  } else if (secondsAgo < YEAR) {
    [divisor, unit] = [MONTH, "month"];
  } else {
    [divisor, unit] = [YEAR, "year"];
  }

  const count = Math.floor(secondsAgo / divisor);
  return `${count} ${unit}${count > 1 ? "s" : ""} ago`;
}

Then you can use it like so:

const date = new Date();
console.log(getTimeAgo(date));
// 1 second ago
// 2 seconds ago
// 1 minute ago
// 2 minutes ago
// ...
Protozoology answered 13/7, 2022 at 22:2 Comment(0)
H
4

I used an old answer by Possible 11 and I added Intl.RelativeTimeFormat for translations.

https://mcmap.net/q/92211/-how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-sites

function timeAgo (value) {
    const seconds = Math.floor((new Date().getTime() - new Date(value).getTime()) / 1000)
    let interval = seconds / 31536000
    const rtf = new Intl.RelativeTimeFormat("en", { numeric: 'auto' })
    if (interval > 1) { return rtf.format(-Math.floor(interval), 'year') }
    interval = seconds / 2592000
    if (interval > 1) { return rtf.format(-Math.floor(interval), 'month') }
    interval = seconds / 86400
    if (interval > 1) { return rtf.format(-Math.floor(interval), 'day') }
    interval = seconds / 3600
    if (interval > 1) { return rtf.format(-Math.floor(interval), 'hour') }
    interval = seconds / 60
    if (interval > 1) { return rtf.format(-Math.floor(interval), 'minute') }
    return rtf.format(-Math.floor(interval), 'second')
}

console.log(timeAgo('2022-08-12 20:50:20'))
Homoeo answered 14/12, 2022 at 11:32 Comment(0)
N
3

I write one with js and python, used in two projects, very nice and simple: a simple library (less then 2kb) used to format date with *** time ago statement.

simple, small, easy used, and well tested.

  1. npm install timeago.js

  2. import timeago from 'timeago.js'; // or use script tag

  3. use api format.

Sample:

var timeagoIns  = timeago();
timeagoIns .format('2016-06-12');

Also you can render in real-time.

var timeagoIns = timeago();
timeagoIns.render(document.querySelectorAll('time'));
Ng answered 24/6, 2016 at 12:52 Comment(1)
As of 4.0 you can use a destructured import instead: import { format, render, cancel, register } from 'timeago.js';Filmdom
C
3

Replying to @Stas Parshin answer, it is best answer here with less code, but it has bug when using with typescript, the .format function of Intl takes 2 inputs

  1. number,

  2. Units - i.e of type 'RelativeTimeFormatUnit' so if you pass a object key typescript will through error saying unit must be of type RelativeTimeFormatUnit and not of type string, so the work-around for this is to use the type to make another list of same type and rest you can have look at code... Happy coding.

    function timeAgo(input) {
     const date = (input instanceof Date) ? input : new Date(input);
     const formatter = new Intl.RelativeTimeFormat('en');
     const ranges = [
       ['years', 3600 * 24 * 365],
       ['months', 3600 * 24 * 30],
       ['weeks', 3600 * 24 * 7],
       ['days', 3600 * 24],
       ['hours', 3600],
       ['minutes', 60],
       ['seconds', 1],
     ] as const;
     const secondsElapsed = (date.getTime() - Date.now()) / 1000;
    
     for (const [rangeType, rangeVal] of ranges) {
       if (rangeVal < Math.abs(secondsElapsed)) {
         const delta = secondsElapsed / rangeVal;
         return formatter.format(Math.round(delta), rangeType);
       }
     }}
    
     console.log(timeAgo('2021-08-09T15:29:01+0000'));
    
Californium answered 30/6, 2022 at 14:13 Comment(2)
Nice! Let me link your answer in the original one so people are aware of thisFriedcake
Instead of this craziness, just use this https://mcmap.net/q/92211/-how-to-format-time-since-xxx-e-g-4-minutes-ago-similar-to-stack-exchange-sites, but after key write as keyof typeof ranges ... The "workaround" above is just a pointless waste of space and time...Sparry
C
2

I have modified Sky Sanders' version. The Math.floor(...) operations are evaluated in the if block

       var timeSince = function(date) {
            var seconds = Math.floor((new Date() - date) / 1000);
            var months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
            if (seconds < 5){
                return "just now";
            }else if (seconds < 60){
                return seconds + " seconds ago";
            }
            else if (seconds < 3600) {
                minutes = Math.floor(seconds/60)
                if(minutes > 1)
                    return minutes + " minutes ago";
                else
                    return "1 minute ago";
            }
            else if (seconds < 86400) {
                hours = Math.floor(seconds/3600)
                if(hours > 1)
                    return hours + " hours ago";
                else
                    return "1 hour ago";
            }
            //2 days and no more
            else if (seconds < 172800) {
                days = Math.floor(seconds/86400)
                if(days > 1)
                    return days + " days ago";
                else
                    return "1 day ago";
            }
            else{

                //return new Date(time).toLocaleDateString();
                return date.getDate().toString() + " " + months[date.getMonth()] + ", " + date.getFullYear();
            }
        }
Cruise answered 20/1, 2016 at 2:2 Comment(1)
there is a typo in the last else if return days + "1 day ago"; should be return "1 day ago";Malay
I
2
function timeago(date) {
    var seconds = Math.floor((new Date() - date) / 1000);
    if(Math.round(seconds/(60*60*24*365.25)) >= 2) return Math.round(seconds/(60*60*24*365.25)) + " years ago";
    else if(Math.round(seconds/(60*60*24*365.25)) >= 1) return "1 year ago";
    else if(Math.round(seconds/(60*60*24*30.4)) >= 2) return Math.round(seconds/(60*60*24*30.4)) + " months ago";
    else if(Math.round(seconds/(60*60*24*30.4)) >= 1) return "1 month ago";
    else if(Math.round(seconds/(60*60*24*7)) >= 2) return Math.round(seconds/(60*60*24*7)) + " weeks ago";
    else if(Math.round(seconds/(60*60*24*7)) >= 1) return "1 week ago";
    else if(Math.round(seconds/(60*60*24)) >= 2) return Math.round(seconds/(60*60*24)) + " days ago";
    else if(Math.round(seconds/(60*60*24)) >= 1) return "1 day ago";
    else if(Math.round(seconds/(60*60)) >= 2) return Math.round(seconds/(60*60)) + " hours ago";
    else if(Math.round(seconds/(60*60)) >= 1) return "1 hour ago";
    else if(Math.round(seconds/60) >= 2) return Math.round(seconds/60) + " minutes ago";
    else if(Math.round(seconds/60) >= 1) return "1 minute ago";
    else if(seconds >= 2)return seconds + " seconds ago";
    else return seconds + "1 second ago";
}
Infuse answered 1/4, 2017 at 19:16 Comment(0)
C
2

My stab at this based on other answers.

function timeSince(date) {
    let minute = 60;
    let hour   = minute * 60;
    let day    = hour   * 24;
    let month  = day    * 30;
    let year   = day    * 365;

    let suffix = ' ago';

    let elapsed = Math.floor((Date.now() - date) / 1000);

    if (elapsed < minute) {
        return 'just now';
    }

    // get an array in the form of [number, string]
    let a = elapsed < hour  && [Math.floor(elapsed / minute), 'minute'] ||
            elapsed < day   && [Math.floor(elapsed / hour), 'hour']     ||
            elapsed < month && [Math.floor(elapsed / day), 'day']       ||
            elapsed < year  && [Math.floor(elapsed / month), 'month']   ||
            [Math.floor(elapsed / year), 'year'];

    // pluralise and append suffix
    return a[0] + ' ' + a[1] + (a[0] === 1 ? '' : 's') + suffix;
}
Channing answered 13/10, 2019 at 12:1 Comment(0)
S
2

Answering 10 years old question to help the newcomers.

We can use this package for that javascript-time-ago

 
// Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en'
 
// Add locale-specific relative date/time formatting rules.
TimeAgo.addLocale(en)
 
// Create relative date/time formatter.
const timeAgo = new TimeAgo('en-US')
 
timeAgo.format(new Date())
// "just now"
 
timeAgo.format(Date.now() - 60 * 1000)
// "a minute ago"
 
timeAgo.format(Date.now() - 2 * 60 * 60 * 1000)
// "2 hours ago"
 
timeAgo.format(Date.now() - 24 * 60 * 60 * 1000)
// "a day ago"
Sipes answered 10/9, 2020 at 5:52 Comment(0)
N
2
const createdAt = moment(created_at).fromNow()

and a customized solution


        const duration = moment.duration(moment().diff(moment(created_at)))
        const createdAt = duration.as('week') >= 1
          ? `${Math.floor(duration.as('week'))} week(s)`
          : duration.as('day') >= 1
            ? `${Math.floor(duration.as('day'))} day(s)`
            : duration.as('hour') >= 1
              ? `${Math.floor(duration.as('hour'))} hour(s)`
              : `${Math.floor(duration.as('minute'))} minute(s)`

Nath answered 18/11, 2021 at 7:12 Comment(0)
H
1

My solution..

(function(global){
            const SECOND   = 1;
            const MINUTE   = 60;
            const HOUR     = 3600;
            const DAY      = 86400;
            const MONTH    = 2629746;
            const YEAR     = 31556952;
            const DECADE   = 315569520;

            global.timeAgo = function(date){
                var now = new Date();
                var diff = Math.round(( now - date ) / 1000);

                var unit = '';
                var num = 0;
                var plural = false;

                switch(true){
                    case diff <= 0:
                        return 'just now';
                    break;

                    case diff < MINUTE:
                        num = Math.round(diff / SECOND);
                        unit = 'sec';
                        plural = num > 1;
                    break;

                    case diff < HOUR:
                        num = Math.round(diff / MINUTE);
                        unit = 'min';
                        plural = num > 1;
                    break;

                    case diff < DAY:
                        num = Math.round(diff / HOUR);
                        unit = 'hour';
                        plural = num > 1;
                    break;

                    case diff < MONTH:
                        num = Math.round(diff / DAY);
                        unit = 'day';
                        plural = num > 1;
                    break;

                    case diff < YEAR:
                        num = Math.round(diff / MONTH);
                        unit = 'month';
                        plural = num > 1;
                    break;

                    case diff < DECADE:
                        num = Math.round(diff / YEAR);
                        unit = 'year';
                        plural = num > 1;
                    break;

                    default:
                        num = Math.round(diff / YEAR);
                        unit = 'year';
                        plural = num > 1;
                }

                var str = '';
                if(num){
                    str += `${num} `;
                }

                str += `${unit}`;

                if(plural){
                    str += 's';
                }

                str += ' ago';

                return str;
            }
        })(window);

        console.log(timeAgo(new Date()));
        console.log(timeAgo(new Date('Jun 03 2018 15:12:19 GMT+0300 (FLE Daylight Time)')));
        console.log(timeAgo(new Date('Jun 03 2018 13:12:19 GMT+0300 (FLE Daylight Time)')));
        console.log(timeAgo(new Date('May 28 2018 13:12:19 GMT+0300 (FLE Daylight Time)')));
        console.log(timeAgo(new Date('May 28 2017 13:12:19 GMT+0300 (FLE Daylight Time)')));
        console.log(timeAgo(new Date('May 28 2000 13:12:19 GMT+0300 (FLE Daylight Time)')));
        console.log(timeAgo(new Date('Sep 10 1994 13:12:19 GMT+0300 (FLE Daylight Time)')));
Hypersonic answered 3/6, 2018 at 12:26 Comment(0)
C
1
 I achieve this by following method

   timeAgo = (date) => {
            var ms = (new Date()).getTime() - date.getTime();
            var seconds = Math.floor(ms / 1000);
            var minutes = Math.floor(seconds / 60);
        var hours = Math.floor(minutes / 60);
        var days = Math.floor(hours / 24);
        var months = Math.floor(days / 30);
        var years = Math.floor(months / 12);
    
        if (ms === 0) {
            return 'Just now';
        } if (seconds < 60) {
            return seconds + ' seconds Ago';
        } if (minutes < 60) {
            return minutes + ' minutes Ago';
        } if (hours < 24) {
            return hours + ' hours Ago';
        } if (days < 30) {
            return days + ' days Ago';
        } if (months < 12) {
            return months + ' months Ago';
        } else {
            return years + ' years Ago';
        }
    
    }
    
        console.log(timeAgo(new Date()));
        console.log(timeAgo(new Date('Jun 27 2020 10:12:19')));
        console.log(timeAgo(new Date('Jun 27 2020 00:12:19')));
        console.log(timeAgo(new Date('May 28 2020 13:12:19')));
        console.log(timeAgo(new Date('May 28 2017 13:12:19')));
Crossbench answered 12/7, 2020 at 1:9 Comment(1)
How does this improve upon any of the existing answers, including Sky Sanders' from ten years ago?Heindrick
L
1

Here is a bit simplified version of @sky-sanders answer.

function timeSince(date) {

  var seconds = Math.floor((new Date() - date) / 1000);
  var divisors = [31536000, 2592000, 86400, 3600, 60, 1]
  var description = ["years", "months", "days", "hours", "minutes", "seconds"]
  var result = [];

  var interval = seconds;

  for (i = 0; i < divisors.length; i++) {
    interval = Math.floor(seconds / divisors[i])
    if (interval > 1) {
      result.push(interval + " " + description[i])
    }
    seconds -= interval * divisors[i]
  }

  return result.join(" ")
}
Leontineleontyne answered 10/3, 2021 at 20:46 Comment(0)
V
1

If you're already using date-fns, you can use the built-in formatDistance (previously distanceInWords):

const date1 = new Date(2014, 6, 2);
const date2 = new Date(2015, 0, 1);
const options = { addSuffix: true }
const result = formatDistance(date1, date2, options);
//=> '6 months ago'
Visor answered 26/7, 2022 at 22:59 Comment(0)
R
0

I was looking for an answer to this and almost implemented one of these solutions, but a colleague reminded me to check the react-intl library since we were already using it.

So adding to the solutions...in the case you are using the react-intl library, they have a <FormattedRelative> component for this.

https://github.com/yahoo/react-intl/wiki/Components#formattedrelative

Redbud answered 13/7, 2016 at 22:11 Comment(0)
R
0

Here's what I did (the object returns the unit of time along with its value):

function timeSince(post_date, reference)
{
	var reference = reference ? new Date(reference) : new Date(),
		diff = reference - new Date(post_date + ' GMT-0000'),
		date = new Date(diff),
		object = { unit: null, value: null };
	
	if (diff < 86400000)
	{
		var secs  = date.getSeconds(),
			mins  = date.getMinutes(),
			hours = date.getHours(),
			array = [ ['second', secs], ['minute', mins], ['hour', hours] ];
	}
	else
	{
		var days   = date.getDate(),
			weeks  = Math.floor(days / 7),
			months = date.getMonth(),
			years  = date.getFullYear() - 1970,
			array  = [ ['day', days], ['week', weeks], ['month', months], ['year', years] ];
	}

	for (var i = 0; i < array.length; i++)
	{
		array[i][0] += array[i][1] != 1 ? 's' : '';

		object.unit  = array[i][1] >= 1 ? array[i][0] : object.unit;
		object.value = array[i][1] >= 1 ? array[i][1] : object.value;
	}

	return object;
}
Recompense answered 16/2, 2017 at 2:56 Comment(0)
P
0
function calDateAgo(dString=null){
    //var dString = "2021-04-1 12:00:00";
     
    var d1 = new Date(dString);
    var d2 = new Date();
    var t2 = d2.getTime();
    var t1 = d1.getTime();
    var d1Y = d1.getFullYear();
    var d2Y = d2.getFullYear();
    var d1M = d1.getMonth();
    var d2M = d2.getMonth();
     
    var time_obj = {};
    time_obj.year = d2.getFullYear()-d1.getFullYear();
    time_obj.month = (d2M+12*d2Y)-(d1M+12*d1Y);
    time_obj.week = parseInt((t2-t1)/(24*3600*1000*7));
    time_obj.day = parseInt((t2-t1)/(24*3600*1000));
    time_obj.hour = parseInt((t2-t1)/(3600*1000));
    time_obj.minute = parseInt((t2-t1)/(60*1000));
    time_obj.second = parseInt((t2-t1)/(1000));

    for (const obj_key in time_obj) {
        if(time_obj[obj_key] == 0){
            delete time_obj[obj_key];
        }
    }
    var ago_text = 'just now';

    if(typeof Object.keys(time_obj)[0] != 'undefined'){
        var time_key = Object.keys(time_obj)[0];
        var time_val = time_obj[Object.keys(time_obj)[0]];
        time_key += (time_val > 1) ? 's':'';
        ago_text = time_val+' '+time_key+' ago'; 
    }
    
    return ago_text;
}
Patisserie answered 1/4, 2021 at 8:41 Comment(0)
M
0
function mdiv(dividend, divisor) {
    return [ Math.floor(dividend/divisor), dividend % divisor ];
}
// pass in milliseconds, gained by Date1.getTime() - Date2.getTime()
// if max_units is two, the result will be, for example
// 2years 12months ago, or 2hours 38minutes ago
// return formated period

function readable_period(ms, max_units=2){
    let [yy, yr] = mdiv(ms, 3.154e10);
    let [mm, mr] = mdiv(yr, 2.628e9);
    let [dd, dr] = mdiv(mr, 8.64e7);
    let [hh, hr] = mdiv(dr, 3.6e6);
    let [tt, ss] = mdiv(hr, 6e4);

    var ymdht = ['year', 'month', 'day', 'hour', 'minute'];
    let res = [];
    [yy, mm, dd, hh, tt].forEach((tis, ii)=>{
        if(res.length === max_units){return};
        if(tis !== 0){
            res.push(tis === 1 ? `${tis}${ymdht[ii]}` : `${tis}${ymdht[ii]}s`);
        }
    });
    return res.length === 0 ? '' : res.join(' ') + ' ago';
}
Malignant answered 28/10, 2021 at 12:6 Comment(0)
A
0

You can use Luxon:

DateTime.toRelative()

https://moment.github.io/luxon/api-docs/index.html#datetimetorelative

Here are some more examples:

const DateTime = luxon.DateTime;

// toRelative
console.log(DateTime.now().minus({ seconds: 1 }).toRelative()) // 1 second ago
console.log(DateTime.now().minus({ seconds: 10 }).toRelative()) // 10 seconds ago
console.log(DateTime.now().minus({ days: 2 }).toRelative())
console.log(DateTime.now().minus({ years: 3 }).toRelative())
console.log(DateTime.now().plus({ years: 3 }).toRelative())
console.log(DateTime.now().plus({ days: 2 }).toRelative())
console.log(DateTime.now().plus({ seconds: 1 }).toRelative())
console.log(DateTime.now().plus({ seconds: 10 }).toRelative())

// toRelativeCalendar
console.log(DateTime.now().minus({ seconds: 1 }).toRelativeCalendar()) // today
console.log(DateTime.now().minus({ seconds: 10 }).toRelativeCalendar()) // today
console.log(DateTime.now().minus({ days: 2 }).toRelativeCalendar())
console.log(DateTime.now().minus({ years: 3 }).toRelativeCalendar())
console.log(DateTime.now().plus({ years: 3 }).toRelativeCalendar())
console.log(DateTime.now().plus({ days: 2 }).toRelativeCalendar())
console.log(DateTime.now().plus({ seconds: 1 }).toRelativeCalendar())
console.log(DateTime.now().plus({ seconds: 10 }).toRelativeCalendar())
<script src="https://moment.github.io/luxon/global/luxon.min.js"></script>
Accidental answered 25/5, 2022 at 11:27 Comment(0)
A
0

I use this package: javascript-time-ago

  • set TimeAgo

     import TimeAgo from 'javascript-time-ago'
     import en from 'javascript-time-ago/locale/en.json'
    
     TimeAgo.addDefaultLocale(en)
    
  • write a function // twitter-now is twitter style. check docs for more options

    const clockToDateString = (timestamp) =>
      timeAgo.format(new Date(timestamp.toNumber() * 1000), 'twitter-now')
    
  • use it in dom

       <div >
          {clockToDateString(post.postTime)}
        </div>
    
Analcite answered 30/5, 2022 at 15:27 Comment(0)
S
0

to use this, just LITERALLY COPY ALL of this code & just import it in your component or where ever & just place in your ISOstring() date inside: showTimeAgo("2022-06-20T13:42:29-05:00") & you will get automatic time updates for every scenario.

sidenote: I made an npm package for this https://www.npmjs.com/package/showtimeago

export const showTimeAgo = () => {
    const MONTH_NAMES = [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December',
    ];

    function getOrdinalNum() {
        return (
            n +
            (n > 0
                ? ['th', 'st', 'nd', 'rd'][
                      (n > 3 && n < 21) || n % 10 > 3 ? 0 : n % 10
                  ]
                : '')
        );
    }

    function getFormattedDate(
        date,
        preformattedDate = false,
        hideYear = false
    ) {
        const day = date.getDate();
        const month = MONTH_NAMES[date.getMonth()];
        const year = date.getFullYear();
        let hours = date.getHours();
        let minutes = date.getMinutes();

        let ampm = hours >= 12 ? 'pm' : 'am';

        switch(true){
            case (hours > 12):
                hours = hours - 12;
                break;
            case (hours === 0):
                hours = 12;
                break;
            case(minutes < 10):
                minutes = `0${minutes}`;
                break;
            case(preformattedDate):
            // Today at 10:20am
            // Yesterday at 10:20am
                return `${preformattedDate} at ${hours}:${minutes} ${ampm}`;

            case(hideYear):
                // January 10th at 10:20pm
                return `${month} ${getOrdinalNum(
                    day
                )}, at ${hours}:${minutes} ${ampm}`;
            default:
                // January 10th 2022 at 10:20pm
                return `${month} ${getOrdinalNum(
                    day
                )}, ${year} at ${hours}:${minutes} ${ampm}`;
        }
        
    }

    // --- Main function
    function timeAgo(dateParam) {
        if (!dateParam) {
            return null;
        }

        const date =
            typeof dateParam === 'object' ? dateParam : new Date(dateParam);
        const DAY_IN_MS = 86400000; // 24 * 60 * 60 * 1000
        const today = new Date();

        const yesterday = new Date(today - DAY_IN_MS);

        const seconds = Math.round((today - date) / 1000);
        const minutes = Math.round(seconds / 60);
        const hour = Math.round(seconds / 3600);
        const day = Math.round(seconds / 86400);
        const month = Math.round(seconds / 2629800);
        const year = Math.floor(seconds / 31536000);
        const isToday = today.toDateString() === date.toDateString();
        const isYesterday =
            yesterday.toDateString() === date.toDateString();
        const isThisYear = today.getFullYear() === date.getFullYear();

        switch(true){
            case (seconds < 5):
                return 'now';
            case (seconds < 60):
                return `${seconds} seconds ago`;
            case (seconds < 90):
                return 'about a minute ago';
            case (minutes < 60):
                return `${minutes} minutes ago`;
            case (hour === 1 && hour < 2):
                return `${hour} hour ago`; // 1 hour ago
            case (hour > 1 && hour <= 12):
                return `${hour} hours ago`; // 2 hours ago
            case (isToday):
                return getFormattedDate(date, 'Today'); // Today at 10:20am
            case (isYesterday): 
                return getFormattedDate(date, 'Yesterday'); // Yesterday at 10:20am
            case(day > 1 && day <= 30):
                return `${day} days ago`; // 2 days ago
            case (isThisYear):
                return getFormattedDate(date, false, true); // January 10th at 10:20pm 
            case (day > 30 && month <= 1):
                return `${hour} month ago`; // 1 month ago
            case (month > 1 && month <= 12):
                return `${month} months ago`; // 2 months ago
            case (year === 1):
                return `${year} year ago`; // 1 year ago
            case (year > 1):
                return `${year} years ago`; // 2 years ago
            default:
                return getFormattedDate(date); // January 10th 2022 at 10:20pm
        }
    }

    return timeAgo(date);
};

console.log(showTimeAgo("2022-06-20T13:42:29-05:00"));-05:00"))
Schaumberger answered 20/6, 2022 at 21:59 Comment(0)
F
0

The above answers are good for old java scripts. But it's not working well on new EC6 JavaScript or TypeScript. Below is a very short and simple function for the latest JavaScript, TypeScript, AngularJs, ReactJs, and NodeJs to return the time ago as per the given date and time.

  public timeAgo(date) {
    var seconds = Math.floor((new Date().getTime() - new Date(date).getTime()) / 1000);
    var interval = seconds / 31536000;
    if (interval > 1) return Math.floor(interval) + " years";
    interval = seconds / 2592000;
    if (interval > 1) return Math.floor(interval) + " months";
    interval = seconds / 86400;
    if (interval > 1) return Math.floor(interval) + " days";
    interval = seconds / 3600;
    if (interval > 1) return Math.floor(interval) + " hours";
    interval = seconds / 60;
    if (interval > 1) return Math.floor(interval) + " minutes";
    return Math.floor(seconds) + " seconds";
  }

console.log(timeAgo('2022-08-12 20:50:20'));
// 2 hours ago, as per the given date time string.
Formic answered 12/8, 2022 at 8:57 Comment(0)
H
0
    const timeHandler = (time: any) => {
      var a = moment(); //now
      var b = moment.utc(time);
      const sec = a.diff(b, 'seconds');
      const minuts = a.diff(b, 'minutes');
      const hours = a.diff(b, 'hours');
      const days = a.diff(b, 'days');
      const weeks = a.diff(b, 'weeks');
      return weeks == 0
        ? days == 0
          ? hours == 0
            ? minuts == 0
              ? sec >= 0 && `${sec}s ago`
              : `${minuts}m ago`
            : `${hours}h ago`
          : `${days}d ago`
        : `${weeks}w ago`;
    };

console.log(timeHandler('2022-11-11T12:11:06.6103808'))
Hamblin answered 16/11, 2022 at 7:12 Comment(0)
Y
0

dayjs has a neat little one liner. Here is a function I pass a timestamp(ts) as a string. ts is in ISO format already.

export function nTimeAgo(ts: string){ 
  return `${dayjs().to(dayjs(ts), true)} ago`;
}

Link to docs here: https://day.js.org/docs/en/plugin/relative-time

Yawp answered 1/8, 2023 at 17:6 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.