javascript-time-ago 2.5.11 → 2.6.0

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.
Files changed (452) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +410 -272
  3. package/bundle/javascript-time-ago.js +1 -1
  4. package/bundle/javascript-time-ago.js.map +1 -1
  5. package/bundle/javascript-time-ago.min.js +1 -1
  6. package/bundle/javascript-time-ago.min.js.map +1 -1
  7. package/commonjs/FullDateFormatter.js +72 -0
  8. package/commonjs/FullDateFormatter.js.map +1 -0
  9. package/commonjs/FullDateFormatter.test.js +26 -0
  10. package/commonjs/FullDateFormatter.test.js.map +1 -0
  11. package/commonjs/LocaleDataStore.js +2 -5
  12. package/commonjs/LocaleDataStore.js.map +1 -1
  13. package/commonjs/PropTypes.js +12 -12
  14. package/commonjs/PropTypes.js.map +1 -1
  15. package/commonjs/TimeAgo.js +300 -253
  16. package/commonjs/TimeAgo.js.map +1 -1
  17. package/commonjs/TimeAgo.test.js +214 -135
  18. package/commonjs/TimeAgo.test.js.map +1 -1
  19. package/commonjs/cache.js +7 -25
  20. package/commonjs/cache.js.map +1 -1
  21. package/commonjs/cache.test.js +1 -3
  22. package/commonjs/cache.test.js.map +1 -1
  23. package/commonjs/isStyleObject.js +9 -9
  24. package/commonjs/isStyleObject.js.map +1 -1
  25. package/commonjs/isStyleObject.test.js +1 -3
  26. package/commonjs/isStyleObject.test.js.map +1 -1
  27. package/commonjs/locale.js +6 -19
  28. package/commonjs/locale.js.map +1 -1
  29. package/commonjs/locale.test.js +2 -11
  30. package/commonjs/locale.test.js.map +1 -1
  31. package/commonjs/round.js +2 -5
  32. package/commonjs/round.js.map +1 -1
  33. package/commonjs/steps/approximate.js +1 -4
  34. package/commonjs/steps/approximate.js.map +1 -1
  35. package/commonjs/steps/approximate.test.js +1 -6
  36. package/commonjs/steps/approximate.test.js.map +1 -1
  37. package/commonjs/steps/getStep.js +36 -53
  38. package/commonjs/steps/getStep.js.map +1 -1
  39. package/commonjs/steps/getStep.test.js +7 -7
  40. package/commonjs/steps/getStep.test.js.map +1 -1
  41. package/commonjs/steps/getStepDenominator.js +2 -5
  42. package/commonjs/steps/getStepDenominator.js.map +1 -1
  43. package/commonjs/steps/getStepDenominator.test.js +1 -3
  44. package/commonjs/steps/getStepDenominator.test.js.map +1 -1
  45. package/commonjs/steps/getStepMinTime.js +31 -39
  46. package/commonjs/steps/getStepMinTime.js.map +1 -1
  47. package/commonjs/steps/getStepMinTime.test.js +1 -3
  48. package/commonjs/steps/getStepMinTime.test.js.map +1 -1
  49. package/commonjs/steps/getTimeToNextUpdate.js +27 -41
  50. package/commonjs/steps/getTimeToNextUpdate.js.map +1 -1
  51. package/commonjs/steps/getTimeToNextUpdate.test.js +29 -25
  52. package/commonjs/steps/getTimeToNextUpdate.test.js.map +1 -1
  53. package/commonjs/steps/getTimeToNextUpdateForUnit.js +3 -12
  54. package/commonjs/steps/getTimeToNextUpdateForUnit.js.map +1 -1
  55. package/commonjs/steps/getTimeToNextUpdateForUnit.test.js +1 -7
  56. package/commonjs/steps/getTimeToNextUpdateForUnit.test.js.map +1 -1
  57. package/commonjs/steps/helpers.js +1 -2
  58. package/commonjs/steps/helpers.js.map +1 -1
  59. package/commonjs/steps/helpers.test.js +0 -1
  60. package/commonjs/steps/helpers.test.js.map +1 -1
  61. package/commonjs/steps/index.js +1 -6
  62. package/commonjs/steps/index.js.map +1 -1
  63. package/commonjs/steps/renameLegacyProperties.js +6 -14
  64. package/commonjs/steps/renameLegacyProperties.js.map +1 -1
  65. package/commonjs/steps/renameLegacyProperties.test.js +1 -3
  66. package/commonjs/steps/renameLegacyProperties.test.js.map +1 -1
  67. package/commonjs/steps/round.js +1 -2
  68. package/commonjs/steps/round.js.map +1 -1
  69. package/commonjs/steps/round.test.js +1 -7
  70. package/commonjs/steps/round.test.js.map +1 -1
  71. package/commonjs/steps/units.js +10 -21
  72. package/commonjs/steps/units.js.map +1 -1
  73. package/commonjs/style/approximate.js +2 -6
  74. package/commonjs/style/approximate.js.map +1 -1
  75. package/commonjs/style/approximateTime.js +2 -6
  76. package/commonjs/style/approximateTime.js.map +1 -1
  77. package/commonjs/style/approximateTime.test.js +116 -75
  78. package/commonjs/style/approximateTime.test.js.map +1 -1
  79. package/commonjs/style/getStyleByName.js +3 -29
  80. package/commonjs/style/getStyleByName.js.map +1 -1
  81. package/commonjs/style/mini.js +9 -6
  82. package/commonjs/style/mini.js.map +1 -1
  83. package/commonjs/style/mini.test.js +15 -25
  84. package/commonjs/style/mini.test.js.map +1 -1
  85. package/commonjs/style/miniMinute.js +8 -13
  86. package/commonjs/style/miniMinute.js.map +1 -1
  87. package/commonjs/style/miniMinute.test.js +11 -17
  88. package/commonjs/style/miniMinute.test.js.map +1 -1
  89. package/commonjs/style/miniMinuteNow.js +8 -13
  90. package/commonjs/style/miniMinuteNow.js.map +1 -1
  91. package/commonjs/style/miniMinuteNow.test.js +11 -17
  92. package/commonjs/style/miniMinuteNow.test.js.map +1 -1
  93. package/commonjs/style/miniNow.js +8 -13
  94. package/commonjs/style/miniNow.js.map +1 -1
  95. package/commonjs/style/miniNow.test.js +11 -17
  96. package/commonjs/style/miniNow.test.js.map +1 -1
  97. package/commonjs/style/renameLegacyProperties.js +7 -13
  98. package/commonjs/style/renameLegacyProperties.js.map +1 -1
  99. package/commonjs/style/renameLegacyProperties.test.js +1 -4
  100. package/commonjs/style/renameLegacyProperties.test.js.map +1 -1
  101. package/commonjs/style/round.js +2 -6
  102. package/commonjs/style/round.js.map +1 -1
  103. package/commonjs/style/round.test.js +51 -67
  104. package/commonjs/style/round.test.js.map +1 -1
  105. package/commonjs/style/roundMinute.js +8 -13
  106. package/commonjs/style/roundMinute.js.map +1 -1
  107. package/commonjs/style/roundMinute.test.js +20 -27
  108. package/commonjs/style/roundMinute.test.js.map +1 -1
  109. package/commonjs/style/twitter.js +34 -39
  110. package/commonjs/style/twitter.js.map +1 -1
  111. package/commonjs/style/twitter.test.js +54 -57
  112. package/commonjs/style/twitter.test.js.map +1 -1
  113. package/commonjs/style/twitterFirstMinute.js +10 -15
  114. package/commonjs/style/twitterFirstMinute.js.map +1 -1
  115. package/commonjs/style/twitterFirstMinute.test.js +16 -23
  116. package/commonjs/style/twitterFirstMinute.test.js.map +1 -1
  117. package/commonjs/style/twitterMinute.js +8 -13
  118. package/commonjs/style/twitterMinute.js.map +1 -1
  119. package/commonjs/style/twitterMinute.test.js +20 -25
  120. package/commonjs/style/twitterMinute.test.js.map +1 -1
  121. package/commonjs/style/twitterMinuteNow.js +8 -13
  122. package/commonjs/style/twitterMinuteNow.js.map +1 -1
  123. package/commonjs/style/twitterMinuteNow.test.js +20 -25
  124. package/commonjs/style/twitterMinuteNow.test.js.map +1 -1
  125. package/commonjs/style/twitterNow.js +8 -13
  126. package/commonjs/style/twitterNow.js.map +1 -1
  127. package/commonjs/style/twitterNow.test.js +18 -24
  128. package/commonjs/style/twitterNow.test.js.map +1 -1
  129. package/full-date-formatter/index.cjs +4 -0
  130. package/full-date-formatter/index.cjs.js +9 -0
  131. package/full-date-formatter/index.d.ts +6 -0
  132. package/full-date-formatter/index.js +1 -0
  133. package/full-date-formatter/package.json +15 -0
  134. package/index.cjs +1 -1
  135. package/index.cjs.js +2 -2
  136. package/index.d.ts +14 -4
  137. package/index.js +3 -1
  138. package/load-all-locales/index.cjs +32 -0
  139. package/load-all-locales/index.cjs.js +32 -0
  140. package/load-all-locales/index.js +64 -0
  141. package/locale/ak/package.json +16 -0
  142. package/locale/ak.json +267 -0
  143. package/locale/ak.json.d.ts +4 -0
  144. package/locale/ak.json.js +267 -0
  145. package/locale/bal-Latn/package.json +16 -0
  146. package/locale/bal-Latn.json +240 -0
  147. package/locale/bal-Latn.json.d.ts +4 -0
  148. package/locale/bal-Latn.json.js +240 -0
  149. package/locale/be-tarask/package.json +16 -0
  150. package/locale/be-tarask.json +259 -0
  151. package/locale/be-tarask.json.d.ts +4 -0
  152. package/locale/be-tarask.json.js +259 -0
  153. package/locale/blo/package.json +16 -0
  154. package/locale/blo.json +159 -0
  155. package/locale/blo.json.d.ts +4 -0
  156. package/locale/blo.json.js +159 -0
  157. package/locale/bn.json +5 -2
  158. package/locale/bn.json.js +5 -2
  159. package/locale/bs.json +8 -2
  160. package/locale/bs.json.js +8 -2
  161. package/locale/ccp.json +2 -8
  162. package/locale/ccp.json.js +2 -8
  163. package/locale/csw/package.json +16 -0
  164. package/locale/csw.json +180 -0
  165. package/locale/csw.json.d.ts +4 -0
  166. package/locale/csw.json.js +180 -0
  167. package/locale/cy.json +6 -12
  168. package/locale/cy.json.js +6 -12
  169. package/locale/doi/package.json +16 -0
  170. package/locale/doi.json +159 -0
  171. package/locale/doi.json.d.ts +4 -0
  172. package/locale/doi.json.js +159 -0
  173. package/locale/en-AU.json +12 -12
  174. package/locale/en-AU.json.js +12 -12
  175. package/locale/en-CZ/package.json +16 -0
  176. package/locale/en-CZ.json +219 -0
  177. package/locale/en-CZ.json.d.ts +4 -0
  178. package/locale/en-CZ.json.js +219 -0
  179. package/locale/en-ES/package.json +16 -0
  180. package/locale/en-ES.json +219 -0
  181. package/locale/en-ES.json.d.ts +4 -0
  182. package/locale/en-ES.json.js +219 -0
  183. package/locale/en-FR/package.json +16 -0
  184. package/locale/en-FR.json +219 -0
  185. package/locale/en-FR.json.d.ts +4 -0
  186. package/locale/en-FR.json.js +219 -0
  187. package/locale/en-GS/package.json +16 -0
  188. package/locale/en-GS.json +219 -0
  189. package/locale/en-GS.json.d.ts +4 -0
  190. package/locale/en-GS.json.js +219 -0
  191. package/locale/en-HU/package.json +16 -0
  192. package/locale/en-HU.json +219 -0
  193. package/locale/en-HU.json.d.ts +4 -0
  194. package/locale/en-HU.json.js +219 -0
  195. package/locale/en-ID/package.json +16 -0
  196. package/locale/en-ID.json +219 -0
  197. package/locale/en-ID.json.d.ts +4 -0
  198. package/locale/en-ID.json.js +219 -0
  199. package/locale/en-IT/package.json +16 -0
  200. package/locale/en-IT.json +219 -0
  201. package/locale/en-IT.json.d.ts +4 -0
  202. package/locale/en-IT.json.js +219 -0
  203. package/locale/en-NO/package.json +16 -0
  204. package/locale/en-NO.json +219 -0
  205. package/locale/en-NO.json.d.ts +4 -0
  206. package/locale/en-NO.json.js +219 -0
  207. package/locale/en-PL/package.json +16 -0
  208. package/locale/en-PL.json +219 -0
  209. package/locale/en-PL.json.d.ts +4 -0
  210. package/locale/en-PL.json.js +219 -0
  211. package/locale/en-PT/package.json +16 -0
  212. package/locale/en-PT.json +219 -0
  213. package/locale/en-PT.json.d.ts +4 -0
  214. package/locale/en-PT.json.js +219 -0
  215. package/locale/en-RO/package.json +16 -0
  216. package/locale/en-RO.json +219 -0
  217. package/locale/en-RO.json.d.ts +4 -0
  218. package/locale/en-RO.json.js +219 -0
  219. package/locale/en-SK/package.json +16 -0
  220. package/locale/en-SK.json +219 -0
  221. package/locale/en-SK.json.d.ts +4 -0
  222. package/locale/en-SK.json.js +219 -0
  223. package/locale/es-MX.json +12 -9
  224. package/locale/es-MX.json.js +12 -9
  225. package/locale/es-SV.json +2 -2
  226. package/locale/es-SV.json.js +2 -2
  227. package/locale/eu.json +24 -24
  228. package/locale/eu.json.js +24 -24
  229. package/locale/ha.json +119 -65
  230. package/locale/ha.json.js +119 -65
  231. package/locale/he.json +0 -6
  232. package/locale/he.json.js +0 -6
  233. package/locale/hi-Latn.json +2 -2
  234. package/locale/hi-Latn.json.js +2 -2
  235. package/locale/ht/package.json +16 -0
  236. package/locale/ht.json +207 -0
  237. package/locale/ht.json.d.ts +4 -0
  238. package/locale/ht.json.js +207 -0
  239. package/locale/id.json +1 -1
  240. package/locale/id.json.js +1 -1
  241. package/locale/ie/package.json +16 -0
  242. package/locale/ie.json +159 -0
  243. package/locale/ie.json.d.ts +4 -0
  244. package/locale/ie.json.js +159 -0
  245. package/locale/ig.json +17 -17
  246. package/locale/ig.json.js +17 -17
  247. package/locale/ii/package.json +16 -0
  248. package/locale/ii.json +165 -0
  249. package/locale/ii.json.d.ts +4 -0
  250. package/locale/ii.json.js +165 -0
  251. package/locale/is.json +4 -1
  252. package/locale/is.json.js +4 -1
  253. package/locale/kaa/package.json +16 -0
  254. package/locale/kaa.json +159 -0
  255. package/locale/kaa.json.d.ts +4 -0
  256. package/locale/kaa.json.js +159 -0
  257. package/locale/kok-Latn/package.json +16 -0
  258. package/locale/kok-Latn.json +159 -0
  259. package/locale/kok-Latn.json.d.ts +4 -0
  260. package/locale/kok-Latn.json.js +159 -0
  261. package/locale/kok.json +8 -8
  262. package/locale/kok.json.js +8 -8
  263. package/locale/ks.json +9 -9
  264. package/locale/ks.json.js +9 -9
  265. package/locale/ku.json +55 -46
  266. package/locale/ku.json.js +55 -46
  267. package/locale/lld/package.json +16 -0
  268. package/locale/lld.json +291 -0
  269. package/locale/lld.json.d.ts +4 -0
  270. package/locale/lld.json.js +291 -0
  271. package/locale/mi.json +22 -22
  272. package/locale/mi.json.js +22 -22
  273. package/locale/ms-Arab/package.json +16 -0
  274. package/locale/ms-Arab.json +159 -0
  275. package/locale/ms-Arab.json.d.ts +4 -0
  276. package/locale/ms-Arab.json.js +159 -0
  277. package/locale/nb.json +2 -2
  278. package/locale/nb.json.js +2 -2
  279. package/locale/ne.json +1 -1
  280. package/locale/ne.json.js +1 -1
  281. package/locale/nn.json +4 -4
  282. package/locale/nn.json.js +4 -4
  283. package/locale/no.json +2 -2
  284. package/locale/no.json.js +2 -2
  285. package/locale/nqo/package.json +16 -0
  286. package/locale/nqo.json +159 -0
  287. package/locale/nqo.json.d.ts +4 -0
  288. package/locale/nqo.json.js +159 -0
  289. package/locale/om/package.json +16 -0
  290. package/locale/om.json +213 -0
  291. package/locale/om.json.d.ts +4 -0
  292. package/locale/om.json.js +213 -0
  293. package/locale/or.json +1 -1
  294. package/locale/or.json.js +1 -1
  295. package/locale/qu.json +46 -46
  296. package/locale/qu.json.js +46 -46
  297. package/locale/rhg/package.json +16 -0
  298. package/locale/rhg.json +159 -0
  299. package/locale/rhg.json.d.ts +4 -0
  300. package/locale/rhg.json.js +159 -0
  301. package/locale/sl.json +12 -12
  302. package/locale/sl.json.js +12 -12
  303. package/locale/sr-Cyrl-BA.json +2 -2
  304. package/locale/sr-Cyrl-BA.json.js +2 -2
  305. package/locale/sr-Latn-BA.json +2 -2
  306. package/locale/sr-Latn-BA.json.js +2 -2
  307. package/locale/sv.json +1 -1
  308. package/locale/sv.json.js +1 -1
  309. package/locale/syr/package.json +16 -0
  310. package/locale/syr.json +303 -0
  311. package/locale/syr.json.d.ts +4 -0
  312. package/locale/syr.json.js +303 -0
  313. package/locale/ti.json +6 -6
  314. package/locale/ti.json.js +6 -6
  315. package/locale/tn/package.json +16 -0
  316. package/locale/tn.json +159 -0
  317. package/locale/tn.json.d.ts +4 -0
  318. package/locale/tn.json.js +159 -0
  319. package/locale/to.json +26 -26
  320. package/locale/to.json.js +26 -26
  321. package/locale/uk.json +1 -1
  322. package/locale/uk.json.js +1 -1
  323. package/locale/ur-IN.json +9 -6
  324. package/locale/ur-IN.json.js +9 -6
  325. package/locale/vec/package.json +16 -0
  326. package/locale/vec.json +267 -0
  327. package/locale/vec.json.d.ts +4 -0
  328. package/locale/vec.json.js +267 -0
  329. package/locale/xnr/package.json +16 -0
  330. package/locale/xnr.json +159 -0
  331. package/locale/xnr.json.d.ts +4 -0
  332. package/locale/xnr.json.js +159 -0
  333. package/locale/yo-BJ.json +17 -17
  334. package/locale/yo-BJ.json.js +17 -17
  335. package/locale/yo.json +17 -17
  336. package/locale/yo.json.js +17 -17
  337. package/modules/FullDateFormatter.js +67 -0
  338. package/modules/FullDateFormatter.js.map +1 -0
  339. package/modules/FullDateFormatter.test.js +22 -0
  340. package/modules/FullDateFormatter.test.js.map +1 -0
  341. package/modules/LocaleDataStore.js +2 -3
  342. package/modules/LocaleDataStore.js.map +1 -1
  343. package/modules/PropTypes.js +11 -7
  344. package/modules/PropTypes.js.map +1 -1
  345. package/modules/TimeAgo.js +299 -237
  346. package/modules/TimeAgo.js.map +1 -1
  347. package/modules/TimeAgo.test.js +217 -119
  348. package/modules/TimeAgo.test.js.map +1 -1
  349. package/modules/cache.js +6 -22
  350. package/modules/cache.js.map +1 -1
  351. package/modules/cache.test.js.map +1 -1
  352. package/modules/isStyleObject.js +9 -7
  353. package/modules/isStyleObject.js.map +1 -1
  354. package/modules/isStyleObject.test.js.map +1 -1
  355. package/modules/locale.js +6 -16
  356. package/modules/locale.js.map +1 -1
  357. package/modules/locale.test.js +0 -4
  358. package/modules/locale.test.js.map +1 -1
  359. package/modules/round.js +2 -3
  360. package/modules/round.js.map +1 -1
  361. package/modules/steps/approximate.js +6 -2
  362. package/modules/steps/approximate.js.map +1 -1
  363. package/modules/steps/approximate.test.js +0 -2
  364. package/modules/steps/approximate.test.js.map +1 -1
  365. package/modules/steps/getStep.js +36 -48
  366. package/modules/steps/getStep.js.map +1 -1
  367. package/modules/steps/getStep.test.js +6 -2
  368. package/modules/steps/getStep.test.js.map +1 -1
  369. package/modules/steps/getStepDenominator.js +2 -3
  370. package/modules/steps/getStepDenominator.js.map +1 -1
  371. package/modules/steps/getStepDenominator.test.js.map +1 -1
  372. package/modules/steps/getStepMinTime.js +31 -36
  373. package/modules/steps/getStepMinTime.js.map +1 -1
  374. package/modules/steps/getStepMinTime.test.js.map +1 -1
  375. package/modules/steps/getTimeToNextUpdate.js +28 -32
  376. package/modules/steps/getTimeToNextUpdate.js.map +1 -1
  377. package/modules/steps/getTimeToNextUpdate.test.js +27 -18
  378. package/modules/steps/getTimeToNextUpdate.test.js.map +1 -1
  379. package/modules/steps/getTimeToNextUpdateForUnit.js +4 -10
  380. package/modules/steps/getTimeToNextUpdateForUnit.js.map +1 -1
  381. package/modules/steps/getTimeToNextUpdateForUnit.test.js +0 -4
  382. package/modules/steps/getTimeToNextUpdateForUnit.test.js.map +1 -1
  383. package/modules/steps/helpers.js +1 -1
  384. package/modules/steps/helpers.js.map +1 -1
  385. package/modules/steps/helpers.test.js.map +1 -1
  386. package/modules/steps/index.js +2 -1
  387. package/modules/steps/index.js.map +1 -1
  388. package/modules/steps/renameLegacyProperties.js +6 -13
  389. package/modules/steps/renameLegacyProperties.js.map +1 -1
  390. package/modules/steps/renameLegacyProperties.test.js.map +1 -1
  391. package/modules/steps/round.js.map +1 -1
  392. package/modules/steps/round.test.js +0 -3
  393. package/modules/steps/round.test.js.map +1 -1
  394. package/modules/steps/units.js +5 -9
  395. package/modules/steps/units.js.map +1 -1
  396. package/modules/style/approximate.js +5 -1
  397. package/modules/style/approximate.js.map +1 -1
  398. package/modules/style/approximateTime.js +6 -2
  399. package/modules/style/approximateTime.js.map +1 -1
  400. package/modules/style/approximateTime.test.js +115 -70
  401. package/modules/style/approximateTime.test.js.map +1 -1
  402. package/modules/style/getStyleByName.js +4 -17
  403. package/modules/style/getStyleByName.js.map +1 -1
  404. package/modules/style/mini.js +8 -4
  405. package/modules/style/mini.js.map +1 -1
  406. package/modules/style/mini.test.js +14 -20
  407. package/modules/style/mini.test.js.map +1 -1
  408. package/modules/style/miniMinute.js +6 -6
  409. package/modules/style/miniMinute.js.map +1 -1
  410. package/modules/style/miniMinute.test.js +10 -12
  411. package/modules/style/miniMinute.test.js.map +1 -1
  412. package/modules/style/miniMinuteNow.js +6 -6
  413. package/modules/style/miniMinuteNow.js.map +1 -1
  414. package/modules/style/miniMinuteNow.test.js +10 -12
  415. package/modules/style/miniMinuteNow.test.js.map +1 -1
  416. package/modules/style/miniNow.js +6 -6
  417. package/modules/style/miniNow.js.map +1 -1
  418. package/modules/style/miniNow.test.js +10 -12
  419. package/modules/style/miniNow.test.js.map +1 -1
  420. package/modules/style/renameLegacyProperties.js +8 -10
  421. package/modules/style/renameLegacyProperties.js.map +1 -1
  422. package/modules/style/renameLegacyProperties.test.js +0 -1
  423. package/modules/style/renameLegacyProperties.test.js.map +1 -1
  424. package/modules/style/round.js +3 -2
  425. package/modules/style/round.js.map +1 -1
  426. package/modules/style/round.test.js +50 -62
  427. package/modules/style/round.test.js.map +1 -1
  428. package/modules/style/roundMinute.js +8 -7
  429. package/modules/style/roundMinute.js.map +1 -1
  430. package/modules/style/roundMinute.test.js +19 -22
  431. package/modules/style/roundMinute.test.js.map +1 -1
  432. package/modules/style/twitter.js +36 -33
  433. package/modules/style/twitter.js.map +1 -1
  434. package/modules/style/twitter.test.js +53 -52
  435. package/modules/style/twitter.test.js.map +1 -1
  436. package/modules/style/twitterFirstMinute.js +8 -7
  437. package/modules/style/twitterFirstMinute.js.map +1 -1
  438. package/modules/style/twitterFirstMinute.test.js +15 -18
  439. package/modules/style/twitterFirstMinute.test.js.map +1 -1
  440. package/modules/style/twitterMinute.js +6 -6
  441. package/modules/style/twitterMinute.js.map +1 -1
  442. package/modules/style/twitterMinute.test.js +19 -20
  443. package/modules/style/twitterMinute.test.js.map +1 -1
  444. package/modules/style/twitterMinuteNow.js +6 -6
  445. package/modules/style/twitterMinuteNow.js.map +1 -1
  446. package/modules/style/twitterMinuteNow.test.js +19 -20
  447. package/modules/style/twitterMinuteNow.test.js.map +1 -1
  448. package/modules/style/twitterNow.js +6 -6
  449. package/modules/style/twitterNow.js.map +1 -1
  450. package/modules/style/twitterNow.test.js +17 -19
  451. package/modules/style/twitterNow.test.js.map +1 -1
  452. package/package.json +275 -13
