javascript-time-ago
International higly customizable relative date/time formatter (both for past and future dates).
Formats a date/timestamp to:
- just now
- 5m
- 15 min
- 25 minutes
- an hour ago
- 1 mo.
- 5 years ago
- … or whatever else
For React users there's also a React component.
Usage
npm install javascript-time-ago --save
import TimeAgo from 'javascript-time-ago'
import en from 'javascript-time-ago/locale/en'
TimeAgo.locale(en)
const timeAgo = new TimeAgo('en-US')
timeAgo.format(new Date())
timeAgo.format(Date.now() - 60 * 1000)
timeAgo.format(Date.now() - 2 * 60 * 60 * 1000)
timeAgo.format(Date.now() - 24 * 60 * 60 * 1000)
Russian
import TimeAgo from 'javascript-time-ago'
import en from 'javascript-time-ago/locale/en'
import ru from 'javascript-time-ago/locale/ru'
TimeAgo.locale(en)
TimeAgo.locale(ru)
const timeAgo = new TimeAgo('ru-RU')
timeAgo.format(new Date())
timeAgo.format(Date.now() - 60 * 1000)
timeAgo.format(Date.now() - 2 * 60 * 60 * 1000)
timeAgo.format(Date.now() - 24 * 60 * 60 * 1000)
Mimics Twitter style of time ago ("1m", "2h", "Mar 3", "Apr 4, 2012")
timeAgo.format(new Date(), 'twitter')
timeAgo.format(Date.now() - 60 * 1000, 'twitter')
timeAgo.format(Date.now() - 2 * 60 * 60 * 1000, 'twitter')
timeAgo.format(Date.now() - 2 * 24 * 60 * 60 * 1000, 'twitter')
timeAgo.format(Date.now() - 365 * 24 * 60 * 60 * 1000, 'twitter')
Twitter style uses Intl
for formatting day/month/year
labels. If Intl
is not available then it falls back to the default style.
"Just time" style
timeAgo.format(Date.now() - 60 * 1000, 'time')
Similar to the default style but with "ago" omitted:
- just now
- 1 minute
- 2 minutes
- 5 minutes
- 10 minutes
- 15 minutes
- 20 minutes
- half an hour
- 1 hour
- 2 hours
- …
- 20 hours
- 1 day
- 2 days
- 3 days
- 4 days
- 5 days
- 1 week
- 2 weeks
- 3 weeks
- 1 month
- 2 months
- 3 months
- 4 months
- half a year
- 1 year
- 2 years
- 3 years
- …
Loading locales
No locale data is loaded by default: a developer must manually choose which locales must be loaded. This is to reduce the resulting javascript bundle size.
If the resulting bundle size is of no concern (e.g. a big enterprise application), or if the code is being run on server side (e.g. Server-Side Rendering), then use this helper to load all available locales:
require('javascript-time-ago/load-all-locales')
RelativeTimeFormat
There's a spec proposal called Intl.RelativeTimeFormat
suggesting web browsers implement "time ago" formatting natively like they already do for dates and numbers. It's still a draft, and not officially accepted yet, but I guess at some point in time it will be accepted, in which case this library could serve as a polyfill for older browsers (iOS, Android).
import JavascriptTimeAgo, { RelativeTimeFormat } from 'javascript-time-ago'
import en from 'javascript-time-ago/locale/en'
JavascriptTimeAgo.locale(en)
new RelativeTimeFormat('en').format(-2, 'day')
Advanced
The above sections explained all the basics required for using this library in a project.
This part of the documentation contains some advanced topics for those willing to have a better understanding of how this library works internally.
Customization
Localization can be further customized by selecting one of the "flavours": long
, short
, or mabe some others (like tiny
defined for en
). Refer to locale/en
for an example.
import english from 'javascript-time-ago/locale/en'
english.tiny
english.short
english.long
One can pass style
(string
or object
) as a second parameter to the .format(date, style)
function. The style
object can specify (all are optional):
flavour
– preferred labels variant (e.g. short
, long
)units
– a list of time interval measurement units which can be used in the formatted output (e.g. ['second', 'minute', 'hour']
)gradation
– custom time interval measurement units scalecustom
– a function of { elapsed, time, date, now, locale }
. If this function returns a value, then the .format()
call will return that value. Otherwise the date/time is formatted as usual.
(see twitter
style for an example)
Gradation
A gradation
is a list of time interval measurement steps.
[
{
unit: 'second',
},
{
unit: 'minute',
factor: 60,
threshold: 59.5
},
{
unit: 'hour',
factor: 60 * 60,
threshold: 59.5 * 60
},
…
]
Each step is described by:
unit
— a localized time measurement unit: second
, minute
, hour
, day
, month
, year
are the standardized CLDR ones.factor
— a divider for the supplied time interval (in seconds).threshold
— a minimum time interval value (in seconds) required for this gradation step. Each step must have a threshold
defined except for the first one. Can a number
or a function(now)
returning a number
. Some advanced threshold
customization is possible like threshold_for_[prev-unit]
(see ./source/gradation/convenient.js
).granularity
— for example, 5
for minute
to allow only 5-minute intervals: 0 minutes
, 5 minutes
, 10 minutes
, etc.
If a gradation step should output not simply a time interval of a certain time unit but something different instead then it may be described by:
unit
— a localized time measurement unit: second
, minute
, hour
, day
, month
, year
are the standardized CLDR ones.factor
— a divider for the supplied time interval (in seconds).threshold
— a minimum time interval value (in seconds) required for this gradation step. Each step must have a threshold
defined except for the first one. Can a number
or a function(now)
returning a number
. Some advanced threshold
customization is possible like threshold_for_[prev-unit]
(see ./source/gradation/convenient.js
).granularity
— for example, 5
for minute
to allow only 5-minute intervals: 0 minutes
, 5 minutes
, 10 minutes
, etc.
For more gradation examples see source/gradation
folder.
threshold
— same as above.format
— a function(value, locale)
returning a string
. value
argument is the date/time being formatted as passed to TimeAgo.format(value)
: either a number
or a Date
. locale
argument is the selected locale (aka "BCP 47 language tag", e.g. ru-RU
). For example, the built-in Twitter gradation has regular minute
and hour
steps followed by a custom one formatting a date as "day/month/year", e.g. Jan 24, 2018
.
Built-in gradations:
import {
canonical,
convenient
} from 'javascript-time-ago/gradation'
Localization internals
The localization resides in the locale
folder.
The format a localization is:
{
…
"day":
{
"past":
{
"one": "{0} day ago",
"other": "{0} days ago"
},
"future":
{
"one": "in {0} day",
"other": "in {0} days"
}
},
…
}
The past
and future
can be defined by any of: zero
, one
, two
, few
, many
and other
. For more info on which is which read the official Unicode CLDR documentation. Unicode CLDR (Common Locale Data Repository) is an industry standard and is basically a collection of formatting rules for all locales (date, time, currency, measurement units, numbers, etc).
To determine whether a certain amount of time (number) is one
, few
, or something else, javascript-time-ago
uses Unicode CLDR rules for formatting plurals. These rules are number quantifying functions (one for each locale) which can tell if a number should be treated as zero
, one
, two
, few
, many
or other
. Knowing how these pluralization rules work is not required but anyway here are some links for curious advanced readers: rules explanation, list of rules for all locales, converting those rules to javascript functions. These quantifying functions can be found as quantify
properties of a locale data.
Future
When given future dates .format()
produces the corresponding output, e.g. "in 5 minutes", "in a year", etc.
Default
The default locale is en
and can be changed: TimeAgo.defaultLocale = 'ru'
.
React
There is also a React component built upon this library which autorefreshes itself.
Intl
Intl
global object is not required for this library, but it may be required if you choose to use the built-in twitter
style (though it will fall back to the default style if Intl
is not available).
Intl
is present in all modern web browsers and is absent from some of the old ones: Internet Explorer 10, Safari 9 and iOS Safari 9.x (which can be solved using Intl
polyfill).
Node.js starting from 0.12
has Intl
built-in, but only includes English locale data by default. If your app needs to support more locales than English on server side (e.g. Server-Side Rendering) then you'll need to use Intl
polyfill.
Applying Intl
polyfill:
npm install intl@1.2.4 --save
Node.js
import IntlPolyfill from 'intl'
const locales = ['en', 'ru', ...]
if (typeof Intl === 'object') {
if (!Intl.DateTimeFormat || Intl.DateTimeFormat.supportedLocalesOf(locales).length !== locales.length) {
Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat
}
}
else {
global.Intl = IntlPolyfill
}
Web browser: only download intl
package if the web browser doesn't support it, and only download the required locale.
async function initIntl() {
if (typeof Intl === 'object') {
return
}
await Promise.all([
import('intl'),
import('intl/locale-data/jsonp/en'),
import('intl/locale-data/jsonp/ru'),
...
])
}
initIntl().then(...)
Contributing
After cloning this repo, ensure dependencies are installed by running:
npm install
This module is written in ES6 and uses Babel for ES5
transpilation. Widely consumable JavaScript can be produced by running:
npm run build
Once npm run build
has run, you may import
or require()
directly from
node.
After developing, the full test suite can be evaluated by running:
npm test
When you're ready to test your new functionality on a real project, you can run
npm pack
It will build
, test
and then create a .tgz
archive which you can then install in your project folder
npm install [module name with version].tar.gz
License
MIT