I'm making a javascript counter that counts 'seconds ago'. I have my time in a JS time object, and I found a "time difference" function snippet here on stack overflow, but it displays "2 hours ago". How can I get it to display "5 hours, 10 minutes and 37 seconds ago."
Here's what I'm working with:
This function converts the current time and the timestamp of something into "20 seconds ago" instead of a cryptic date:
function timeDifference(current, previous) {
var msPerMinute = 60 * 1000;
var msPerHour = msPerMinute * 60;
var msPerDay = msPerHour * 24;
var msPerMonth = msPerDay * 30;
var msPerYear = msPerDay * 365;
var elapsed = current - previous;
if (elapsed < msPerMinute) {
return Math.round(elapsed/1000) + ' seconds ago';
} else if (elapsed < msPerHour) {
return Math.round(elapsed/msPerMinute) + ' minutes ago';
} else if (elapsed < msPerDay ) {
return Math.round(elapsed/msPerHour ) + ' hours ago';
} else if (elapsed < msPerMonth) {
return 'approximately ' + Math.round(elapsed/msPerDay) + ' days ago';
} else if (elapsed < msPerYear) {
return 'approximately ' + Math.round(elapsed/msPerMonth) + ' months ago';
} else {
return 'approximately ' + Math.round(elapsed/msPerYear ) + ' years ago';
}
}
And here's what I'm using to "count up" the time each second. I'd like it to say "5 hours, 3 minutes, 10 seconds ago" and then 1 second later, "5 hours, 3 minutes, 11 seconds ago"
var newTime = new Date(data.popular[i].timestamp*1000)
var relTime = timeDifference(new Date(),newTime)
setInterval(function(){
var theTimeEl = $('.timestamp-large').filter(function(){
return $(this).html() == relTime
});
newTime.setSeconds(newTime.getSeconds() + 1);
var relTime = timeDifference(new Date(), newTime);
$(theTimeEl).html(relTime);
console.log(relTime)
}, 1000)
The variable newTime is the time in the UTC javascript date format. relTime is that in "seconds ago" format. The interval loops through a bunch of timestamp elements and picks the right one for each time stamp. Then it adds a second to the time, converts it back into "fuzzy time" (seconds ago), replaces the html with the new time and logs it in the console.
How do I change "5 hours ago" to "5 hours, 37 mintues, 10 seconds ago"? The time difference function needs to be modified.
Here's a function that is close to what you're asking for.
var timeparts = [
{name: 'year', div: 31536000000, mod: 10000},
{name: 'day', div: 86400000, mod: 365},
{name: 'hour', div: 3600000, mod: 24},
{name: 'minute', div: 60000, mod: 60},
{name: 'second', div: 1000, mod: 60}
];
function timeAgoNaive(comparisonDate) {
var
i = 0,
l = timeparts.length,
calc,
values = [],
interval = new Date().getTime() - comparisonDate.getTime();
while (i < l) {
calc = Math.floor(interval / timeparts[i].div) % timeparts[i].mod;
if (calc) {
values.push(calc + ' ' + timeparts[i].name + (calc != 1 ? 's' : ''));
}
i += 1;
}
if (values.length === 0) { values.push('0 seconds'); }
return values.join(', ') + ' ago';
}
console.log(timeAgoNaive(new Date(Date.parse('Jun 12 2006 11:52:33'))));
console.log(timeAgoNaive(new Date(new Date().getTime() - 3600000)));
console.log(timeAgoNaive(new Date()));
Results:
6 years, 33 days, 4 hours, 52 minutes, 22 seconds ago
1 hour ago
0 seconds ago
I called it "naive" because it doesn't really pay attention to the human way that we calculate time. If it is "1/1/2013 12:01:00 am" exactly, comparing to "1/1/2012 12:01:00 am" should yield "1 year, 0 months, 0 days, 0 hours, 0 minutes, 0 seconds ago". But it won't do that by extending the logic in the function you presented, and it won't do that in my function either (plus my function won't use months). A better approximation of years than 365 days is 365.24, but that also is ignored.
I excluded the empty time parts as you requested, leaving "0 seconds" at a minimum when there are no time parts found.
Now, if you want that human-like way of calculating, you have to decide some things. You can't just use boundaries crossed because Feb 28 to Mar 1 is not a whole month. Second, here's a question will expose the real problem:
How many months and days is Feb 2 to Mar 31?
If you calculate Feb 2 to Mar 2 as one month, then it's 1 month 29 days. But what if it were Jan 2 to Mar 1? That's the same number of days elapsed between them. Is that now 1 month (for all of April) + 1 day in March + the 31 days in Jan for 1 month 32 days? Do you want your months to coincide to a physical calendar so a human could back track with his finger and get the correct date out of it? That is much harder than you think.
If you can answer with sensible and complete rules about how you would do "human-like elapsed time figuring" then maybe I can write you another function to do it.
Update
Here's a new function that does months, and has 365.24 days in a year (30.43666666 days in a month):
var timeparts = [
{name: 'millenni', div: 31556736000, p: 'a', s: 'um'},
{name: 'centur', div: 3155673600, p: 'ies', s: 'y'},
{name: 'decade', div: 315567360},
{name: 'year', div: 31556736},
{name: 'month', div: 2629728},
{name: 'day', div: 86400},
{name: 'hour', div: 3600},
{name: 'minute', div: 60},
{name: 'second', div: 1}
];
function timeAgoNaive2(comparisonDate) {
var i = 0,
parts = [],
interval = Math.floor((new Date().getTime() - comparisonDate.getTime()) / 1000);
for ( ; interval > 0; i += 1) {
var value = Math.floor(interval / timeparts[i].div);
interval = interval - (value * timeparts[i].div);
if (value) {
parts.push(value + ' ' + timeparts[i].name + (value !== 1 ? timeparts[i].p || 's' : timeparts[i].s || ''));
}
}
if (parts.length === 0) { return 'now'; }
return parts.join(', ') + ' ago';
}
console.log(timeAgoNaive2(new Date(Date.parse('Jun 12 2006 11:52:33'))));
console.log(timeAgoNaive2(new Date(new Date().getTime() - 3600000)));
console.log(timeAgoNaive2(new Date()));
console.log(timeAgoNaive2(new Date(-92709631247000)));
Output:
6 years, 1 month, 1 day, 10 hours, 53 minutes, 44 seconds ago
1 hour ago
now
2 millennia, 9 centuries, 8 decades, 4 months, 26 days, 22 hours, 41 minutes, 47 seconds ago
It is still naive, but it does a little better job. Plus it will work for REALLY old dates like B.C. ones. :)
Change the logic so that rather than just finding the single greatest unit of measurement it can, it does something with the remainder.
Basically what you'd need to do is start with the greatest increment, find the value, then subtract it from the total to get the remainder. Then repeat.
Something like this maybe, I haven't tested it.
var elapsed = current - previous;
var remainder = elapsed;
int years;
int months;
years = Math.floor(remainder/msPerYear);
remainder = remainder % msPerYear;
months = Math.floor(remainder/msPerMonth);
remainder = remainder % msPerMonth;
// repeat
Then just build your string off the variables.
This should do the trick:
var msPerMinute = 60 * 1000;
var msPerHour = msPerMinute * 60;
var msPerDay = msPerHour * 24;
var msPerMonth = msPerDay * 30;
var msPerYear = msPerDay * 365;
function timeDifference(current, previous) {
var remainder = current - previous;
var message = "";
var sep = "";
var years = Math.floor(remainder/msPerYear);
remainder = remainder - years * msPerYear;
if (years > 0) {
message += years + " years";
sep = ", ";
console.log(message);
}
var months = Math.floor(remainder/msPerMonth);
remainder = remainder - months * msPerMonth;
if (months > 0) {
message += sep + months + " months";
sep = ", ";
console.log(message);
}
var days = Math.floor(remainder/msPerDay);
remainder = remainder - days * msPerDay;
if (days > 0) {
message += sep + days + " days";
sep = ", ";
console.log(message);
}
var hours = Math.floor(remainder/msPerHour);
remainder = remainder - hours * msPerHour;
if (hours > 0) {
message += sep + hours + " hours";
sep = ", ";
console.log(message);
}
var minutes = Math.floor(remainder/msPerMinute);
remainder = remainder - minutes * msPerMinute;
if (months > 0) {
message += sep + minutes + " minutes";
sep = ", ";
console.log(message);
}
var seconds = Math.floor(remainder/1000);
remainder = remainder - seconds * 1000;
if (months > 0) {
message += sep + seconds + " seconds";
sep = ", ";
console.log(message);
}
message += " ago";
var pos = message.lastIndexOf(',');
message = message.substring(0,pos) + ' and' + message.substring(pos+1)
return message;
};
var output = timeDifference(new Date(2012, 10, 20, 12, 0, 59), new Date(2012, 2, 13, 10, 15, 12));
console.log(output);
Output: 8 months, 12 days, 1 hours, 45 minutes and 47 seconds ago
This could of course be refactored to be a bit less repetitive.
You can try out this fiddle with it working: http://jsfiddle.net/vawEf/
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
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)));
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.
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)));
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.
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)));
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`;
}
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://stackoverflow.com/a/67338038/938822
*/
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 MONTH = 30 * DAY;
const YEAR = 365 * DAY;
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://stackoverflow.com/a/67338038/938822
*/
function fromNow(date) {
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
const WEEK = 7 * DAY;
const MONTH = 30 * DAY;
const YEAR = 365 * DAY;
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);
}
}
};
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.
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.
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!)
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
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'));
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';
}
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;
}
}
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
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
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
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.
npm install timeago.js
import timeago from 'timeago.js'; // or use script tag
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'));
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'));
I used an old answer by Possible 11 and I added Intl.RelativeTimeFormat for translations.
https://stackoverflow.com/a/73331658/673809
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'))
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();
}
}
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";
}
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"
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;
}
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)`
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
// ...
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)')));
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')));
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
number,
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.
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
};
type RelativeTimeFormatUnit =
| "year" | "years"
| "quarter" | "quarters"
| "month" | "months"
| "week" | "weeks"
| "day" | "days"
| "hour" | "hours"
| "minute" | "minutes"
| "second" | "seconds"
;
const units: RelativeTimeFormatUnit[] = ["years", "months", "weeks", "days", "hours", "minutes", "seconds"]; // order matters here.
const secondsElapsed = (date.getTime() - Date.now()) / 1000;
for (let key in ranges) {
let i = 0;
if (ranges[key] < Math.abs(secondsElapsed)) {
const delta = secondsElapsed / ranges[key];
return formatter.format(Math.round(delta), units[i++]);
}
}
}
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