package/README.md CHANGED
@@ -4,24 +4,21 @@
4
4
  [![npm downloads](https://img.shields.io/npm/dm/javascript-time-ago.svg?style=flat-square)](https://www.npmjs.com/package/javascript-time-ago)
5
5
  [![coverage](https://img.shields.io/coveralls/catamphetamine/javascript-time-ago/master.svg?style=flat-square)](https://coveralls.io/r/catamphetamine/javascript-time-ago?branch=master)
6
6
 
7
- Localized relative date/time formatting (both for past and future dates).
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
- * in 2 months
14
+ * 2 days ago
15
+ * in 4 months
19
16
  * in 5 years
20
17
  * …
21
18
 
22
- For React users, there's a [React version](https://www.npmjs.com/package/react-time-ago) [See Demo](https://catamphetamine.gitlab.io/react-time-ago/)
19
+ It also tells one how often to [refresh](#refreshing) the label as the time goes by.
23
20
 
24
- This is a readme for version `2.x`. For older versions, [see version `1.x` readme](https://github.com/catamphetamine/javascript-time-ago/tree/1.x). For migrating from version `1.x` to version `2.x`, see a [migration guide](https://github.com/catamphetamine/javascript-time-ago/blob/master/MIGRATION.md).
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
- If you're not using a bundler then use a [standalone version from a CDN](#cdn).
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
- TimeAgo.addDefaultLocale(en)
43
+ // Add English language
44
+ TimeAgo.addLocale(en)
45
+ ```
43
46
 
44
- // Create formatter (English).
45
- const timeAgo = new TimeAgo('en-US')
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,66 @@ timeAgo.format(Date.now() - 24 * 60 * 60 * 1000)
57
63
  // "1 day ago"
58
64
  ```
59
65
 
60
- ## Locales
66
+ P.S. After rendering a label, don't forget to [refresh](#refreshing) it as the time goes by.
67
+
68
+ ## Languages
69
+
70
+ 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.
71
+
72
+ 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.
61
73
 
62
- This library includes date/time formatting rules and labels for any language.
74
+ 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.
63
75
 
64
- No languages are loaded default: a developer must manually choose which languages should be loaded. Languages should be imported from [`javascript-time-ago/locale`](https://unpkg.com/browse/javascript-time-ago/locale/) and then added via `TimeAgo.addLocale()` or `TimeAgo.addDefaultLocale()`.
76
+ <!-- or `TimeAgo.setDefaultLocale("en")`. By default, the default `locale` is `"en"`, although it still has to be added manually. -->
65
77
 
66
- The `locale` argument of `new TimeAgo(locale)` constructor is matched against the list of added languages, and the first matching one is used. For example, locales `"en-US"` and `"en-GB"` both match `"en"` language. If none of the added languages match the `locale`, the "default language" is used. If the "default language" hasn't been added, an error is thrown.
78
+ 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`.
67
79
 
68
- The "default language" is `"en"` by default, and can be set either by calling `addDefaultLocale()`:
80
+ 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
81
 
70
82
  ```js
71
- import ru from 'javascript-time-ago/locale/ru'
83
+ import en from 'javascript-time-ago/locale/en'
72
84
  import de from 'javascript-time-ago/locale/de'
73
- import es from 'javascript-time-ago/locale/es'
85
+ import fr from 'javascript-time-ago/locale/fr'
74
86
 
75
- TimeAgo.addLocale(ru)
87
+ TimeAgo.addDefaultLocale(en)
76
88
  TimeAgo.addLocale(de)
77
- TimeAgo.addDefaultLocale(es)
89
+ TimeAgo.addLocale(fr)
90
+ ```
91
+
92
+ ```js
93
+ // "es" locale hasn't been added, so it falls back to "en".
94
+ const timeAgo = new TimeAgo('es')
95
+
96
+ timeAgo.format(new Date())
97
+ // "just now"
78
98
  ```
79
99
 
80
- or by calling `setDefaultLocale()`:
100
+ `TimeAgo.addDefaultLocale()` is just a shortcut for `TimeAgo.addLocale()` + `TimeAgo.setDefaultLocale()`, so the code above is the same as the code below.
81
101
 
82
102
  ```js
83
- import ru from 'javascript-time-ago/locale/ru'
103
+ import en from 'javascript-time-ago/locale/en'
84
104
  import de from 'javascript-time-ago/locale/de'
85
- import es from 'javascript-time-ago/locale/es'
105
+ import fr from 'javascript-time-ago/locale/fr'
86
106
 
87
- TimeAgo.addLocale(ru)
107
+ TimeAgo.addLocale(en)
88
108
  TimeAgo.addLocale(de)
89
- TimeAgo.addLocale(es)
109
+ TimeAgo.addLocale(fr)
90
110
 
91
- TimeAgo.setDefaultLocale('es')
111
+ TimeAgo.setDefaultLocale('en')
92
112
  ```
93
113
 
94
- In some cases, a developer might prefer to specify a list of `locales` to choose from rather than a single `locale`:
114
+ `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
115
 
96
116
  ```js
117
+ // Add English and German languages
97
118
  TimeAgo.addDefaultLocale(en)
98
119
  TimeAgo.addLocale(de)
99
120
 
100
- // "de" language will be used, as it's the first one to match.
101
- new TimeAgo(['ru-RU', 'de-DE', 'en-US'])
121
+ // "de" language will be chosen because it's the first one that works.
122
+ const timeAgo = new TimeAgo(['ru-RU', 'de-DE', 'en-US'])
123
+
124
+ timeAgo.format(new Date())
125
+ // "gerade jetzt"
102
126
  ```
103
127
 
104
128
  <!--
@@ -109,15 +133,15 @@ require('javascript-time-ago/load-all-locales')
109
133
  ```
110
134
  -->
111
135
 
112
- An example of using Russian language:
136
+ <!--
137
+ An example of formatting dates in Russian:
113
138
 
114
139
  ```js
115
140
  import TimeAgo from 'javascript-time-ago'
116
-
117
- // Russian.
118
141
  import ru from 'javascript-time-ago/locale/ru'
119
142
 
120
- TimeAgo.addDefaultLocale(ru)
143
+ // Add Russian language.
144
+ TimeAgo.addLocale(ru)
121
145
 
122
146
  const timeAgo = new TimeAgo('ru-RU')
123
147
 
@@ -133,24 +157,40 @@ timeAgo.format(Date.now() - 2 * 60 * 60 * 1000)
133
157
  timeAgo.format(Date.now() - 24 * 60 * 60 * 1000)
134
158
  // "1 день назад"
135
159
  ```
160
+ -->
161
+
162
+ ## Formatting Styles
163
+
164
+ 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:
165
+
166
+ * Interval from `0` to `-1 second` is assigned `"just now"` label.
167
+ * Interval from `-1 second` to `-1 minute` is assigned `"{0} second(s) ago"` label.
168
+ * Interval from `-1 minute` to `-1 hour` is assigned `"{0} minute(s) ago"` label.
169
+ * ...
170
+ * Interval from `0` to `+1 second` is assigned `"in a moment"` label.
171
+ * Interval from `+1 second` to `+1 minute` is assigned `"in {0} second(s)"` label.
172
+ * Interval from `+1 minute` to `+1 hour` is assigned `"in {0} minute(s)"` label.
173
+ * ...
136
174
 
137
- ## Styles
175
+ Intervals follow each other without any gaps, so the entire time range is divided into such intervals.
138
176
 
139
- This library allows for any custom logic for formatting time intervals:
177
+ 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.
140
178
 
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.
179
+ 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`.
142
180
 
143
- * What labels should be used: should it use the standard built-in labels for the languages (`"... minutes ago"`, `"... min. ago"`, `"...m"`), or should it use custom ones, or should it skip using relative time labels in some cases and instead output something like `"Dec 11, 2015"`.
181
+ As one can see, with this approach, there can be an infinite amount of formatting styles, and any possible formatting style can be expressed with this simple logic.
144
182
 
145
- Such configuration comes under the name of "style".
183
+ It could be precise up to a second with `"1 second ago"`, `"2 seconds ago"`, `"3 seconds ago"`, etc labels, or it could hide the amount of seconds under a `"less than minute ago"` label.
146
184
 
147
- While a completely [custom](#custom) "style" could be supplied, this library comes with several [built-in ](https://github.com/catamphetamine/javascript-time-ago/tree/master/source/style) "styles" that some people might find useful.
185
+ 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 choose to output a full date like `"Dec 11, 2015"` starting from `-1 year` threshold.
148
186
 
149
- Following is the list of built-in "styles".
187
+ The possibilities are endless, and it's all defined by a formatting "style".
188
+
189
+ While one could implement their own [custom](#custom-style) formatting "style" from scratch, most developers will find exactly what they're looking for in one of the few built-in styles that're described below.
150
190
 
151
191
  ### Round
152
192
 
153
- Rounds the time up to the closest time measurement unit (second, minute, hour, etc).
193
+ `"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
194
 
155
195
  ```js
156
196
  timeAgo.format(Date.now(), 'round')
@@ -201,7 +241,7 @@ timeAgo.format(Date.now() - 1.5 * 60 * 1000, 'round')
201
241
 
202
242
  ### Round (minute)
203
243
 
204
- Same as `"round"` style but without seconds. This is the default style.
244
+ `"round-minute"` style is same as `"round"` style but without seconds. This is the default style.
205
245
 
206
246
  ```js
207
247
  timeAgo.format(Date.now(), 'round-minute')
@@ -223,7 +263,7 @@ timeAgo.format(Date.now() - 30 * 1000, 'round-minute')
223
263
 
224
264
  ### Mini
225
265
 
226
- Same as `"round"` style but as short as possible and without `" ago"`. Also, [doesn't include](https://github.com/catamphetamine/javascript-time-ago/issues/40) "weeks".
266
+ `"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
267
 
228
268
  ```js
229
269
  timeAgo.format(new Date(), 'mini')
@@ -255,7 +295,7 @@ For best compatibility, `mini.json` labels should be [defined](https://github.co
255
295
 
256
296
  ### Mini (now)
257
297
 
258
- Same as `"mini"` style but outputs `"now"` instead of `"0s"`.
298
+ `"mini-now"` style is same as `"mini"` style with the only difference that it outputs `"now"` instead of `"0s"`.
259
299
 
260
300
  ```js
261
301
  timeAgo.format(new Date(), 'mini-now')
@@ -269,7 +309,7 @@ timeAgo.format(new Date() - 1 * 1000, 'mini-now')
269
309
 
270
310
  ### Mini (minute)
271
311
 
272
- Same as `"mini"` style but without seconds (starts with minutes).
312
+ `"mini-minute"` style is same as `"mini"` style but without seconds.
273
313
 
274
314
  ```js
275
315
  timeAgo.format(new Date(), 'mini-minute')
@@ -286,7 +326,7 @@ timeAgo.format(new Date() - 30 * 1000, 'mini-minute')
286
326
 
287
327
  ### Mini (minute-now)
288
328
 
289
- Same as `"mini-minute"` style but outputs `"now"` instead of `"0m"`.
329
+ `"mini-minute-now"` style is same as `"mini-minute"` style with the only difference that it outputs `"now"` instead of `"0m"`.
290
330
 
291
331
  ```js
292
332
  timeAgo.format(new Date(), 'mini-minute-now')
@@ -322,7 +362,7 @@ timeAgo.format(new Date() - 60 * 1000, 'twitter-first-minute')
322
362
 
323
363
  ### Twitter
324
364
 
325
- Mimics [Twitter](https://twitter.com) style of "time ago" labels (`"1s"`, `"2m"`, `"3h"`, `"Mar 4"`, `"Apr 5, 2012"`)
365
+ `"twitter"` style mimicks [Twitter](https://twitter.com) labels: `"1s"`, `"2m"`, `"3h"`, `"Mar 4"`, `"Apr 5, 2012"`.
326
366
 
327
367
  ```js
328
368
  timeAgo.format(new Date(), 'twitter')
@@ -344,13 +384,13 @@ timeAgo.format(Date.now() - 364 * 24 * 60 * 60 * 1000, 'twitter')
344
384
  // Another year → `month/day/year` ("Mar 5, 2017")
345
385
  ```
346
386
 
347
- `"twitter"` style uses [`Intl`](https://gitlab.com/catamphetamine/relative-time-format#intl) for formatting `day/month/year` labels. If `Intl` is not available (for example, in Internet Explorer), it falls back to day/month/year labels: `"1d"`, `"1mo"`, `"1yr"`.
387
+ `"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
388
 
349
- For best compatibility, `mini.json` labels should be defined for a [locale](https://github.com/catamphetamine/javascript-time-ago/tree/master/locale-more-styles). Send pull requests for the missing ones.
389
+ 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
390
 
351
391
  ### Twitter (now)
352
392
 
353
- Same as `"twitter"` style but outputs `"now"` instead of `"0s"`.
393
+ `"twitter-now"` style is same as `"twitter"` style with the only difference that it outputs `"now"` instead of `"0s"`.
354
394
 
355
395
  ```js
356
396
  timeAgo.format(new Date(), 'twitter-now')
@@ -364,7 +404,7 @@ timeAgo.format(new Date() - 1 * 1000, 'twitter-now')
364
404
 
365
405
  ### Twitter (minute)
366
406
 
367
- Same as `"twitter"` style but without seconds (starts with minutes).
407
+ `"twitter-minute"` style is same as `"twitter"` style but without seconds.
368
408
 
369
409
  ```js
370
410
  timeAgo.format(new Date(), 'twitter-minute')
@@ -381,7 +421,7 @@ timeAgo.format(new Date() - 30 * 1000, 'twitter-minute')
381
421
 
382
422
  ### Twitter (minute-now)
383
423
 
384
- Same as `"twitter-minute"` style but outputs `"now"` instead of `"0m"`.
424
+ `"twitter-minute-now"` style is same as `"twitter-minute"` style with the only difference that it outputs `"now"` instead of `"0m"`.
385
425
 
386
426
  ```js
387
427
  timeAgo.format(new Date(), 'twitter-minute-now')
@@ -398,7 +438,7 @@ timeAgo.format(new Date() - 30 * 1000, 'twitter-minute-now')
398
438
 
399
439
  ### Twitter (first minute)
400
440
 
401
- Same as `"twitter"` style but doesn't output anything before the first minute.
441
+ `"twitter-first-minute"` style is same as `"twitter"` style with the only difference that it doesn't output anything before the first minute.
402
442
 
403
443
  ```js
404
444
  timeAgo.format(new Date(), 'twitter-first-minute')
@@ -413,29 +453,40 @@ timeAgo.format(new Date() - 30 * 1000, 'twitter-first-minute')
413
453
  // The rest is same as "twitter" style.
414
454
  ```
415
455
 
416
- ## Custom
456
+ ## Custom Style
417
457
 
418
- A custom "style" object may be passed as a second parameter to `.format(date, style)`, a `style` being an object defining `labels` and `steps`.
458
+ 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`.
459
+
460
+ Refer to the definition of the [built-in styles](https://github.com/catamphetamine/javascript-time-ago/tree/master/source/style) for an example.
419
461
 
420
462
  ### Labels
421
463
 
422
- `labels` should be the name of the time labels variant that will be used for generating output. When not defined, is set to [`"long"`](#labels) by default.
464
+ `labels` property value should be the type of labels to output. There're several types of labels available:
423
465
 
424
- `labels` can also be an array of such time label variant names, in which case each one of them is tried until a supported one is found. 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.
466
+ * Labels that're provided by [Unicode CLDR](http://cldr.unicode.org/) for all languages:
467
+ * `long` labels are the "normal" ones. Example: `"1 minute ago"`.
468
+ * `short` labels are an abbreviated version of `long` ones. Example: `"1 min. ago"`.
469
+ * `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.
470
+ * 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.
471
+ * `mini` labels are the shortest. Example: `"1m"`.
472
+ * `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
473
 
426
- [`"long"`, `"short"` and `"narrow"`](https://unpkg.com/browse/relative-time-format/locale/en.json) time labels are always present for each language, because they're provided by [CLDR](http://cldr.unicode.org/). `long` is the normal one, `short` is an abbreviated version of `long`, `narrow` is supposed to be shorter than `short` but ends up just being weird: it's either equal to `short` or is, for example, `"-1 d."` for `"1 day ago"` in Russian.
474
+ The default value for the `labels` property is `"long"`.
427
475
 
428
- Other time labels like `"now"` and `"mini"` are only defined for a [small subset](https://github.com/catamphetamine/javascript-time-ago/tree/master/locale-more-styles) of languages. Send your pull requests for the missing ones.
476
+ `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
477
 
430
- New labels can be added by calling `TimeAgo.addLabels()` function.
478
+ 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
479
 
432
480
  ```js
433
481
  import TimeAgo from 'javascript-time-ago'
434
482
  import en from 'javascript-time-ago/locale/en'
483
+
484
+ // Steps from the built-in "round" style can be reused in custom styles.
435
485
  import { round } from 'javascript-time-ago/steps'
436
486
 
437
487
  TimeAgo.addDefaultLocale(en)
438
488
 
489
+ // Define custom labels.
439
490
  const customLabels = {
440
491
  second: {
441
492
  past: {
@@ -450,115 +501,123 @@ const customLabels = {
450
501
  ...
451
502
  }
452
503
 
504
+ // Add the custom labels for English language under "custom" name.
453
505
  TimeAgo.addLabels('en', 'custom', customLabels)
454
506
 
507
+ // Create English formatter.
455
508
  const timeAgo = new TimeAgo('en-US')
456
509
 
510
+ // Define a custom style that reuses the `steps` from the built-in "round" style
511
+ // and uses the newly added "custom" labels.
457
512
  const customStyle = {
458
513
  steps: round,
459
514
  labels: 'custom'
460
515
  }
461
516
 
517
+ // Format a "10 seconds ago" date using the new custom style.
462
518
  timeAgo.format(Date.now() - 10 * 1000, customStyle)
463
- // "10 seconds earlier"
519
+ // Returns "10 seconds earlier"
464
520
  ```
465
521
 
466
522
  ### Steps
467
523
 
468
- Time interval measurement "steps".
524
+ `steps` property should define a set of intervals that cover the time difference from `0` to `±∞`.
469
525
 
470
- Each "step" is tried until the last matching one is found, and that "step" is then used to generate the output. If no matching `step` was found, then an empty string is returned.
526
+ 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.
471
527
 
472
- An example of `"round"` style `steps`:
528
+ If the `.format()` function doesn't pass the `minTime` threshold of the first step then it just outputs an empty string.
529
+
530
+ Here's an example of `steps` that're used in the built-in `"round"` style:
473
531
 
474
532
  ```js
475
533
  [
476
534
  {
477
- // "second" labels are used for formatting the output.
535
+ // Starting from the time difference `0`,
536
+ // use "second" labels.
478
537
  formatAs: 'second'
479
538
  },
480
539
  {
481
- // This step is effective starting from 59.5 seconds.
482
- minTime: 60,
483
- // "minute" labels are used for formatting the output.
540
+ // When the time difference becomes at least 1 minute (after rounding),
541
+ // use "minute" labels.
484
542
  formatAs: 'minute'
485
543
  },
486
544
  {
487
- // This step is effective starting from 59.5 minutes.
488
- minTime: 60 * 60,
489
- // "hour" labels are used for formatting the output.
545
+ // When the time difference becomes at least 1 hour (after rounding),
546
+ // use "hour" labels.
490
547
  formatAs: 'hour'
491
548
  },
492
549
 
493
550
  ]
494
551
  ```
495
552
 
496
- A step can be described by:
553
+ Each "step" could be described by the following properties:
554
+ * `formatAs?: string` — The labels for which unit of time to use in the output: `"second"`, `"minute"`, etc.
555
+ * `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`.
556
+ * `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
557
 
498
558
  <!-- * `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
559
 
500
560
  <!-- 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
561
 
502
- <!-- * `formatAs: string` — A time measurement unit, the labels for which are used to generate the output of this step. If the time unit isn't supported by the language, then the step is ignored. The time units 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"`). -->
562
+ <!-- * `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
563
 
504
564
  <!-- * `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
565
 
506
566
  <!-- * `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
567
 
508
- ##### `minTime`
568
+ <details>
569
+ <summary>More on <code>minTime</code></summary>
509
570
 
510
- A minimum time interval (in seconds) required for a step, meaning that `minTime` controls the progression from one step to another. Every step must define a `minTime` in one way or another. The first step's `minTime` is `0` by default.
571
+ ######
511
572
 
512
- If a step is defined by [`formatAs`](#formatas), and its previous step is also defined by `formatAs`, then such step's `minTime`, if not specified, is calculated automatically 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 as `59.5` when `round` is "round" (default), and `60` when `round` is "floor".
573
+ `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
574
 
514
- While `minTime` is usually a number, it could also be a `function` returning a number in order to support unusual cases like absolute date formatting in [`"twitter"`](https://github.com/catamphetamine/javascript-time-ago/blob/master/source/style/twitter.js) style.
575
+ 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
576
 
516
- ```js
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
- ```
577
+ 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
578
 
537
- ##### `formatAs`
579
+ 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
580
 
539
- A time measurement unit, the labels for which are used to generate the output of a step. If the time unit isn't supported by the language, then the step is ignored. The time units 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 also defines `now` unit (`"just now"`).
581
+ 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
582
 
541
- ##### `format()`
583
+ * `minTime()` — Returns the `minTime` number.
584
+ * Arguments:
585
+ * `date: number` — The `date`, converted to a timestamp number. Not a time difference.
586
+ * `parameters: object`
587
+ * `future: boolean` — Is `true` either when `date > now` or when `date === now` and `future: true` parameter was passed to `.format()`.
588
+ * `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`.
589
+ * Example:
590
+ * 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.
591
+ * 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.
592
+ * The second argument — `prevUnit` — is not required and will be automatically set to the previous step's `formatAs` property value.
593
+ * If `unit` or `prevUnit` argument is invalid, `getMinTimeForUnit()` will not throw an error and will just return `undefined`.
594
+ </details>
542
595
 
543
- Alternatively to `formatAs`, a step may specify a `format()` function:
596
+ <details>
597
+ <summary>More on <code>formatAs</code></summary>
544
598
 
545
- ```js
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"
599
+ ######
554
600
 
555
- now: number, // The current date timestamp.
601
+ `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).
602
+ </details>
556
603
 
557
- future: boolean // Is `true` if `date > now`, or if `date === now`
558
- // and `future: true` option was passed to `.format()`.
559
- }
560
- ): string?
561
- ```
604
+ <details>
605
+ <summary>More on <code>format()</code></summary>
606
+
607
+ ######
608
+
609
+ 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.
610
+
611
+ * `format()` — Returns a custom label, or `undefined` for an empty output.
612
+ * Arguments:
613
+ * `date: number` — The `date`, converted to a timestamp number. Not a time difference.
614
+ * `locale: string` — The selected language. Example: `"en"`.
615
+ * `parameters: object`
616
+ * `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.
617
+ * Example: `formatAs('second', -2)` returns `"2 seconds ago"`.
618
+ * `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.
619
+ * `future: boolean` — Is `true` either when `date > now` or when `date === now` and `future: true` parameter was passed to `.format()`.
620
+ </details>
562
621
 
563
622
  <!--
564
623
  ##### Built-in `steps`
@@ -574,27 +633,6 @@ import { round } from 'javascript-time-ago/steps'
574
633
 
575
634
  <!-- * [`"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
635
 
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
636
  <!--
599
637
  The `/steps` export also provides a utility function:
600
638
 
@@ -629,123 +667,145 @@ getDate(1500000000000) === getDate(new Date('2017-07-14'))
629
667
  <!--
630
668
  ### Units
631
669
 
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 units like `"quarter"` which is present in [CLDR](http://cldr.unicode.org/) data. -->
670
+ 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. -->
633
671
 
634
- ### Rounding
672
+ ## Refreshing
635
673
 
636
- Controls the rounding of an amount of time units.
674
+ 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?
637
675
 
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()`.
647
-
648
- * `0.1` sec. ago → `"0 sec. ago"`
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.
655
-
656
- ## Future
657
-
658
- When given future dates, `.format()` produces the corresponding output.
676
+ This library provides an easy way to know when is the best time to refresh the label:
659
677
 
660
678
  ```js
661
- timeAgo.format(Date.now() + 5 * 60 * 1000)
662
- // "in 5 minutes"
679
+ const [text, refreshDelay] = timeAgo.format(date, { getTimeToNextUpdate: true })
663
680
  ```
664
681
 
665
- Zero time interval is a special case: by default, it's formatted in past time. To format zero time interval in future time, pass `future: true` option to `.format()`.
682
+ Example:
666
683
 
667
684
  ```js
668
- // Without `future: true` option:
685
+ let timerId
669
686
 
670
- timeAgo.format(Date.now())
671
- // "just now"
687
+ function renderLoop() {
688
+ const [text, refreshDelay] = timeAgo.format(date, { getTimeToNextUpdate: true })
672
689
 
673
- timeAgo.format(Date.now() + 5 * 60 * 1000)
674
- // "in 5 minutes"
690
+ // Update the label text.
691
+ timeLabel.textContent = text
675
692
 
676
- // With `future: true` option:
693
+ if (refreshDelay !== undefined) {
694
+ timerId = setTimeout(renderLoop, refreshDelay)
695
+ }
696
+ }
677
697
 
678
- timeAgo.format(Date.now(), { future: true })
679
- // "in a moment"
698
+ // Render the label.
699
+ // It will automatically be refreshed when needed
700
+ timeLabel = createTimeLabelElement()
701
+ renderLoop()
680
702
 
681
- timeAgo.format(Date.now() + 5 * 60 * 1000, { future: true })
682
- // "in 5 minutes" (no difference)
703
+ // And when the label is no longer rendered, one should manually stop the periodic refresh.
704
+ clearTimeout(timerId)
683
705
  ```
684
706
 
685
- ## Now
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.
707
+ The code above could be simplified by passing a `refresh` function instead of `getTimeToNextUpdate: true` parameter:
688
708
 
689
709
  ```js
690
- timeAgo.format(60 * 1000, { now: 0 })
691
- // "1 minute ago"
692
- ```
693
-
694
- ## Update Interval
695
-
696
- When speaking of good User Experience ("UX"), a formatted relative date, once rendered, should be constantly refreshed. And for that, the application should know how often should it refresh the formatted date. For that, each `step` should provide an update interval.
710
+ const [text, stopRefreshing] = timeAgo.format(date, {
711
+ // It will automatically call the `refresh()` function every time the label needs to be refreshed.
712
+ refresh: (text) => {
713
+ // Update the label text.
714
+ timeLabel.textContent = text
715
+ }
716
+ })
697
717
 
698
- When a `step` has `formatAs` configured, then `getTimeToNextUpdate()` function is created automatically for it. Otherwise, a developer should supply their own `getTimeToNextUpdate()` function for a `step`.
718
+ // Render the label.
719
+ // It will automatically be refreshed when needed.
720
+ timeLabel = createTimeLabelElement()
721
+ timeLabel.textContent = text
699
722
 
700
- ```js
701
- getTimeToNextUpdate(
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?
723
+ // And when the label is no longer rendered, one should manually stop the periodic refresh.
724
+ stopRefreshing()
718
725
  ```
719
726
 
720
- The application can then pass `getTimeToNextUpdate: true` option to `.format()` to get the best time to update the relative date label.
727
+ 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`).
728
+
729
+ <!--
730
+ <details>
731
+ <summary>An example of how an application could use <code>timeToNextUpdate</code> to refresh the relative time label.</summary>
721
732
 
722
733
  ```js
723
734
  const timeAgo = new TimeAgo('en-US')
724
735
 
736
+ const DEFAULT_REFRESH_INTERVAL = 60 * 1000
737
+
738
+ // `setTimeout()` timer reference.
725
739
  let updateTimer
726
740
 
727
- function render() {
728
- // Format the date.
729
- const [formattedDate, timeToNextUpdate] = timeAgo.format(date, {
741
+ function renderLabel() {
742
+ // Format the date and get the time to next update.
743
+ const [text, timeToNextUpdate] = timeAgo.format(date, {
730
744
  getTimeToNextUpdate: true
731
745
  })
732
746
  // Update the label.
733
- setFormattedDate(formattedDate)
734
- // Schedule next render.
735
- // `timeToNextUpdate` may be `undefined`, so provide a sensible default.
736
- updateTimer = setTimeout(render, getSafeTimeoutInterval(timeToNextUpdate || 60 * 1000))
747
+ labelElement.innerText = text
748
+ // Schedule a re-render of the label.
749
+ // The returend `timeToNextUpdate` could potentially be `undefined` when using a custom "style",
750
+ // so a developer should provide some sensible default value.
751
+ updateTimer = setTimeoutSafe(renderLabel, timeToNextUpdate || DEFAULT_REFRESH_INTERVAL)
737
752
  }
738
753
 
739
- // `setTimeout()` has a bug where it fires immediately
740
- // when the interval is longer than about `24.85` days.
754
+ // Start a recursive re-render of the label.
755
+ renderLabel()
756
+
757
+ // `setTimeout()` function has a bug when it fires immediately
758
+ // when the delay is longer than about `24.85` days.
741
759
  // https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values
742
- const SET_TIMEOUT_MAX_SAFE_INTERVAL = 2147483647
743
- function getSafeTimeoutInterval(interval) {
744
- return Math.min(interval, SET_TIMEOUT_MAX_SAFE_INTERVAL)
760
+ //
761
+ // Since `renderLabel()` function uses `setTimeout()` for recursion,
762
+ // that would mean infinite recursion.
763
+ //
764
+ // `setTimeoutSafe()` function works around that bug
765
+ // by capping the delay at the maximum allowed value.
766
+ //
767
+ function setTimeoutSafe(func, delay) {
768
+ return setTimeout(func, getSafeTimeoutDelay(delay))
769
+ }
770
+ function getSafeTimeoutDelay(delay) {
771
+ return Math.min(delay, 2147483647)
745
772
  }
746
773
  ```
774
+ </details>
775
+ -->
747
776
 
748
- Notice that `setTimeout()` has a bug where it [fires immediately](https://stackoverflow.com/questions/3468607/why-does-settimeout-break-for-large-millisecond-delay-values) when the interval is longer than about `24.85` days, so the interval should not exceed that number. Otherwise, it will result in an infinite recursion.
777
+ <!-- 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. -->
778
+
779
+ <details>
780
+ <summary>See the conditions that a custom formatting "style" has to meet in order to work well with <code>getTimeToNextUpdate: true</code> feature</summary>
781
+
782
+ * 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.
783
+ * For steps that don't explicitly specify `minTime`, `minTime` can still be derived from `formatAs` property.
784
+ * 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.
785
+ * `getTimeToNextUpdate()`
786
+ * Returns:
787
+ * `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"`.
788
+ * Arguments:
789
+ * `date: number` — The `date`, converted to a timestamp number. Not a time difference.
790
+ * `parameters: object`
791
+ * `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"`.
792
+ * 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.
793
+ * `getTimeToNextUpdateForUnit("now")` always returns `undefined`.
794
+ * 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:
795
+ * Consider that the current time difference is `-20` seconds and the unit of time is `"minute"`.
796
+ * Initially, it outputs `"0 minutes ago"` because it rounds `20/60` to `0`.
797
+ * What will `getTimeToNextUpdateForUnit("minute")` return?
798
+ * 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"`.
799
+ * 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"`.
800
+ * Now imagine that `20` seconds have passed. What will `getTimeToNextUpdateForUnit("minute")` return now?
801
+ * 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"`.
802
+ * 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"`.
803
+ * As the time goes by, `getTimeToNextUpdateForUnit("minute")` will keep returning a different result every other time it is called.
804
+ * `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.
805
+ * `future: boolean` — Is `true` either when `date > now` or when `date === now` and `future: true` parameter was passed to `.format()`.
806
+ <!-- * `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"`. -->
807
+ <!-- * `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)`. -->
808
+ </details>
749
809
 
750
810
  <!--
751
811
  ## Caching
@@ -760,71 +820,111 @@ const object = cache.get('key1', 'key2', ...) || cache.put('key1', 'key2', ...,
760
820
  ```
761
821
  -->
762
822
 
763
- ## Localization internals
823
+ ## Rounding
824
+
825
+ 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.
826
+
827
+ The `round` setting controls how exactly fractional numbers should be rounded to integers.
828
+
829
+ The default `round` setting is `"round"` which follows `Math.round()` behavior.
830
+
831
+ * `0.1` sec. ago → `"0 sec. ago"`
832
+ * `0.4` sec. ago → `"0 sec. ago"`
833
+ * `0.5` sec. ago → `"1 sec. ago"`
834
+ * `0.9` sec. ago → `"1 sec. ago"`
835
+ * `1.0` sec. ago → `"1 sec. ago"`
836
+
837
+ 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.
838
+
839
+ * `0.1` sec. ago → `"0 sec. ago"`
840
+ * `0.4` sec. ago → `"0 sec. ago"`
841
+ * `0.5` sec. ago → `"0 sec. ago"`
842
+ * `0.9` sec. ago → `"0 sec. ago"`
843
+ * `1.0` sec. ago → `"1 sec. ago"`
844
+
845
+ A developer can specify the preferred rounding by passing a `round` parameter to `timeAgo.format(date, [style,] options)`.
846
+
847
+ The default rounding method could also be specified "globally" for a given [style](#styles) by specifying a `round` property in the style object.
848
+
849
+ ## Past vs Future
764
850
 
765
- This library uses an [`Intl.RelativeTimeFormat`](https://www.npmjs.com/package/relative-time-format) polyfill under the hood. The 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.
851
+ When given a past date, `.format()` returns an `"... ago"` label.
766
852
 
767
- Some people have
768
- [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: in this case, pass `polyfill: false` option when creating a `TimeAgo` instance.
853
+ When given a future date, `.format()` returns an `"in ..."` label.
769
854
 
770
855
  ```js
771
- new TimeAgo('en-US', { polyfill: false })
856
+ timeAgo.format(Date.now() + 5 * 60 * 1000)
857
+ // "in 5 minutes"
772
858
  ```
773
859
 
774
- ## React
860
+ 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
861
 
776
- For React users, there's a [React version](https://www.npmjs.com/package/react-time-ago) [See Demo](https://catamphetamine.gitlab.io/react-time-ago/).
862
+ By default, it treats it as `-0`, meaning that it will return `"0 seconds ago"` rather than `"in 0 seconds"`.
777
863
 
778
- ## CDN
864
+ To instruct `.format()` to treat it as `+0`, pass `future: true` parameter.
779
865
 
780
- One can use any npm CDN service, e.g. [unpkg.com](https://unpkg.com) or [jsdelivr.com](https://jsdelivr.com)
866
+ ```js
867
+ // Without `future: true`
868
+ timeAgo.format(Date.now())
869
+ // "just now"
781
870
 
782
- ```html
783
- <!-- Example `[version]`: `2.x` -->
784
- <script src="https://unpkg.com/javascript-time-ago@[version]/bundle/javascript-time-ago.js"></script>
871
+ // With `future: true`
872
+ timeAgo.format(Date.now(), { future: true })
873
+ // "in a moment"
874
+ ```
785
875
 
786
- <script>
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>
876
+ ## `now` parameter
811
877
 
812
- <script>
813
- alert(new TimeAgo('en-US').format(new Date()))
814
- </script>
878
+ 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.
879
+
880
+ ```js
881
+ timeAgo.format(60 * 1000, { now: 0 })
882
+ // "1 minute ago"
815
883
  ```
816
884
 
817
- ## Intl
885
+ ## Full Date Formatter
818
886
 
819
- (this is an "advanced" section)
887
+ 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.
820
888
 
821
- [`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) global object is not required for this library, but, for example, if you choose to use the built-in `twitter` style then it will format longer intervals as `1d`, `1mo`, `1yr` instead of `Apr 10` or `Apr 10, 2019` if `Intl` is not available: that's because it uses `Intl.DateTimeFormat` for formatting absolute dates.
889
+ This library exports a "helper" date formatter for just that:
822
890
 
823
- `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](http://caniuse.com/#search=intl) (which can be solved using [`Intl` polyfill](https://github.com/andyearnshaw/Intl.js)).
891
+ ```js
892
+ import FullDateFormatter from 'javascript-time-ago/full-date-formatter'
824
893
 
825
- 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](https://github.com/andyearnshaw/Intl.js).
894
+ const formatter = new FullDateFormatter('en')
895
+ const tooltipText = formatter.format(date)
896
+ // Example output: "Saturday, January 1, 2000 at 3:00:00 AM"
897
+ ```
898
+
899
+ `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.
826
900
 
827
- An example of applying [`Intl` polyfill](https://github.com/andyearnshaw/Intl.js):
901
+ ## React
902
+
903
+ 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/).
904
+
905
+ ## `Intl.RelativeTimeFormat` Polyfill
906
+
907
+ 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.
908
+
909
+ Still, some people have
910
+ [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.
911
+
912
+ ```js
913
+ new TimeAgo('en-US', { polyfill: false })
914
+ ```
915
+
916
+ <!--
917
+ ## `Intl` Polyfill
918
+
919
+ (this is an "advanced details" section and you should probably skip it)
920
+
921
+ [`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`.
922
+
923
+ `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).
924
+
925
+ 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).
926
+
927
+ An example of using an [`Intl` polyfill](https://www.npmjs.com/package/intl):
828
928
 
829
929
  ```
830
930
  npm install intl@1.2.4 --save
@@ -846,7 +946,7 @@ if (typeof Intl === 'object') {
846
946
  }
847
947
  ```
848
948
 
849
- Web browser: only download `intl` package if the web browser doesn't support it, and only download the required locale.
949
+ Web browsers (only downloads `intl` package if the web browser doesn't support it, and only downloads the required locales).
850
950
 
851
951
  ```js
852
952
  async function initIntl() {
@@ -863,10 +963,7 @@ async function initIntl() {
863
963
 
864
964
  initIntl().then(...)
865
965
  ```
866
-
867
- ## TypeScript
868
-
869
- This library comes with TypeScript "typings". If you happen to find any bugs in those, create an issue.
966
+ -->
870
967
 
871
968
  <!--
872
969
  ## Contributing
@@ -906,6 +1003,45 @@ npm install [module name with version].tar.gz
906
1003
  ```
907
1004
  -->
908
1005
 
1006
+ ## CDN
1007
+
1008
+ 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)
1009
+
1010
+ ```html
1011
+ <!-- Example `[version]`: `2.x` -->
1012
+ <script src="https://unpkg.com/javascript-time-ago@[version]/bundle/javascript-time-ago.js"></script>
1013
+
1014
+ <script>
1015
+ TimeAgo.addDefaultLocale({
1016
+ locale: 'en',
1017
+ now: {
1018
+ now: {
1019
+ current: "now",
1020
+ future: "in a moment",
1021
+ past: "just now"
1022
+ }
1023
+ },
1024
+ long: {
1025
+ year: {
1026
+ past: {
1027
+ one: "{0} year ago",
1028
+ other: "{0} years ago"
1029
+ },
1030
+ future: {
1031
+ one: "in {0} year",
1032
+ other: "in {0} years"
1033
+ }
1034
+ },
1035
+ ...
1036
+ }
1037
+ })
1038
+ </script>
1039
+
1040
+ <script>
1041
+ alert(new TimeAgo('en-US').format(new Date()))
1042
+ </script>
1043
+ ```
1044
+
909
1045
  ## Tests
910
1046
 
911
1047
  This component comes with a 100% code coverage.
@@ -924,7 +1060,7 @@ npm run test-coverage
924
1060
 
925
1061
  The code coverage report can be viewed by opening `./coverage/lcov-report/index.html`.
926
1062
 
927
- The `handlebars@4.5.3` [work](https://github.com/handlebars-lang/handlebars.js/issues/1646#issuecomment-578306544)[around](https://github.com/facebook/jest/issues/9396#issuecomment-573328488) in `devDependencies` is for the test coverage to not produce empty reports:
1063
+ 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
1064
 
929
1065
  ```
930
1066
  Handlebars: Access has been denied to resolve the property "statements" because it is not an "own property" of its parent.
@@ -932,6 +1068,8 @@ You can add a runtime option to disable the check or this warning:
932
1068
  See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details
933
1069
  ```
934
1070
 
1071
+ 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`.
1072
+
935
1073
  ## GitHub
936
1074
 
937
1075
  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.