javascript-time-ago 2.5.12 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -1
- package/README.md +462 -316
- package/bundle/javascript-time-ago.js +1 -1
- package/bundle/javascript-time-ago.js.map +1 -1
- package/bundle/javascript-time-ago.min.js +1 -1
- package/bundle/javascript-time-ago.min.js.map +1 -1
- package/commonjs/FullDateFormatter.js +72 -0
- package/commonjs/FullDateFormatter.js.map +1 -0
- package/commonjs/FullDateFormatter.test.js +26 -0
- package/commonjs/FullDateFormatter.test.js.map +1 -0
- package/commonjs/TimeAgo.js +208 -106
- package/commonjs/TimeAgo.js.map +1 -1
- package/commonjs/TimeAgo.test.js +95 -10
- package/commonjs/TimeAgo.test.js.map +1 -1
- package/commonjs/steps/getStepMinTime.js +26 -19
- package/commonjs/steps/getStepMinTime.js.map +1 -1
- package/commonjs/steps/getTimeToNextUpdate.js +10 -2
- package/commonjs/steps/getTimeToNextUpdate.js.map +1 -1
- package/commonjs/style/twitter.js +2 -3
- package/commonjs/style/twitter.js.map +1 -1
- package/commonjs/style/twitter.test.js +5 -2
- package/commonjs/style/twitter.test.js.map +1 -1
- package/full-date-formatter/index.cjs +4 -0
- package/full-date-formatter/index.cjs.js +9 -0
- package/full-date-formatter/index.d.ts +6 -0
- package/full-date-formatter/index.js +1 -0
- package/full-date-formatter/package.json +15 -0
- package/index.cjs +1 -1
- package/index.cjs.js +2 -2
- package/index.d.ts +14 -4
- package/index.js +3 -1
- package/modules/FullDateFormatter.js +67 -0
- package/modules/FullDateFormatter.js.map +1 -0
- package/modules/FullDateFormatter.test.js +22 -0
- package/modules/FullDateFormatter.test.js.map +1 -0
- package/modules/TimeAgo.js +208 -107
- package/modules/TimeAgo.js.map +1 -1
- package/modules/TimeAgo.test.js +95 -8
- package/modules/TimeAgo.test.js.map +1 -1
- package/modules/steps/getStepMinTime.js +26 -19
- package/modules/steps/getStepMinTime.js.map +1 -1
- package/modules/steps/getTimeToNextUpdate.js +10 -2
- package/modules/steps/getTimeToNextUpdate.js.map +1 -1
- package/modules/style/twitter.js +3 -3
- package/modules/style/twitter.js.map +1 -1
- package/modules/style/twitter.test.js +5 -2
- package/modules/style/twitter.test.js.map +1 -1
- package/package.json +16 -11
package/README.md
CHANGED
|
@@ -4,24 +4,21 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/javascript-time-ago)
|
|
5
5
|
[](https://coveralls.io/r/catamphetamine/javascript-time-ago?branch=master)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Automatically chooses the right units (seconds, minutes, etc) to format a time interval.
|
|
10
|
-
|
|
11
|
-
Examples:
|
|
7
|
+
Formats a `Date` into a string like `"1 day ago"`. In any language.
|
|
12
8
|
|
|
13
9
|
* just now
|
|
14
10
|
* 45s
|
|
15
11
|
* 5m
|
|
16
12
|
* 15 minutes ago
|
|
17
13
|
* 3 hours ago
|
|
18
|
-
*
|
|
14
|
+
* 2 days ago
|
|
15
|
+
* in 4 months
|
|
19
16
|
* in 5 years
|
|
20
17
|
* …
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
It also tells one how often to [refresh](#refreshing) the label as the time goes by.
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
There's also a [React version](https://www.npmjs.com/package/react-time-ago) (see [demo](https://catamphetamine.gitlab.io/react-time-ago/))
|
|
25
22
|
|
|
26
23
|
## Install
|
|
27
24
|
|
|
@@ -29,20 +26,29 @@ This is a readme for version `2.x`. For older versions, [see version `1.x` readm
|
|
|
29
26
|
npm install javascript-time-ago --save
|
|
30
27
|
```
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
Alternatively, one could include it on a web page [directly](#cdn) via a `<script/>` tag.
|
|
33
30
|
|
|
34
31
|
## Use
|
|
35
32
|
|
|
33
|
+
<!-- Migration notes: This is a readme for version `2.x`. If you're using version `1.x`, [see the old readme](https://github.com/catamphetamine/javascript-time-ago/tree/1.x). Also see a [migration guide](https://github.com/catamphetamine/javascript-time-ago/blob/master/MIGRATION.md) from version `1.x` to `2.x`. -->
|
|
34
|
+
|
|
35
|
+
To begin, decide on the set of languages that your application will be translated into. For now, let's assume that it's gonna be just English.
|
|
36
|
+
|
|
37
|
+
Then, for each of those languages, `import` the language data from `javascript-time-ago/locale/..`, and pass it to `TimeAgo.addLocale()` function.
|
|
38
|
+
|
|
36
39
|
```js
|
|
37
40
|
import TimeAgo from 'javascript-time-ago'
|
|
38
|
-
|
|
39
|
-
// English.
|
|
40
41
|
import en from 'javascript-time-ago/locale/en'
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
// Add English language
|
|
44
|
+
TimeAgo.addLocale(en)
|
|
45
|
+
```
|
|
43
46
|
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
Now you're ready to create a `new TimeAgo()` formatter for any of those languages, and use it to convert dates into strings.
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
// Create English formatter
|
|
51
|
+
const timeAgo = new TimeAgo('en')
|
|
46
52
|
|
|
47
53
|
timeAgo.format(new Date())
|
|
48
54
|
// "just now"
|
|
@@ -57,48 +63,68 @@ timeAgo.format(Date.now() - 24 * 60 * 60 * 1000)
|
|
|
57
63
|
// "1 day ago"
|
|
58
64
|
```
|
|
59
65
|
|
|
60
|
-
|
|
66
|
+
To change the output style, see the list of available [formatting styles](#formatting-styles).
|
|
67
|
+
|
|
68
|
+
P.S. After rendering a label, don't forget to [refresh](#refreshing) it as the time goes by.
|
|
69
|
+
|
|
70
|
+
## Languages
|
|
71
|
+
|
|
72
|
+
This library supports a lot of languages. None of those languages are loaded by default. A developer must manually choose which languages should be loaded and then call `TimeAgo.addLocale()` for each one of them.
|
|
61
73
|
|
|
62
|
-
|
|
74
|
+
The `locale` argument of `new TimeAgo(locale)` constructor will be matched against the list of added languages, and the first matching one will be used. For example, `new TimeAgo("en")` and `new TimeAgo("en-US")` will both use `"en"` language.
|
|
63
75
|
|
|
64
|
-
|
|
76
|
+
If the language for the specified `locale` hasn't been added, it will retry with a "default" `locale`. For that, a "default" `locale` has to have been added by calling `TimeAgo.addDefaultLocale()`. Otherwise, when there's no "default" locale to fall back to, it will just throw an error.
|
|
65
77
|
|
|
66
|
-
|
|
78
|
+
<!-- or `TimeAgo.setDefaultLocale("en")`. By default, the default `locale` is `"en"`, although it still has to be added manually. -->
|
|
67
79
|
|
|
68
|
-
|
|
80
|
+
So how is "default" locale useful? It frees a developer from worrying about whether the `locale` argument is supported or not. They can just create a `new TimeAgo()` formatter with whatever `locale` argument and not even worry about potentially crashing the application in case it throws an error for that `locale`.
|
|
81
|
+
|
|
82
|
+
In the following example, the application supports three languages — English, German and French — and English is set to be the "default" one that will be used for any other language like Spanish.
|
|
69
83
|
|
|
70
84
|
```js
|
|
71
|
-
import
|
|
85
|
+
import en from 'javascript-time-ago/locale/en'
|
|
72
86
|
import de from 'javascript-time-ago/locale/de'
|
|
73
|
-
import
|
|
87
|
+
import fr from 'javascript-time-ago/locale/fr'
|
|
74
88
|
|
|
75
|
-
TimeAgo.
|
|
89
|
+
TimeAgo.addDefaultLocale(en)
|
|
76
90
|
TimeAgo.addLocale(de)
|
|
77
|
-
TimeAgo.
|
|
91
|
+
TimeAgo.addLocale(fr)
|
|
78
92
|
```
|
|
79
93
|
|
|
80
|
-
|
|
94
|
+
```js
|
|
95
|
+
// "es" locale hasn't been added, so it falls back to "en".
|
|
96
|
+
const timeAgo = new TimeAgo('es')
|
|
97
|
+
|
|
98
|
+
timeAgo.format(new Date())
|
|
99
|
+
// "just now"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`TimeAgo.addDefaultLocale()` is just a shortcut for `TimeAgo.addLocale()` + `TimeAgo.setDefaultLocale()`, so the code above is the same as the code below.
|
|
81
103
|
|
|
82
104
|
```js
|
|
83
|
-
import
|
|
105
|
+
import en from 'javascript-time-ago/locale/en'
|
|
84
106
|
import de from 'javascript-time-ago/locale/de'
|
|
85
|
-
import
|
|
107
|
+
import fr from 'javascript-time-ago/locale/fr'
|
|
86
108
|
|
|
87
|
-
TimeAgo.addLocale(
|
|
109
|
+
TimeAgo.addLocale(en)
|
|
88
110
|
TimeAgo.addLocale(de)
|
|
89
|
-
TimeAgo.addLocale(
|
|
111
|
+
TimeAgo.addLocale(fr)
|
|
90
112
|
|
|
91
|
-
TimeAgo.setDefaultLocale('
|
|
113
|
+
TimeAgo.setDefaultLocale('en')
|
|
92
114
|
```
|
|
93
115
|
|
|
94
|
-
|
|
116
|
+
`new TimeAgo()` constructor also supports passing a list of `locales` to choose from. In that case, it will choose the first one that works.
|
|
95
117
|
|
|
96
118
|
```js
|
|
119
|
+
// Add English and German languages
|
|
97
120
|
TimeAgo.addDefaultLocale(en)
|
|
98
121
|
TimeAgo.addLocale(de)
|
|
99
122
|
|
|
100
|
-
// "de" language will be
|
|
101
|
-
new TimeAgo(['ru-RU', 'de-DE', 'en-US'])
|
|
123
|
+
// "de" language will be chosen because it's the first one that works.
|
|
124
|
+
const timeAgo = new TimeAgo(['ru-RU', 'de-DE', 'en-US'])
|
|
125
|
+
|
|
126
|
+
timeAgo.format(new Date())
|
|
127
|
+
// "gerade jetzt"
|
|
102
128
|
```
|
|
103
129
|
|
|
104
130
|
<!--
|
|
@@ -109,15 +135,15 @@ require('javascript-time-ago/load-all-locales')
|
|
|
109
135
|
```
|
|
110
136
|
-->
|
|
111
137
|
|
|
112
|
-
|
|
138
|
+
<!--
|
|
139
|
+
An example of formatting dates in Russian:
|
|
113
140
|
|
|
114
141
|
```js
|
|
115
142
|
import TimeAgo from 'javascript-time-ago'
|
|
116
|
-
|
|
117
|
-
// Russian.
|
|
118
143
|
import ru from 'javascript-time-ago/locale/ru'
|
|
119
144
|
|
|
120
|
-
|
|
145
|
+
// Add Russian language.
|
|
146
|
+
TimeAgo.addLocale(ru)
|
|
121
147
|
|
|
122
148
|
const timeAgo = new TimeAgo('ru-RU')
|
|
123
149
|
|
|
@@ -133,40 +159,37 @@ timeAgo.format(Date.now() - 2 * 60 * 60 * 1000)
|
|
|
133
159
|
timeAgo.format(Date.now() - 24 * 60 * 60 * 1000)
|
|
134
160
|
// "1 день назад"
|
|
135
161
|
```
|
|
162
|
+
-->
|
|
136
163
|
|
|
137
|
-
## Styles
|
|
138
|
-
|
|
139
|
-
This library allows for any custom logic for formatting time intervals:
|
|
140
|
-
|
|
141
|
-
* What scale should be used for measuring time intervals: should it be precise down to the second, or should it only measure it up to a minute, or should it start from being more precise when time intervals are small and then gradually decrease its precision as time intervals get longer.
|
|
164
|
+
## Formatting Styles
|
|
142
165
|
|
|
143
|
-
|
|
166
|
+
A "formatting style" defines how a date should be formatted relative to the current time.
|
|
144
167
|
|
|
145
|
-
|
|
168
|
+
It could be precise up to a second with `"1 second ago"`, `"2 seconds ago"`, `"3 seconds ago"`, etc labels, or it could prefer to combine all those under a single `"less than a minute ago"` label.
|
|
146
169
|
|
|
147
|
-
|
|
170
|
+
It could use verbose labels like `"1 minute ago"` or it could prefer shorter variants like `"1 min. ago"` or even `"1m"`. Or it could prefer to output a full date like `"Dec 11, 2015"` for dates that're older than 1 year from now.
|
|
148
171
|
|
|
149
|
-
|
|
172
|
+
While one could certainly implement their own [custom](#custom-style) formatting "style" from scratch, most applications would be totally fine with one of the few already-available styles that're described below.
|
|
150
173
|
|
|
151
174
|
### Round
|
|
152
175
|
|
|
153
|
-
|
|
176
|
+
`"round"` style just rounds the time difference up to the closest unit of time — second, minute, hour, etc — and then returns a label for that unit of time.
|
|
154
177
|
|
|
155
178
|
```js
|
|
156
179
|
timeAgo.format(Date.now(), 'round')
|
|
157
|
-
// 0 seconds ago
|
|
180
|
+
// 0 seconds ago: "just now"
|
|
158
181
|
|
|
159
182
|
timeAgo.format(Date.now() - 1 * 1000, 'round')
|
|
160
|
-
// 1 second ago
|
|
183
|
+
// 1 second ago: "1 second ago"
|
|
161
184
|
|
|
162
185
|
timeAgo.format(Date.now() - 29 * 1000, 'round')
|
|
163
|
-
// 29 seconds ago
|
|
186
|
+
// 29 seconds ago: "29 seconds ago"
|
|
164
187
|
|
|
165
188
|
timeAgo.format(Date.now() - 30 * 1000, 'round')
|
|
166
|
-
// 30 seconds ago
|
|
189
|
+
// 30 seconds ago: "1 minute ago"
|
|
167
190
|
|
|
168
191
|
timeAgo.format(Date.now() - 1.5 * 60 * 1000, 'round')
|
|
169
|
-
// 1.5 minutes ago
|
|
192
|
+
// 1.5 minutes ago: "2 minutes ago"
|
|
170
193
|
```
|
|
171
194
|
|
|
172
195
|
* just now
|
|
@@ -201,17 +224,17 @@ timeAgo.format(Date.now() - 1.5 * 60 * 1000, 'round')
|
|
|
201
224
|
|
|
202
225
|
### Round (minute)
|
|
203
226
|
|
|
204
|
-
|
|
227
|
+
`"round-minute"` style is same as `"round"` style but without seconds. This is the default style that is used when no custom style is specified.
|
|
205
228
|
|
|
206
229
|
```js
|
|
207
230
|
timeAgo.format(Date.now(), 'round-minute')
|
|
208
|
-
// 0 seconds ago
|
|
231
|
+
// 0 seconds ago: "just now"
|
|
209
232
|
|
|
210
233
|
timeAgo.format(Date.now() - 29 * 1000, 'round-minute')
|
|
211
|
-
// 29 seconds ago
|
|
234
|
+
// 29 seconds ago: "just now"
|
|
212
235
|
|
|
213
236
|
timeAgo.format(Date.now() - 30 * 1000, 'round-minute')
|
|
214
|
-
// 30 seconds ago
|
|
237
|
+
// 30 seconds ago: "1 minute ago"
|
|
215
238
|
|
|
216
239
|
// The rest is same as "round" style.
|
|
217
240
|
```
|
|
@@ -223,80 +246,80 @@ timeAgo.format(Date.now() - 30 * 1000, 'round-minute')
|
|
|
223
246
|
|
|
224
247
|
### Mini
|
|
225
248
|
|
|
226
|
-
|
|
249
|
+
`"mini"` style is same as `"round"` style but with labels that're as short as possible, without the `" ago"` part, and it [doesn't](https://github.com/catamphetamine/javascript-time-ago/issues/40) output "weeks".
|
|
227
250
|
|
|
228
251
|
```js
|
|
229
252
|
timeAgo.format(new Date(), 'mini')
|
|
230
|
-
// 0 seconds ago
|
|
253
|
+
// 0 seconds ago: "0s"
|
|
231
254
|
|
|
232
255
|
timeAgo.format(new Date() - 1 * 1000, 'mini')
|
|
233
|
-
// 1 second ago
|
|
256
|
+
// 1 second ago: "1s"
|
|
234
257
|
|
|
235
258
|
timeAgo.format(Date.now() - 2 * 60 * 1000, 'mini')
|
|
236
|
-
// 2 minutes ago
|
|
259
|
+
// 2 minutes ago: "2m"
|
|
237
260
|
|
|
238
261
|
timeAgo.format(Date.now() - 3 * 60 * 60 * 1000, 'mini')
|
|
239
|
-
// 3 hours ago
|
|
262
|
+
// 3 hours ago: "3h"
|
|
240
263
|
|
|
241
264
|
timeAgo.format(Date.now() - 4 * 24 * 60 * 60 * 1000, 'mini')
|
|
242
|
-
// 4 days ago
|
|
265
|
+
// 4 days ago: "4d"
|
|
243
266
|
|
|
244
267
|
timeAgo.format(Date.now() - 23 * 24 * 60 * 60 * 1000, 'mini')
|
|
245
|
-
// 23 days ago
|
|
268
|
+
// 23 days ago: "23d"
|
|
246
269
|
|
|
247
270
|
timeAgo.format(Date.now() - 5 * 30 * 24 * 60 * 60 * 1000, 'mini')
|
|
248
|
-
// 5 months ago
|
|
271
|
+
// 5 months ago: "5mo"
|
|
249
272
|
|
|
250
273
|
timeAgo.format(Date.now() - 12 * 30 * 24 * 60 * 60 * 1000, 'mini')
|
|
251
|
-
// 1 year ago
|
|
274
|
+
// 1 year ago: "1yr"
|
|
252
275
|
```
|
|
253
276
|
|
|
254
277
|
For best compatibility, `mini.json` labels should be [defined](https://github.com/catamphetamine/javascript-time-ago/tree/master/locale-more-styles) for a locale, otherwise you might [end up with](https://github.com/catamphetamine/javascript-time-ago/issues/49) labels like `"-1m"` for "one minute ago" for some languages. Send `mini.json` pull requests for the missing languages if you speak those.
|
|
255
278
|
|
|
256
279
|
### Mini (now)
|
|
257
280
|
|
|
258
|
-
|
|
281
|
+
`"mini-now"` style is same as `"mini"` style with the only difference that it outputs `"now"` instead of `"0s"`.
|
|
259
282
|
|
|
260
283
|
```js
|
|
261
284
|
timeAgo.format(new Date(), 'mini-now')
|
|
262
|
-
// 0 seconds ago
|
|
285
|
+
// 0 seconds ago: "now"
|
|
263
286
|
|
|
264
287
|
timeAgo.format(new Date() - 1 * 1000, 'mini-now')
|
|
265
|
-
// 1 second ago
|
|
288
|
+
// 1 second ago: "1s"
|
|
266
289
|
|
|
267
290
|
// The rest is same as "mini" style.
|
|
268
291
|
```
|
|
269
292
|
|
|
270
293
|
### Mini (minute)
|
|
271
294
|
|
|
272
|
-
|
|
295
|
+
`"mini-minute"` style is same as `"mini"` style but without seconds.
|
|
273
296
|
|
|
274
297
|
```js
|
|
275
298
|
timeAgo.format(new Date(), 'mini-minute')
|
|
276
|
-
// 0 seconds ago
|
|
299
|
+
// 0 seconds ago: "0m"
|
|
277
300
|
|
|
278
301
|
timeAgo.format(new Date() - 29 * 1000, 'mini-minute')
|
|
279
|
-
// 29 seconds ago
|
|
302
|
+
// 29 seconds ago: "0m"
|
|
280
303
|
|
|
281
304
|
timeAgo.format(new Date() - 30 * 1000, 'mini-minute')
|
|
282
|
-
// 30 seconds ago
|
|
305
|
+
// 30 seconds ago: "1m"
|
|
283
306
|
|
|
284
307
|
// The rest is same as "mini" style.
|
|
285
308
|
```
|
|
286
309
|
|
|
287
310
|
### Mini (minute-now)
|
|
288
311
|
|
|
289
|
-
|
|
312
|
+
`"mini-minute-now"` style is same as `"mini-minute"` style with the only difference that it outputs `"now"` instead of `"0m"`.
|
|
290
313
|
|
|
291
314
|
```js
|
|
292
315
|
timeAgo.format(new Date(), 'mini-minute-now')
|
|
293
|
-
// 0 seconds ago
|
|
316
|
+
// 0 seconds ago: "now"
|
|
294
317
|
|
|
295
318
|
timeAgo.format(new Date() - 29 * 1000, 'mini-minute-now')
|
|
296
|
-
// 29 seconds ago
|
|
319
|
+
// 29 seconds ago: "now"
|
|
297
320
|
|
|
298
321
|
timeAgo.format(new Date() - 30 * 1000, 'mini-minute-now')
|
|
299
|
-
// 30 seconds ago
|
|
322
|
+
// 30 seconds ago: "1m"
|
|
300
323
|
|
|
301
324
|
// The rest is same as "mini" style.
|
|
302
325
|
```
|
|
@@ -308,13 +331,13 @@ Same as `"twitter"` style but doesn't output anything before the first minute.
|
|
|
308
331
|
|
|
309
332
|
```js
|
|
310
333
|
timeAgo.format(new Date(), 'twitter-first-minute')
|
|
311
|
-
// 0 seconds ago
|
|
334
|
+
// 0 seconds ago: ""
|
|
312
335
|
|
|
313
336
|
timeAgo.format(new Date() - 59 * 1000, 'twitter-first-minute')
|
|
314
|
-
// 59 seconds ago
|
|
337
|
+
// 59 seconds ago: ""
|
|
315
338
|
|
|
316
339
|
timeAgo.format(new Date() - 60 * 1000, 'twitter-first-minute')
|
|
317
|
-
// 1 minute ago
|
|
340
|
+
// 1 minute ago: "1m"
|
|
318
341
|
|
|
319
342
|
// The rest is same as "twitter" style.
|
|
320
343
|
```
|
|
@@ -322,20 +345,20 @@ timeAgo.format(new Date() - 60 * 1000, 'twitter-first-minute')
|
|
|
322
345
|
|
|
323
346
|
### Twitter
|
|
324
347
|
|
|
325
|
-
|
|
348
|
+
`"twitter"` style mimicks [Twitter](https://twitter.com) labels: `"1s"`, `"2m"`, `"3h"`, `"Mar 4"`, `"Apr 5, 2012"`.
|
|
326
349
|
|
|
327
350
|
```js
|
|
328
351
|
timeAgo.format(new Date(), 'twitter')
|
|
329
|
-
// 0 seconds ago
|
|
352
|
+
// 0 seconds ago: "0s"
|
|
330
353
|
|
|
331
354
|
timeAgo.format(new Date() - 1 * 1000, 'twitter')
|
|
332
|
-
// 1 second ago
|
|
355
|
+
// 1 second ago: "1s"
|
|
333
356
|
|
|
334
357
|
timeAgo.format(Date.now() - 2 * 60 * 1000, 'twitter')
|
|
335
|
-
// 2 minutes ago
|
|
358
|
+
// 2 minutes ago: "2m"
|
|
336
359
|
|
|
337
360
|
timeAgo.format(Date.now() - 3 * 60 * 60 * 1000, 'twitter')
|
|
338
|
-
// 3 hours ago
|
|
361
|
+
// 3 hours ago: "3h"
|
|
339
362
|
|
|
340
363
|
timeAgo.format(Date.now() - 4 * 24 * 60 * 60 * 1000, 'twitter')
|
|
341
364
|
// More than 24 hours ago → `month/day` ("Mar 4")
|
|
@@ -344,98 +367,134 @@ timeAgo.format(Date.now() - 364 * 24 * 60 * 60 * 1000, 'twitter')
|
|
|
344
367
|
// Another year → `month/day/year` ("Mar 5, 2017")
|
|
345
368
|
```
|
|
346
369
|
|
|
347
|
-
`"twitter"` style uses [`Intl`](https://
|
|
370
|
+
`"twitter"` style uses [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) when formatting `day/month/year` labels. When `Intl` is not available — for example, in Internet Explorer — it falls back to the usual short labels: `"1d"`, `"1mo"`, `"1yr"`, etc.
|
|
348
371
|
|
|
349
|
-
For best compatibility, `mini.json` labels should be defined
|
|
372
|
+
For best compatibility, `mini.json` labels should be [defined](https://github.com/catamphetamine/javascript-time-ago/tree/master/locale-more-styles) for a locale. Send `mini.json` pull requests for the missing languages if you speak those.
|
|
350
373
|
|
|
351
374
|
### Twitter (now)
|
|
352
375
|
|
|
353
|
-
|
|
376
|
+
`"twitter-now"` style is same as `"twitter"` style with the only difference that it outputs `"now"` instead of `"0s"`.
|
|
354
377
|
|
|
355
378
|
```js
|
|
356
379
|
timeAgo.format(new Date(), 'twitter-now')
|
|
357
|
-
// 0 seconds ago
|
|
380
|
+
// 0 seconds ago: "now"
|
|
358
381
|
|
|
359
382
|
timeAgo.format(new Date() - 1 * 1000, 'twitter-now')
|
|
360
|
-
// 1 second ago
|
|
383
|
+
// 1 second ago: "1s"
|
|
361
384
|
|
|
362
385
|
// The rest is same as "twitter" style.
|
|
363
386
|
```
|
|
364
387
|
|
|
365
388
|
### Twitter (minute)
|
|
366
389
|
|
|
367
|
-
|
|
390
|
+
`"twitter-minute"` style is same as `"twitter"` style but without seconds.
|
|
368
391
|
|
|
369
392
|
```js
|
|
370
393
|
timeAgo.format(new Date(), 'twitter-minute')
|
|
371
|
-
// 0 seconds ago
|
|
394
|
+
// 0 seconds ago: "0m"
|
|
372
395
|
|
|
373
396
|
timeAgo.format(new Date() - 29 * 1000, 'twitter-minute')
|
|
374
|
-
// 29 seconds ago
|
|
397
|
+
// 29 seconds ago: "0m"
|
|
375
398
|
|
|
376
399
|
timeAgo.format(new Date() - 30 * 1000, 'twitter-minute')
|
|
377
|
-
// 30 seconds ago
|
|
400
|
+
// 30 seconds ago: "1m"
|
|
378
401
|
|
|
379
402
|
// The rest is same as "twitter" style.
|
|
380
403
|
```
|
|
381
404
|
|
|
382
405
|
### Twitter (minute-now)
|
|
383
406
|
|
|
384
|
-
|
|
407
|
+
`"twitter-minute-now"` style is same as `"twitter-minute"` style with the only difference that it outputs `"now"` instead of `"0m"`.
|
|
385
408
|
|
|
386
409
|
```js
|
|
387
410
|
timeAgo.format(new Date(), 'twitter-minute-now')
|
|
388
|
-
// 0 seconds ago
|
|
411
|
+
// 0 seconds ago: "now"
|
|
389
412
|
|
|
390
413
|
timeAgo.format(new Date() - 29 * 1000, 'twitter-minute-now')
|
|
391
|
-
// 29 seconds ago
|
|
414
|
+
// 29 seconds ago: "now"
|
|
392
415
|
|
|
393
416
|
timeAgo.format(new Date() - 30 * 1000, 'twitter-minute-now')
|
|
394
|
-
// 30 seconds ago
|
|
417
|
+
// 30 seconds ago: "1m"
|
|
395
418
|
|
|
396
419
|
// The rest is same as "twitter" style.
|
|
397
420
|
```
|
|
398
421
|
|
|
399
422
|
### Twitter (first minute)
|
|
400
423
|
|
|
401
|
-
|
|
424
|
+
`"twitter-first-minute"` style is same as `"twitter"` style with the only difference that it doesn't output anything before the first minute.
|
|
402
425
|
|
|
403
426
|
```js
|
|
404
427
|
timeAgo.format(new Date(), 'twitter-first-minute')
|
|
405
|
-
// 0 seconds ago
|
|
428
|
+
// 0 seconds ago: ""
|
|
406
429
|
|
|
407
430
|
timeAgo.format(new Date() - 29 * 1000, 'twitter-first-minute')
|
|
408
|
-
// 29 seconds ago
|
|
431
|
+
// 29 seconds ago: ""
|
|
409
432
|
|
|
410
433
|
timeAgo.format(new Date() - 30 * 1000, 'twitter-first-minute')
|
|
411
|
-
// 30 seconds ago
|
|
434
|
+
// 30 seconds ago: "1m"
|
|
412
435
|
|
|
413
436
|
// The rest is same as "twitter" style.
|
|
414
437
|
```
|
|
415
438
|
|
|
416
|
-
## Custom
|
|
439
|
+
## Custom Style
|
|
440
|
+
|
|
441
|
+
A custom "style" object may be passed as a second argument to `.format(date, style)` function. A `style` object should have two properties: `labels` and `steps`.
|
|
442
|
+
|
|
443
|
+
Refer to the definition of the [built-in styles](https://github.com/catamphetamine/javascript-time-ago/tree/master/source/style) for an example.
|
|
444
|
+
|
|
445
|
+
<details>
|
|
446
|
+
<summary>How does a formatting style work</summary>
|
|
447
|
+
|
|
448
|
+
######
|
|
449
|
+
|
|
450
|
+
The time range (in seconds) spans from `-∞` to `+∞`, with "now" at `0` point. This entire range gets split into intervals, each with its own label. Example:
|
|
451
|
+
|
|
452
|
+
* Interval from `0` to `-1 second` is assigned `"just now"` label.
|
|
453
|
+
* Interval from `-1 second` to `-1 minute` is assigned `"{0} second(s) ago"` label.
|
|
454
|
+
* Interval from `-1 minute` to `-1 hour` is assigned `"{0} minute(s) ago"` label.
|
|
455
|
+
* ...
|
|
456
|
+
* Interval from `0` to `+1 second` is assigned `"in a moment"` label.
|
|
457
|
+
* Interval from `+1 second` to `+1 minute` is assigned `"in {0} second(s)"` label.
|
|
458
|
+
* Interval from `+1 minute` to `+1 hour` is assigned `"in {0} minute(s)"` label.
|
|
459
|
+
* ...
|
|
460
|
+
|
|
461
|
+
Intervals follow each other without any gaps, so the entire time range is divided into such intervals.
|
|
417
462
|
|
|
418
|
-
|
|
463
|
+
Now the job of the `format(date)` function is simple: it calculates the time difference (in seconds) between `date` and "now", and then maps that number onto the time range to see what interval it falls into. Then it returns the label for that interval, replacing `{0}` with the time difference number converted to the unit of time used by the interval.
|
|
464
|
+
|
|
465
|
+
For example, for `const date = new Date(Date.now() - 2 * 60 * 1000)`, `format(date)` first calculates the difference between the `date` and `Date.now()`, which is `-2 * 60` seconds, and then maps those `-2 * 60` seconds onto the time range and finds that it falls into the `-1 min … -1 hour` interval. So it returns the label for that interval, which is `"{0} minute(s) ago"`, replacing `{0}` with `2` because the unit of time used by the interval is `"minute"` which is equal to `60` seconds, so `-2 * 60 / 60 === -2`.
|
|
466
|
+
|
|
467
|
+
As one can see, with this approach, there could be an infinite amount of all kinds of formatting styles, and any possible formatting style could be expressed with this simple logic.
|
|
468
|
+
</details>
|
|
419
469
|
|
|
420
470
|
### Labels
|
|
421
471
|
|
|
422
|
-
`labels` should be the
|
|
472
|
+
`labels` property value should be the type of labels to output. There're several types of labels available:
|
|
423
473
|
|
|
424
|
-
|
|
474
|
+
* Labels that're provided by [Unicode CLDR](http://cldr.unicode.org/) for all languages:
|
|
475
|
+
* `long` labels are the "normal" ones. Example: `"1 minute ago"`.
|
|
476
|
+
* `short` labels are an abbreviated version of `long` ones. Example: `"1 min. ago"`.
|
|
477
|
+
* `narrow` labels are supposed to be shorter than `short` ones but for some reason they look fine for some languages and weird for other ones. For example, a `short` label for `"1 day ago"` is `"1d ago"` in English, which looks fine, and `"-1 d."` in Russian, which looks weird. So I personally don't use `narrow` labels.
|
|
478
|
+
* Labels that're provided by the community via pull requests, available for a [subset](https://github.com/catamphetamine/javascript-time-ago/tree/master/locale-more-styles) of languages. If your language is missing from the list, you could create a pull request for it.
|
|
479
|
+
* `mini` labels are the shortest. Example: `"1m"`.
|
|
480
|
+
* `now` labels describe the time interval between `-1 second` and `+1 second`. There're 3 labels total: one for `-0.5 sec`, one for `0 sec`, and one for `0.5 sec`. Example: `"just now"`, `"now"`, `"in a moment"`.
|
|
425
481
|
|
|
426
|
-
|
|
482
|
+
The default value for the `labels` property is `"long"`.
|
|
427
483
|
|
|
428
|
-
|
|
484
|
+
`labels` can also be an array of label types, in which case the first supported label type will be used. For example, for `labels: ["mini", "short"]` it will search for `"mini"` labels first and then fall back to `"short"` labels if `"mini"` labels aren't defined for the language. This could be useful when defining a "style" with labels that might not be defined for all languages.
|
|
429
485
|
|
|
430
|
-
|
|
486
|
+
One could also supply custom labels. To do that, define `past` and `future` labels for each unit of time — `second`, `minute`, `hour`, `day`, `week`, `month`, `quarter`, `year` — and then pass the object to `TimeAgo.addLabels()` function.
|
|
431
487
|
|
|
432
488
|
```js
|
|
433
489
|
import TimeAgo from 'javascript-time-ago'
|
|
434
490
|
import en from 'javascript-time-ago/locale/en'
|
|
491
|
+
|
|
492
|
+
// Steps from the built-in "round" style can be reused in custom styles.
|
|
435
493
|
import { round } from 'javascript-time-ago/steps'
|
|
436
494
|
|
|
437
495
|
TimeAgo.addDefaultLocale(en)
|
|
438
496
|
|
|
497
|
+
// Define custom labels.
|
|
439
498
|
const customLabels = {
|
|
440
499
|
second: {
|
|
441
500
|
past: {
|
|
@@ -450,115 +509,123 @@ const customLabels = {
|
|
|
450
509
|
...
|
|
451
510
|
}
|
|
452
511
|
|
|
512
|
+
// Add the custom labels for English language under "custom" name.
|
|
453
513
|
TimeAgo.addLabels('en', 'custom', customLabels)
|
|
454
514
|
|
|
515
|
+
// Create English formatter.
|
|
455
516
|
const timeAgo = new TimeAgo('en-US')
|
|
456
517
|
|
|
518
|
+
// Define a custom style that reuses the `steps` from the built-in "round" style
|
|
519
|
+
// and uses the newly added "custom" labels.
|
|
457
520
|
const customStyle = {
|
|
458
521
|
steps: round,
|
|
459
522
|
labels: 'custom'
|
|
460
523
|
}
|
|
461
524
|
|
|
525
|
+
// Format a "10 seconds ago" date using the new custom style.
|
|
462
526
|
timeAgo.format(Date.now() - 10 * 1000, customStyle)
|
|
463
|
-
// "10 seconds earlier"
|
|
527
|
+
// Returns "10 seconds earlier"
|
|
464
528
|
```
|
|
465
529
|
|
|
466
530
|
### Steps
|
|
467
531
|
|
|
468
|
-
|
|
532
|
+
`steps` property should define a set of intervals that cover the time difference from `0` to `±∞`.
|
|
533
|
+
|
|
534
|
+
The `.format()` function starts at the first step and then moves to next one as long as the time difference passes the `minTime` threshold of the next step. The process is repeated until it stops at a certain step. That step is used to output the label.
|
|
469
535
|
|
|
470
|
-
|
|
536
|
+
If the `.format()` function doesn't pass the `minTime` threshold of the first step then it just outputs an empty string.
|
|
471
537
|
|
|
472
|
-
|
|
538
|
+
Here's an example of `steps` that're used in the built-in `"round"` style:
|
|
473
539
|
|
|
474
540
|
```js
|
|
475
541
|
[
|
|
476
542
|
{
|
|
477
|
-
//
|
|
543
|
+
// Starting from the time difference `0`,
|
|
544
|
+
// use "second" labels.
|
|
478
545
|
formatAs: 'second'
|
|
479
546
|
},
|
|
480
547
|
{
|
|
481
|
-
//
|
|
482
|
-
|
|
483
|
-
// "minute" labels are used for formatting the output.
|
|
548
|
+
// When the time difference becomes at least 1 minute (after rounding),
|
|
549
|
+
// use "minute" labels.
|
|
484
550
|
formatAs: 'minute'
|
|
485
551
|
},
|
|
486
552
|
{
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
// "hour" labels are used for formatting the output.
|
|
553
|
+
// When the time difference becomes at least 1 hour (after rounding),
|
|
554
|
+
// use "hour" labels.
|
|
490
555
|
formatAs: 'hour'
|
|
491
556
|
},
|
|
492
557
|
…
|
|
493
558
|
]
|
|
494
559
|
```
|
|
495
560
|
|
|
496
|
-
|
|
561
|
+
Each "step" could be described by the following properties:
|
|
562
|
+
* `formatAs?: string` — The labels for which unit of time to use in the output: `"second"`, `"minute"`, etc.
|
|
563
|
+
* `format?: (date) => string?` — If a developer doesn't like to use the standard `formatAs` labels, they could output any custom label for a given `date`.
|
|
564
|
+
* `minTime?: number` — The minimum time difference (in seconds) required for the step. When not specified, will be derived from `formatAs` property — for example, `formatAs: "minute"` → `minTime: 60` with `round: "floor"`.
|
|
497
565
|
|
|
498
566
|
<!-- * `minTime: number` — A minimum time interval (in seconds) required for this step, meaning that `minTime` controls the progression from one step to another. The first step's `minTime` is `0` by default. -->
|
|
499
567
|
|
|
500
568
|
<!-- In some cases, when using `unit`s that may or may not be defined for a language, a developer could support both cases: when the `unit` is available and when it's not. For that, they'd use a special `minTime: { [stepId]: minTime, ..., default: minTime }` property that overrides `min` when the previous eligible step's `id` is `[stepId]`. -->
|
|
501
569
|
|
|
502
|
-
<!-- * `formatAs: string` — A
|
|
570
|
+
<!-- * `formatAs: string` — A unit of time, the labels for which are used to generate the output of this step. If the unit of time isn't supported by the language, then the step is ignored. The units of time that're supported in all languages are: `second`, `minute`, `hour`, `day`, `week`, `quarter`, `month`, `year`. For [some languages](https://github.com/catamphetamine/javascript-time-ago/tree/master/locale-more-styles), this library also defines `now` unit (`"just now"`). -->
|
|
503
571
|
|
|
504
572
|
<!-- * `factor` — A divider for the time interval value which is in seconds. For example, if `unit` is `"seconds"` then `factor` should be `1`, and if `unit` is `"minutes"` then `factor` should be `60` because to get the amount of minutes one should divide the amout of seconds by `60`. This `factor` property is actually a redundant one and can be derived from `unit` so it will be removed in the next major version. -->
|
|
505
573
|
|
|
506
574
|
<!-- * `granularity` — (advanced) Time interval value "granularity". For example, it could be set to `5` for minutes to allow only 5-minute increments when formatting time intervals: `0 minutes`, `5 minutes`, `10 minutes`, etc. Perhaps this feature will be removed because there seem to be no use cases of it in the real world. -->
|
|
507
575
|
|
|
508
|
-
|
|
576
|
+
<details>
|
|
577
|
+
<summary>More on <code>minTime</code></summary>
|
|
509
578
|
|
|
510
|
-
|
|
579
|
+
######
|
|
511
580
|
|
|
512
|
-
|
|
581
|
+
`minTime` threshold is the minimum time difference (in seconds) required for the step. Basically, `minTime` controls the progression from one step to another: it can't progress to the next step until the time difference (in seconds) reaches the `minTime` of that step.
|
|
513
582
|
|
|
514
|
-
|
|
583
|
+
Every step has to specify `minTime`, either explicitly or implicitly. An example of "implicitly" would be the first step's `minTime` which is `0` by default.
|
|
515
584
|
|
|
516
|
-
|
|
517
|
-
minTime(
|
|
518
|
-
date: number, // The date argument, converted to a timestamp.
|
|
519
|
-
{
|
|
520
|
-
future: boolean, // Is `true` if `date > now`, or if `date === now`
|
|
521
|
-
// and `future: true` option was passed to `.format()`.
|
|
522
|
-
|
|
523
|
-
getMinTimeForUnit(unit: string, prevUnit: string?): number?
|
|
524
|
-
// Returns the `minTime` for a transition from a
|
|
525
|
-
// previous step with `formatAs: prevUnit` to a
|
|
526
|
-
// step with `formatAs: unit`.
|
|
527
|
-
// For example, if the previous step is `formatAs: "second"`,
|
|
528
|
-
// then `getMinTimeForUnit('minute')` returns `59.5`
|
|
529
|
-
// when `round` is "round", and `60` when `round` is "floor".
|
|
530
|
-
// A developer can also explicitly specify the previous step's unit:
|
|
531
|
-
// `getMinTimeForUnit('minute', 'second')`.
|
|
532
|
-
// Returns `undefined` if `unit` or `prevUnit` are not supported.
|
|
533
|
-
}
|
|
534
|
-
): number
|
|
535
|
-
```
|
|
585
|
+
Another example of "implicitly" would be under-the-hood calcuation of `minTime` based on the unit of time of the step: when a step specifies [`formatAs`](#formatas) property, and the previous step also specifies `formatAs` property, then the step's `minTime`, when not explicitly specified, is calculated automatically from those `formatAs` properties according to the selected ["rounding"](#rounding). For example, if the previous step is `{ formatAs: "second" }` and the current step is `{ formatAs: "minute" }` then the current step's `minTime` is automatically calculated to be `59.5` when `round` is set to `"round"` (default), or `60` when `round` is set to `"floor"`.
|
|
536
586
|
|
|
537
|
-
|
|
587
|
+
So in almost all cases, the library automatically calculates `minTime` for you. However, if you're implementing some kind of an extravagant custom "style" then for steps that don't specify `formatAs` property you will be required to specify `minTime` explicitly.
|
|
538
588
|
|
|
539
|
-
|
|
589
|
+
In that case, even though `minTime` could be specified as a number, the recommended way is to specify it as a `function` returning a number because that would be the only way to account for the [rounding](#rounding) setting, which, by default, is `"round"`, but could also be `"floor"`.
|
|
540
590
|
|
|
541
|
-
|
|
591
|
+
* `minTime()` — Returns the `minTime` number.
|
|
592
|
+
* Arguments:
|
|
593
|
+
* `date: number` — The `date`, converted to a timestamp number. Not a time difference.
|
|
594
|
+
* `parameters: object`
|
|
595
|
+
* `future: boolean` — Is `true` either when `date > now` or when `date === now` and `future: true` parameter was passed to `.format()`.
|
|
596
|
+
* `getMinTimeForUnit(unit: string, prevUnit?: string): number?` — returns the minimum time difference (in seconds) required for moving from a step with `formatAs: prevUnit` to a step with `formatAs: unit`.
|
|
597
|
+
* Example:
|
|
598
|
+
* When `round` is set to `"round"` (default), `getMinTimeForUnit("minute", "second")` returns `59.5`, meaning that the minimum time difference for moving from `"second"` labels to `"minute"` labels is `59.5` seconds, and for smaller time differences it should stay at `"second"` labels.
|
|
599
|
+
* When `round` is set to `"foor"`, `getMinTimeForUnit("minute", "second")` returns `60`, meaning that the minimum time difference for moving from `"second"` labels to `"minute"` labels is `60` seconds, and for smaller time differences it should stay at `"second"` labels.
|
|
600
|
+
* The second argument — `prevUnit` — is not required and will be automatically set to the previous step's `formatAs` property value.
|
|
601
|
+
* If `unit` or `prevUnit` argument is invalid, `getMinTimeForUnit()` will not throw an error and will just return `undefined`.
|
|
602
|
+
</details>
|
|
542
603
|
|
|
543
|
-
|
|
604
|
+
<details>
|
|
605
|
+
<summary>More on <code>formatAs</code></summary>
|
|
544
606
|
|
|
545
|
-
|
|
546
|
-
format(
|
|
547
|
-
date: number, // The date argument, converted to a timestamp.
|
|
548
|
-
locale: string, // The currently selected language. Example: "en".
|
|
549
|
-
{
|
|
550
|
-
formatAs(unit: string, value: number): string,
|
|
551
|
-
// A function that could be used to format `value` in `unit`s.
|
|
552
|
-
// Example: `formatAs('second', -2)`
|
|
553
|
-
// Outputs: "2 seconds ago"
|
|
607
|
+
######
|
|
554
608
|
|
|
555
|
-
|
|
609
|
+
`formatAs` is the unit of time, the labels for which will be used to generate the output of the step. If the specified unit of time isn't supported by the language (as explained further), then such step is ignored. The units of time that're supported in all languages are: `second`, `minute`, `hour`, `day`, `week`, `month`, `quarter`, `year`. For [some languages](https://github.com/catamphetamine/javascript-time-ago/tree/master/locale-more-styles), this library defines an additional unit called `now` which can be used to output labels like `"just now"`. So if a step specifies `formatAs: "now"` and `now` unit isn't supported in a given language then such step is just skipped. If your language doesn't have a `now.json`, send a pull request (native speakers only).
|
|
610
|
+
</details>
|
|
556
611
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
612
|
+
<details>
|
|
613
|
+
<summary>More on <code>format()</code></summary>
|
|
614
|
+
|
|
615
|
+
######
|
|
616
|
+
|
|
617
|
+
A developer might prefer to output a custom label for a given step. In that case, instead of specifying `formatAs` property, they should specify a `format()` function.
|
|
618
|
+
|
|
619
|
+
* `format()` — Returns a custom label, or `undefined` for an empty output.
|
|
620
|
+
* Arguments:
|
|
621
|
+
* `date: number` — The `date`, converted to a timestamp number. Not a time difference.
|
|
622
|
+
* `locale: string` — The selected language. Example: `"en"`.
|
|
623
|
+
* `parameters: object`
|
|
624
|
+
* `formatAs(unit: string, amount: number): string` — A function that can be used to get a label for an `amount` of given `unit`s of time.
|
|
625
|
+
* Example: `formatAs('second', -2)` returns `"2 seconds ago"`.
|
|
626
|
+
* `now: number` — The current date's timestamp. Use it instead of `Date.now()` to avoid the issue of `Date.now()` being slightly different for each different step.
|
|
627
|
+
* `future: boolean` — Is `true` either when `date > now` or when `date === now` and `future: true` parameter was passed to `.format()`.
|
|
628
|
+
</details>
|
|
562
629
|
|
|
563
630
|
<!--
|
|
564
631
|
##### Built-in `steps`
|
|
@@ -574,27 +641,6 @@ import { round } from 'javascript-time-ago/steps'
|
|
|
574
641
|
|
|
575
642
|
<!-- * [`"approximate"`](https://github.com/catamphetamine/javascript-time-ago/blob/master/source/steps/approximate.js) — (legacy) The `steps` used in the [`"approximate"`](https://github.com/catamphetamine/javascript-time-ago/blob/master/source/style/approximate.js) style. -->
|
|
576
643
|
|
|
577
|
-
##### Time unit constants
|
|
578
|
-
|
|
579
|
-
<!-- The `/steps` export provides a few utility time unit constants. --><!-- and functions. -->
|
|
580
|
-
|
|
581
|
-
`/steps` export provides some utility time unit constants that could be used to calculate `minTime` values when defining custom `steps`:
|
|
582
|
-
|
|
583
|
-
```js
|
|
584
|
-
import { minute, hour, day, week, month, year } from 'javascript-time-ago/steps'
|
|
585
|
-
|
|
586
|
-
// In seconds
|
|
587
|
-
minute === 60
|
|
588
|
-
hour === 60 * 60
|
|
589
|
-
day === 24 * 60 * 60
|
|
590
|
-
...
|
|
591
|
-
|
|
592
|
-
const customSteps = [{
|
|
593
|
-
minTime: 5 * minute,
|
|
594
|
-
...
|
|
595
|
-
}]
|
|
596
|
-
```
|
|
597
|
-
|
|
598
644
|
<!--
|
|
599
645
|
The `/steps` export also provides a utility function:
|
|
600
646
|
|
|
@@ -629,123 +675,145 @@ getDate(1500000000000) === getDate(new Date('2017-07-14'))
|
|
|
629
675
|
<!--
|
|
630
676
|
### Units
|
|
631
677
|
|
|
632
|
-
A list of allowed time interval measurement units. Example: `["second", "minute", "hour", ...]`. By default, all available units are defined. This property can be used to filter out some of the non-conventional time
|
|
633
|
-
|
|
634
|
-
### Rounding
|
|
635
|
-
|
|
636
|
-
Controls the rounding of an amount of time units.
|
|
637
|
-
|
|
638
|
-
The default `round` is `"round"` which equals to `Math.round()`.
|
|
639
|
-
|
|
640
|
-
* `0.1` sec. ago → `"0 sec. ago"`
|
|
641
|
-
* `0.4` sec. ago → `"0 sec. ago"`
|
|
642
|
-
* `0.5` sec. ago → `"1 sec. ago"`
|
|
643
|
-
* `0.9` sec. ago → `"1 sec. ago"`
|
|
644
|
-
* `1.0` sec. ago → `"1 sec. ago"`
|
|
645
|
-
|
|
646
|
-
Some people [asked](https://github.com/catamphetamine/javascript-time-ago/issues/38#issuecomment-707094043) for an alternative rounding method, so there's also `round: "floor"` which equals to `Math.floor()`.
|
|
678
|
+
A list of allowed time interval measurement units. Example: `["second", "minute", "hour", ...]`. By default, all available units are defined. This property can be used to filter out some of the non-conventional units of time like `"quarter"` which is present in [CLDR](http://cldr.unicode.org/) data. -->
|
|
647
679
|
|
|
648
|
-
|
|
649
|
-
* `0.4` sec. ago → `"0 sec. ago"`
|
|
650
|
-
* `0.5` sec. ago → `"0 sec. ago"`
|
|
651
|
-
* `0.9` sec. ago → `"0 sec. ago"`
|
|
652
|
-
* `1.0` sec. ago → `"1 sec. ago"`
|
|
653
|
-
|
|
654
|
-
A developer can choose the rounding method by passing `round` option to `timeAgo.format(date, [style], options)`. The default rounding can also be set for a [style](#styles) by setting its `round` property.
|
|
680
|
+
## Refreshing
|
|
655
681
|
|
|
656
|
-
|
|
682
|
+
After a time label has been rendered, it should periodically be updated (refreshed), because the label will change as the time flows. For that, the application needs to know how often it should refresh the label. It could refresh the label every second but that wouldn't be very efficient. For example, there's absolutely no need to refresh a `"1 year ago"` label every second. So how does one know how soon should the label be refreshed?
|
|
657
683
|
|
|
658
|
-
|
|
684
|
+
This library provides an easy way to know when is the best time to refresh the label:
|
|
659
685
|
|
|
660
686
|
```js
|
|
661
|
-
timeAgo.format(
|
|
662
|
-
// "in 5 minutes"
|
|
687
|
+
const [text, refreshDelay] = timeAgo.format(date, { getTimeToNextUpdate: true })
|
|
663
688
|
```
|
|
664
689
|
|
|
665
|
-
|
|
690
|
+
Example:
|
|
666
691
|
|
|
667
692
|
```js
|
|
668
|
-
|
|
693
|
+
let timerId
|
|
669
694
|
|
|
670
|
-
|
|
671
|
-
|
|
695
|
+
function renderLoop() {
|
|
696
|
+
const [text, refreshDelay] = timeAgo.format(date, { getTimeToNextUpdate: true })
|
|
672
697
|
|
|
673
|
-
|
|
674
|
-
|
|
698
|
+
// Update the label text.
|
|
699
|
+
timeLabel.textContent = text
|
|
675
700
|
|
|
676
|
-
|
|
701
|
+
if (refreshDelay !== undefined) {
|
|
702
|
+
timerId = setTimeout(renderLoop, refreshDelay)
|
|
703
|
+
}
|
|
704
|
+
}
|
|
677
705
|
|
|
678
|
-
|
|
679
|
-
//
|
|
706
|
+
// Render the label.
|
|
707
|
+
// It will automatically be refreshed when needed
|
|
708
|
+
timeLabel = createTimeLabelElement()
|
|
709
|
+
renderLoop()
|
|
680
710
|
|
|
681
|
-
|
|
682
|
-
|
|
711
|
+
// And when the label is no longer rendered, one should manually stop the periodic refresh.
|
|
712
|
+
clearTimeout(timerId)
|
|
683
713
|
```
|
|
684
714
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
The `.format()` function accepts an optional `now: number` option: it can be used in tests to specify the exact "base" timestamp relative to which the time interval will be calculated.
|
|
715
|
+
The code above could be simplified by passing a `refresh` function instead of `getTimeToNextUpdate: true` parameter:
|
|
688
716
|
|
|
689
717
|
```js
|
|
690
|
-
timeAgo.format(
|
|
691
|
-
//
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
718
|
+
const [text, stopRefreshing] = timeAgo.format(date, {
|
|
719
|
+
// It will automatically call the `refresh()` function every time the label needs to be refreshed.
|
|
720
|
+
refresh: (text) => {
|
|
721
|
+
// Update the label text.
|
|
722
|
+
timeLabel.textContent = text
|
|
723
|
+
}
|
|
724
|
+
})
|
|
697
725
|
|
|
698
|
-
|
|
726
|
+
// Render the label.
|
|
727
|
+
// It will automatically be refreshed when needed.
|
|
728
|
+
timeLabel = createTimeLabelElement()
|
|
729
|
+
timeLabel.textContent = text
|
|
699
730
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
date: number, // The date argument, converted to a timestamp.
|
|
703
|
-
{
|
|
704
|
-
getTimeToNextUpdateForUnit(unit: string): number?,
|
|
705
|
-
// Returns "time to next update" for a time unit.
|
|
706
|
-
// This is what the library calls internally
|
|
707
|
-
// when `formatAs` is configured for a `step`.
|
|
708
|
-
// Example: `getTimeToNextUpdateForUnit('minute')`.
|
|
709
|
-
// Can return `undefined` in edge cases:
|
|
710
|
-
// for example, when `unit` is "now".
|
|
711
|
-
|
|
712
|
-
now: number, // The current date timestamp.
|
|
713
|
-
|
|
714
|
-
future: boolean // Is `true` if `date > now`, or if `date === now`
|
|
715
|
-
// and `future: true` option was passed to `.format()`.
|
|
716
|
-
}
|
|
717
|
-
): number?
|
|
731
|
+
// And when the label is no longer rendered, one should manually stop the periodic refresh.
|
|
732
|
+
stopRefreshing()
|
|
718
733
|
```
|
|
719
734
|
|
|
720
|
-
|
|
735
|
+
If you're only using the built-in "styles", the returned `timeToNextUpdate` number is always defined. If you're using a custom "style", it is possible that it doesn't support `timeToNextUpdate` feature "out of the box" and in that case the returned `timeToNextUpdate` could be `undefined`. So as a general rule, when using a custom "style", always check that `timeToNextUpdate` is not `undefined`, and if it is, then assign some default refresh interval to it — like one minute (`60 * 1000`).
|
|
736
|
+
|
|
737
|
+
<!--
|
|
738
|
+
<details>
|
|
739
|
+
<summary>An example of how an application could use <code>timeToNextUpdate</code> to refresh the relative time label.</summary>
|
|
721
740
|
|
|
722
741
|
```js
|
|
723
742
|
const timeAgo = new TimeAgo('en-US')
|
|
724
743
|
|
|
744
|
+
const DEFAULT_REFRESH_INTERVAL = 60 * 1000
|
|
745
|
+
|
|
746
|
+
// `setTimeout()` timer reference.
|
|
725
747
|
let updateTimer
|
|
726
748
|
|
|
727
|
-
function
|
|
728
|
-
// Format the date.
|
|
729
|
-
const [
|
|
749
|
+
function renderLabel() {
|
|
750
|
+
// Format the date and get the time to next update.
|
|
751
|
+
const [text, timeToNextUpdate] = timeAgo.format(date, {
|
|
730
752
|
getTimeToNextUpdate: true
|
|
731
753
|
})
|
|
732
754
|
// Update the label.
|
|
733
|
-
|
|
734
|
-
// Schedule
|
|
735
|
-
// `timeToNextUpdate`
|
|
736
|
-
|
|
755
|
+
labelElement.innerText = text
|
|
756
|
+
// Schedule a re-render of the label.
|
|
757
|
+
// The returend `timeToNextUpdate` could potentially be `undefined` when using a custom "style",
|
|
758
|
+
// so a developer should provide some sensible default value.
|
|
759
|
+
updateTimer = setTimeoutSafe(renderLabel, timeToNextUpdate || DEFAULT_REFRESH_INTERVAL)
|
|
737
760
|
}
|
|
738
761
|
|
|
739
|
-
//
|
|
740
|
-
|
|
762
|
+
// Start a recursive re-render of the label.
|
|
763
|
+
renderLabel()
|
|
764
|
+
|
|
765
|
+
// `setTimeout()` function has a bug when it fires immediately
|
|
766
|
+
// when the delay is longer than about `24.85` days.
|
|
741
767
|
// https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values
|
|
742
|
-
|
|
743
|
-
function
|
|
744
|
-
|
|
768
|
+
//
|
|
769
|
+
// Since `renderLabel()` function uses `setTimeout()` for recursion,
|
|
770
|
+
// that would mean infinite recursion.
|
|
771
|
+
//
|
|
772
|
+
// `setTimeoutSafe()` function works around that bug
|
|
773
|
+
// by capping the delay at the maximum allowed value.
|
|
774
|
+
//
|
|
775
|
+
function setTimeoutSafe(func, delay) {
|
|
776
|
+
return setTimeout(func, getSafeTimeoutDelay(delay))
|
|
777
|
+
}
|
|
778
|
+
function getSafeTimeoutDelay(delay) {
|
|
779
|
+
return Math.min(delay, 2147483647)
|
|
745
780
|
}
|
|
746
781
|
```
|
|
782
|
+
</details>
|
|
783
|
+
-->
|
|
747
784
|
|
|
748
|
-
|
|
785
|
+
<!-- The `getTimeToNextUpdate: true` feature works out-of-the-box with any of the [built-in](#rounding) formatting "styles". For it to work well with a [custom](#custom-style) formatting "style", it has to satisfy certain conditions. -->
|
|
786
|
+
|
|
787
|
+
<details>
|
|
788
|
+
<summary>See the conditions that a custom formatting "style" has to meet in order to work well with <code>getTimeToNextUpdate: true</code> feature</summary>
|
|
789
|
+
|
|
790
|
+
* When each `step` in a given formatting "style" specifies a `minTime`, `timeToNextUpdate` can easily be calcuated by subtracting the current time difference from the `minTime` threshold of the next step.
|
|
791
|
+
* For steps that don't explicitly specify `minTime`, `minTime` can still be derived from `formatAs` property.
|
|
792
|
+
* For steps that don't specify niether `minTime` nor `formatAs` property, the calculation of `timeToNextUpdate` becomes slightly more complicated: such steps are required to specify a `getTimeToNextUpdate()` function property.
|
|
793
|
+
* `getTimeToNextUpdate()`
|
|
794
|
+
* Returns:
|
|
795
|
+
* `timeToNextUpdate: number?` — The time until next update. If it returns `undefined` then it's interpreted as "the current label is constant and will not change" — for example, `"Jan 1, 2000"`.
|
|
796
|
+
* Arguments:
|
|
797
|
+
* `date: number` — The `date`, converted to a timestamp number. Not a time difference.
|
|
798
|
+
* `parameters: object`
|
|
799
|
+
* `getTimeToNextUpdateForUnit(unit: string): number` — A function that returns the "time until next update" (in milliseconds) for a given `unit` of time. For example, when using the default rounding method, and the argument is `"minute"`, and the current time difference is `60 * 1000` milliseconds, then the current label is gonna be `"1 minute ago"` and the "time until next update" for that label will be `30 * 1000` milliseconds because that's the time after which `"1 minute ago"` label changes into `"2 minutes ago"`.
|
|
800
|
+
* The library itself uses this function when calculating the "time until next update" for steps that don't specify `minTime` but do specify `formatAs` property.
|
|
801
|
+
* `getTimeToNextUpdateForUnit("now")` always returns `undefined`.
|
|
802
|
+
* The return value of the function depends on the exact time that it was called at, i.e. when called at different times, it will produce different results. Example:
|
|
803
|
+
* Consider that the current time difference is `-20` seconds and the unit of time is `"minute"`.
|
|
804
|
+
* Initially, it outputs `"0 minutes ago"` because it rounds `20/60` to `0`.
|
|
805
|
+
* What will `getTimeToNextUpdateForUnit("minute")` return?
|
|
806
|
+
* When `round` value is `"round"` (default), it will return `10 * 1000` milliseconds, because `20 + 10` = `30`, and `30` seconds of time difference is the moment when it rounds `0.5 minute` to `1 minute` and changes the label from `"0 minutes ago"` to `"1 minute ago"`.
|
|
807
|
+
* When `round` value is `"floor"`, it will return `40 * 1000` milliseconds, because `20 + 40` = `60`, and `60` seconds of time difference is the moment when it rounds `1.0 minute` to `1 minute` and changes the label from `"0 minutes ago"` to `"1 minute ago"`.
|
|
808
|
+
* Now imagine that `20` seconds have passed. What will `getTimeToNextUpdateForUnit("minute")` return now?
|
|
809
|
+
* When `round` value is `"round"` (default), it will return `50 * 1000` milliseconds, because `20 + 20 + 50` = `90`, and `90` seconds of time difference is the moment when it rounds `1.5 minute` to `2 minutes` and changes the label from `"1 minute ago"` to `"2 minutes ago"`.
|
|
810
|
+
* When `round` value is `"floor"`, it will return `20 * 1000` milliseconds, because `20 + 20 + 20` = `60`, and `60` seconds of time difference is the moment when it rounds `1.0 minute` to `1 minute` and changes the label from `"0 minutes ago"` to `"1 minute ago"`.
|
|
811
|
+
* As the time goes by, `getTimeToNextUpdateForUnit("minute")` will keep returning a different result every other time it is called.
|
|
812
|
+
* `now: number` — The current date's timestamp. Use it instead of `Date.now()` to avoid the issue of `Date.now()` being slightly different for each different step.
|
|
813
|
+
* `future: boolean` — Is `true` either when `date > now` or when `date === now` and `future: true` parameter was passed to `.format()`.
|
|
814
|
+
<!-- * `round: string` — The rounding method that is used to convert a fractional time difference number to an integer. Either `"round"` or `"floor"`. Example: `"0.75 minutes ago"` → `"1 minute ago"` in case of `round: "round"`. -->
|
|
815
|
+
<!-- * `getRoundFunction(round: string): (number) => number` — Returns the rounding function that corresponds to the `round: string` parameter above. For example, for `round: "floor"` it returns `Math.floor` function, and for `round: "round"` (or anything else) it returns `Math.round` function. A developer could use it to convert a fractional time difference number to an integer: `getRoundFunction(round)(number)`. -->
|
|
816
|
+
</details>
|
|
749
817
|
|
|
750
818
|
<!--
|
|
751
819
|
## Caching
|
|
@@ -760,71 +828,111 @@ const object = cache.get('key1', 'key2', ...) || cache.put('key1', 'key2', ...,
|
|
|
760
828
|
```
|
|
761
829
|
-->
|
|
762
830
|
|
|
763
|
-
##
|
|
831
|
+
## Rounding
|
|
832
|
+
|
|
833
|
+
When printing a label such as `"1 minute ago"`, the time difference number has to be rounded for readability. For example, it won't print `"0.49 minutes ago"`. It has to round that number first.
|
|
764
834
|
|
|
765
|
-
|
|
835
|
+
The `round` setting controls how exactly fractional numbers should be rounded to integers.
|
|
836
|
+
|
|
837
|
+
The default `round` setting is `"round"` which follows `Math.round()` behavior.
|
|
838
|
+
|
|
839
|
+
* `0.1` sec. ago → `"0 sec. ago"`
|
|
840
|
+
* `0.4` sec. ago → `"0 sec. ago"`
|
|
841
|
+
* `0.5` sec. ago → `"1 sec. ago"`
|
|
842
|
+
* `0.9` sec. ago → `"1 sec. ago"`
|
|
843
|
+
* `1.0` sec. ago → `"1 sec. ago"`
|
|
766
844
|
|
|
767
|
-
Some people
|
|
768
|
-
|
|
845
|
+
Some people [asked](https://github.com/catamphetamine/javascript-time-ago/issues/38#issuecomment-707094043) for an alternative rounding method, so there's an alternative `round` setting value `"floor"` which follows `Math.floor()` behavior.
|
|
846
|
+
|
|
847
|
+
* `0.1` sec. ago → `"0 sec. ago"`
|
|
848
|
+
* `0.4` sec. ago → `"0 sec. ago"`
|
|
849
|
+
* `0.5` sec. ago → `"0 sec. ago"`
|
|
850
|
+
* `0.9` sec. ago → `"0 sec. ago"`
|
|
851
|
+
* `1.0` sec. ago → `"1 sec. ago"`
|
|
852
|
+
|
|
853
|
+
A developer can specify the preferred rounding by passing a `round` parameter to `timeAgo.format(date, [style,] options)`.
|
|
854
|
+
|
|
855
|
+
The default rounding method could also be specified "globally" for a given [style](#formatting-styles) by specifying a `round` property in the style object.
|
|
856
|
+
|
|
857
|
+
## Past vs Future
|
|
858
|
+
|
|
859
|
+
When given a past date, `.format()` returns an `"... ago"` label.
|
|
860
|
+
|
|
861
|
+
When given a future date, `.format()` returns an `"in ..."` label.
|
|
769
862
|
|
|
770
863
|
```js
|
|
771
|
-
|
|
864
|
+
timeAgo.format(Date.now() + 5 * 60 * 1000)
|
|
865
|
+
// "in 5 minutes"
|
|
772
866
|
```
|
|
773
867
|
|
|
774
|
-
|
|
868
|
+
When given a "now" date, which is neither past, nor future, `.format()` doesn't really know how it should represent the time difference — as `-0` or as `+0`.
|
|
775
869
|
|
|
776
|
-
|
|
870
|
+
By default, it treats it as `-0`, meaning that it will return `"0 seconds ago"` rather than `"in 0 seconds"`.
|
|
777
871
|
|
|
778
|
-
|
|
872
|
+
To instruct `.format()` to treat it as `+0`, pass `future: true` parameter.
|
|
779
873
|
|
|
780
|
-
|
|
874
|
+
```js
|
|
875
|
+
// Without `future: true`
|
|
876
|
+
timeAgo.format(Date.now())
|
|
877
|
+
// "just now"
|
|
781
878
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
879
|
+
// With `future: true`
|
|
880
|
+
timeAgo.format(Date.now(), { future: true })
|
|
881
|
+
// "in a moment"
|
|
882
|
+
```
|
|
785
883
|
|
|
786
|
-
|
|
787
|
-
TimeAgo.addDefaultLocale({
|
|
788
|
-
locale: 'en',
|
|
789
|
-
now: {
|
|
790
|
-
now: {
|
|
791
|
-
current: "now",
|
|
792
|
-
future: "in a moment",
|
|
793
|
-
past: "just now"
|
|
794
|
-
}
|
|
795
|
-
},
|
|
796
|
-
long: {
|
|
797
|
-
year: {
|
|
798
|
-
past: {
|
|
799
|
-
one: "{0} year ago",
|
|
800
|
-
other: "{0} years ago"
|
|
801
|
-
},
|
|
802
|
-
future: {
|
|
803
|
-
one: "in {0} year",
|
|
804
|
-
other: "in {0} years"
|
|
805
|
-
}
|
|
806
|
-
},
|
|
807
|
-
...
|
|
808
|
-
}
|
|
809
|
-
})
|
|
810
|
-
</script>
|
|
884
|
+
## `now` parameter
|
|
811
885
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
886
|
+
The `.format()` function accepts an optional `now: number` parameter. It is used in tests to specify the exact "base" timestamp relative to which the time difference will be calculated.
|
|
887
|
+
|
|
888
|
+
```js
|
|
889
|
+
timeAgo.format(60 * 1000, { now: 0 })
|
|
890
|
+
// "1 minute ago"
|
|
891
|
+
```
|
|
892
|
+
|
|
893
|
+
## Full Date Formatter
|
|
894
|
+
|
|
895
|
+
Usually, when rendering a "relative" time label, one should also provide an "absolute" time label, perhaps somewhere in a tooltip, for the user to be able to know the precise date/time of the event.
|
|
896
|
+
|
|
897
|
+
This library exports a "helper" date formatter for just that:
|
|
898
|
+
|
|
899
|
+
```js
|
|
900
|
+
import FullDateFormatter from 'javascript-time-ago/full-date-formatter'
|
|
901
|
+
|
|
902
|
+
const formatter = new FullDateFormatter('en')
|
|
903
|
+
const tooltipText = formatter.format(date)
|
|
904
|
+
// Example output: "Saturday, January 1, 2000 at 3:00:00 AM"
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
`FullDateFormatter` uses [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) under the hood, which works in all modern browsers, and falls back to `Date.toString()` for ancient web browsers like Internet Explorer.
|
|
908
|
+
|
|
909
|
+
## React
|
|
910
|
+
|
|
911
|
+
For React users, there's a [React version](https://www.npmjs.com/package/react-time-ago) of this library. See [demo](https://catamphetamine.gitlab.io/react-time-ago/).
|
|
912
|
+
|
|
913
|
+
## `Intl.RelativeTimeFormat` Polyfill
|
|
914
|
+
|
|
915
|
+
This library uses an [`Intl.RelativeTimeFormat`](https://www.npmjs.com/package/relative-time-format) polyfill under the hood. That polyfill is only [`3.5 kB`](https://github.com/tc39/proposal-intl-relative-time#polyfills) in size so it won't affect the total bundle size.
|
|
916
|
+
|
|
917
|
+
Still, some people have
|
|
918
|
+
[requested](https://github.com/catamphetamine/javascript-time-ago/issues/21) the ability to use native [`Intl.RelativeTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat) and [`Intl.PluralRules`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/PluralRules) instead of the polyfills. To do that, pass `polyfill: false` option when creating a `TimeAgo` instance.
|
|
919
|
+
|
|
920
|
+
```js
|
|
921
|
+
new TimeAgo('en-US', { polyfill: false })
|
|
815
922
|
```
|
|
816
923
|
|
|
817
|
-
|
|
924
|
+
<!--
|
|
925
|
+
## `Intl` Polyfill
|
|
818
926
|
|
|
819
|
-
(this is an "advanced" section)
|
|
927
|
+
(this is an "advanced details" section and you should probably skip it)
|
|
820
928
|
|
|
821
|
-
[`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) global object is not required
|
|
929
|
+
[`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) global object is not required by this library, but some "special" formatting styles may still use it. For example, `"twitter"` style uses `Intl.DateTimeFormat` to format long intervals as absolute dates — for example, `Jan 1` or `Jan 1, 2000` — and if `Intl` is not available, it falls back to formatting those intervals as `1d`, `1mo`, `1yr`.
|
|
822
930
|
|
|
823
|
-
`Intl` is present in all modern web browsers and is absent from some of the
|
|
931
|
+
`Intl` is present in all modern web browsers and is only absent from some of the ancient ones: [Internet Explorer 10, Safari 9 and iOS Safari 9.x](http://caniuse.com/#search=intl). If your application must support those ancient browsers, consider using an [`Intl` polyfill](https://www.npmjs.com/package/intl).
|
|
824
932
|
|
|
825
|
-
Node.js starting from `0.12
|
|
933
|
+
Node.js, starting from version `0.12`, has `Intl` built-in, but it only includes English locale data by default. If your app needs to support more languages than just English on server side (for example, if it employs Server-Side Rendering) then you'll need to use an [`Intl` polyfill](https://www.npmjs.com/package/intl).
|
|
826
934
|
|
|
827
|
-
An example of
|
|
935
|
+
An example of using an [`Intl` polyfill](https://www.npmjs.com/package/intl):
|
|
828
936
|
|
|
829
937
|
```
|
|
830
938
|
npm install intl@1.2.4 --save
|
|
@@ -846,7 +954,7 @@ if (typeof Intl === 'object') {
|
|
|
846
954
|
}
|
|
847
955
|
```
|
|
848
956
|
|
|
849
|
-
Web
|
|
957
|
+
Web browsers (only downloads `intl` package if the web browser doesn't support it, and only downloads the required locales).
|
|
850
958
|
|
|
851
959
|
```js
|
|
852
960
|
async function initIntl() {
|
|
@@ -863,10 +971,7 @@ async function initIntl() {
|
|
|
863
971
|
|
|
864
972
|
initIntl().then(...)
|
|
865
973
|
```
|
|
866
|
-
|
|
867
|
-
## TypeScript
|
|
868
|
-
|
|
869
|
-
This library comes with TypeScript "typings". If you happen to find any bugs in those, create an issue.
|
|
974
|
+
-->
|
|
870
975
|
|
|
871
976
|
<!--
|
|
872
977
|
## Contributing
|
|
@@ -906,6 +1011,45 @@ npm install [module name with version].tar.gz
|
|
|
906
1011
|
```
|
|
907
1012
|
-->
|
|
908
1013
|
|
|
1014
|
+
## CDN
|
|
1015
|
+
|
|
1016
|
+
To include this library directly via a `<script/>` tag on a page, one can use any npm CDN service, e.g. [unpkg.com](https://unpkg.com) or [jsdelivr.com](https://jsdelivr.com)
|
|
1017
|
+
|
|
1018
|
+
```html
|
|
1019
|
+
<!-- Example `[version]`: `2.x` -->
|
|
1020
|
+
<script src="https://unpkg.com/javascript-time-ago@[version]/bundle/javascript-time-ago.js"></script>
|
|
1021
|
+
|
|
1022
|
+
<script>
|
|
1023
|
+
TimeAgo.addDefaultLocale({
|
|
1024
|
+
locale: 'en',
|
|
1025
|
+
now: {
|
|
1026
|
+
now: {
|
|
1027
|
+
current: "now",
|
|
1028
|
+
future: "in a moment",
|
|
1029
|
+
past: "just now"
|
|
1030
|
+
}
|
|
1031
|
+
},
|
|
1032
|
+
long: {
|
|
1033
|
+
year: {
|
|
1034
|
+
past: {
|
|
1035
|
+
one: "{0} year ago",
|
|
1036
|
+
other: "{0} years ago"
|
|
1037
|
+
},
|
|
1038
|
+
future: {
|
|
1039
|
+
one: "in {0} year",
|
|
1040
|
+
other: "in {0} years"
|
|
1041
|
+
}
|
|
1042
|
+
},
|
|
1043
|
+
...
|
|
1044
|
+
}
|
|
1045
|
+
})
|
|
1046
|
+
</script>
|
|
1047
|
+
|
|
1048
|
+
<script>
|
|
1049
|
+
alert(new TimeAgo('en-US').format(new Date()))
|
|
1050
|
+
</script>
|
|
1051
|
+
```
|
|
1052
|
+
|
|
909
1053
|
## Tests
|
|
910
1054
|
|
|
911
1055
|
This component comes with a 100% code coverage.
|
|
@@ -924,7 +1068,7 @@ npm run test-coverage
|
|
|
924
1068
|
|
|
925
1069
|
The code coverage report can be viewed by opening `./coverage/lcov-report/index.html`.
|
|
926
1070
|
|
|
927
|
-
|
|
1071
|
+
Previously, when generating a code coverage report, it used to print a cryptic error message in the console and then produce an empty code coverage report:
|
|
928
1072
|
|
|
929
1073
|
```
|
|
930
1074
|
Handlebars: Access has been denied to resolve the property "statements" because it is not an "own property" of its parent.
|
|
@@ -932,6 +1076,8 @@ You can add a runtime option to disable the check or this warning:
|
|
|
932
1076
|
See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details
|
|
933
1077
|
```
|
|
934
1078
|
|
|
1079
|
+
That has been [worked](https://github.com/handlebars-lang/handlebars.js/issues/1646#issuecomment-578306544) [around](https://github.com/facebook/jest/issues/9396#issuecomment-573328488) by "anchoring" `handlebars` version in `devDependencies` at `handlebars@4.5.3`.
|
|
1080
|
+
|
|
935
1081
|
## GitHub
|
|
936
1082
|
|
|
937
1083
|
On March 9th, 2020, GitHub, Inc. silently [banned](https://medium.com/@catamphetamine/how-github-blocked-me-and-all-my-libraries-c32c61f061d3) my account (erasing all my repos, issues and comments, even in my employer's private repos) without any notice or explanation. Because of that, all source codes had to be promptly moved to GitLab. The [GitHub repo](https://github.com/catamphetamine/javascript-time-ago) is now only used as a backup (you can star the repo there too), and the primary repo is now the [GitLab one](https://gitlab.com/catamphetamine/javascript-time-ago). Issues can be reported in any repo.
|