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.
- package/CHANGELOG.md +10 -1
- package/README.md +410 -272
- 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/LocaleDataStore.js +2 -5
- package/commonjs/LocaleDataStore.js.map +1 -1
- package/commonjs/PropTypes.js +12 -12
- package/commonjs/PropTypes.js.map +1 -1
- package/commonjs/TimeAgo.js +300 -253
- package/commonjs/TimeAgo.js.map +1 -1
- package/commonjs/TimeAgo.test.js +214 -135
- package/commonjs/TimeAgo.test.js.map +1 -1
- package/commonjs/cache.js +7 -25
- package/commonjs/cache.js.map +1 -1
- package/commonjs/cache.test.js +1 -3
- package/commonjs/cache.test.js.map +1 -1
- package/commonjs/isStyleObject.js +9 -9
- package/commonjs/isStyleObject.js.map +1 -1
- package/commonjs/isStyleObject.test.js +1 -3
- package/commonjs/isStyleObject.test.js.map +1 -1
- package/commonjs/locale.js +6 -19
- package/commonjs/locale.js.map +1 -1
- package/commonjs/locale.test.js +2 -11
- package/commonjs/locale.test.js.map +1 -1
- package/commonjs/round.js +2 -5
- package/commonjs/round.js.map +1 -1
- package/commonjs/steps/approximate.js +1 -4
- package/commonjs/steps/approximate.js.map +1 -1
- package/commonjs/steps/approximate.test.js +1 -6
- package/commonjs/steps/approximate.test.js.map +1 -1
- package/commonjs/steps/getStep.js +36 -53
- package/commonjs/steps/getStep.js.map +1 -1
- package/commonjs/steps/getStep.test.js +7 -7
- package/commonjs/steps/getStep.test.js.map +1 -1
- package/commonjs/steps/getStepDenominator.js +2 -5
- package/commonjs/steps/getStepDenominator.js.map +1 -1
- package/commonjs/steps/getStepDenominator.test.js +1 -3
- package/commonjs/steps/getStepDenominator.test.js.map +1 -1
- package/commonjs/steps/getStepMinTime.js +31 -39
- package/commonjs/steps/getStepMinTime.js.map +1 -1
- package/commonjs/steps/getStepMinTime.test.js +1 -3
- package/commonjs/steps/getStepMinTime.test.js.map +1 -1
- package/commonjs/steps/getTimeToNextUpdate.js +27 -41
- package/commonjs/steps/getTimeToNextUpdate.js.map +1 -1
- package/commonjs/steps/getTimeToNextUpdate.test.js +29 -25
- package/commonjs/steps/getTimeToNextUpdate.test.js.map +1 -1
- package/commonjs/steps/getTimeToNextUpdateForUnit.js +3 -12
- package/commonjs/steps/getTimeToNextUpdateForUnit.js.map +1 -1
- package/commonjs/steps/getTimeToNextUpdateForUnit.test.js +1 -7
- package/commonjs/steps/getTimeToNextUpdateForUnit.test.js.map +1 -1
- package/commonjs/steps/helpers.js +1 -2
- package/commonjs/steps/helpers.js.map +1 -1
- package/commonjs/steps/helpers.test.js +0 -1
- package/commonjs/steps/helpers.test.js.map +1 -1
- package/commonjs/steps/index.js +1 -6
- package/commonjs/steps/index.js.map +1 -1
- package/commonjs/steps/renameLegacyProperties.js +6 -14
- package/commonjs/steps/renameLegacyProperties.js.map +1 -1
- package/commonjs/steps/renameLegacyProperties.test.js +1 -3
- package/commonjs/steps/renameLegacyProperties.test.js.map +1 -1
- package/commonjs/steps/round.js +1 -2
- package/commonjs/steps/round.js.map +1 -1
- package/commonjs/steps/round.test.js +1 -7
- package/commonjs/steps/round.test.js.map +1 -1
- package/commonjs/steps/units.js +10 -21
- package/commonjs/steps/units.js.map +1 -1
- package/commonjs/style/approximate.js +2 -6
- package/commonjs/style/approximate.js.map +1 -1
- package/commonjs/style/approximateTime.js +2 -6
- package/commonjs/style/approximateTime.js.map +1 -1
- package/commonjs/style/approximateTime.test.js +116 -75
- package/commonjs/style/approximateTime.test.js.map +1 -1
- package/commonjs/style/getStyleByName.js +3 -29
- package/commonjs/style/getStyleByName.js.map +1 -1
- package/commonjs/style/mini.js +9 -6
- package/commonjs/style/mini.js.map +1 -1
- package/commonjs/style/mini.test.js +15 -25
- package/commonjs/style/mini.test.js.map +1 -1
- package/commonjs/style/miniMinute.js +8 -13
- package/commonjs/style/miniMinute.js.map +1 -1
- package/commonjs/style/miniMinute.test.js +11 -17
- package/commonjs/style/miniMinute.test.js.map +1 -1
- package/commonjs/style/miniMinuteNow.js +8 -13
- package/commonjs/style/miniMinuteNow.js.map +1 -1
- package/commonjs/style/miniMinuteNow.test.js +11 -17
- package/commonjs/style/miniMinuteNow.test.js.map +1 -1
- package/commonjs/style/miniNow.js +8 -13
- package/commonjs/style/miniNow.js.map +1 -1
- package/commonjs/style/miniNow.test.js +11 -17
- package/commonjs/style/miniNow.test.js.map +1 -1
- package/commonjs/style/renameLegacyProperties.js +7 -13
- package/commonjs/style/renameLegacyProperties.js.map +1 -1
- package/commonjs/style/renameLegacyProperties.test.js +1 -4
- package/commonjs/style/renameLegacyProperties.test.js.map +1 -1
- package/commonjs/style/round.js +2 -6
- package/commonjs/style/round.js.map +1 -1
- package/commonjs/style/round.test.js +51 -67
- package/commonjs/style/round.test.js.map +1 -1
- package/commonjs/style/roundMinute.js +8 -13
- package/commonjs/style/roundMinute.js.map +1 -1
- package/commonjs/style/roundMinute.test.js +20 -27
- package/commonjs/style/roundMinute.test.js.map +1 -1
- package/commonjs/style/twitter.js +34 -39
- package/commonjs/style/twitter.js.map +1 -1
- package/commonjs/style/twitter.test.js +54 -57
- package/commonjs/style/twitter.test.js.map +1 -1
- package/commonjs/style/twitterFirstMinute.js +10 -15
- package/commonjs/style/twitterFirstMinute.js.map +1 -1
- package/commonjs/style/twitterFirstMinute.test.js +16 -23
- package/commonjs/style/twitterFirstMinute.test.js.map +1 -1
- package/commonjs/style/twitterMinute.js +8 -13
- package/commonjs/style/twitterMinute.js.map +1 -1
- package/commonjs/style/twitterMinute.test.js +20 -25
- package/commonjs/style/twitterMinute.test.js.map +1 -1
- package/commonjs/style/twitterMinuteNow.js +8 -13
- package/commonjs/style/twitterMinuteNow.js.map +1 -1
- package/commonjs/style/twitterMinuteNow.test.js +20 -25
- package/commonjs/style/twitterMinuteNow.test.js.map +1 -1
- package/commonjs/style/twitterNow.js +8 -13
- package/commonjs/style/twitterNow.js.map +1 -1
- package/commonjs/style/twitterNow.test.js +18 -24
- package/commonjs/style/twitterNow.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/load-all-locales/index.cjs +32 -0
- package/load-all-locales/index.cjs.js +32 -0
- package/load-all-locales/index.js +64 -0
- package/locale/ak/package.json +16 -0
- package/locale/ak.json +267 -0
- package/locale/ak.json.d.ts +4 -0
- package/locale/ak.json.js +267 -0
- package/locale/bal-Latn/package.json +16 -0
- package/locale/bal-Latn.json +240 -0
- package/locale/bal-Latn.json.d.ts +4 -0
- package/locale/bal-Latn.json.js +240 -0
- package/locale/be-tarask/package.json +16 -0
- package/locale/be-tarask.json +259 -0
- package/locale/be-tarask.json.d.ts +4 -0
- package/locale/be-tarask.json.js +259 -0
- package/locale/blo/package.json +16 -0
- package/locale/blo.json +159 -0
- package/locale/blo.json.d.ts +4 -0
- package/locale/blo.json.js +159 -0
- package/locale/bn.json +5 -2
- package/locale/bn.json.js +5 -2
- package/locale/bs.json +8 -2
- package/locale/bs.json.js +8 -2
- package/locale/ccp.json +2 -8
- package/locale/ccp.json.js +2 -8
- package/locale/csw/package.json +16 -0
- package/locale/csw.json +180 -0
- package/locale/csw.json.d.ts +4 -0
- package/locale/csw.json.js +180 -0
- package/locale/cy.json +6 -12
- package/locale/cy.json.js +6 -12
- package/locale/doi/package.json +16 -0
- package/locale/doi.json +159 -0
- package/locale/doi.json.d.ts +4 -0
- package/locale/doi.json.js +159 -0
- package/locale/en-AU.json +12 -12
- package/locale/en-AU.json.js +12 -12
- package/locale/en-CZ/package.json +16 -0
- package/locale/en-CZ.json +219 -0
- package/locale/en-CZ.json.d.ts +4 -0
- package/locale/en-CZ.json.js +219 -0
- package/locale/en-ES/package.json +16 -0
- package/locale/en-ES.json +219 -0
- package/locale/en-ES.json.d.ts +4 -0
- package/locale/en-ES.json.js +219 -0
- package/locale/en-FR/package.json +16 -0
- package/locale/en-FR.json +219 -0
- package/locale/en-FR.json.d.ts +4 -0
- package/locale/en-FR.json.js +219 -0
- package/locale/en-GS/package.json +16 -0
- package/locale/en-GS.json +219 -0
- package/locale/en-GS.json.d.ts +4 -0
- package/locale/en-GS.json.js +219 -0
- package/locale/en-HU/package.json +16 -0
- package/locale/en-HU.json +219 -0
- package/locale/en-HU.json.d.ts +4 -0
- package/locale/en-HU.json.js +219 -0
- package/locale/en-ID/package.json +16 -0
- package/locale/en-ID.json +219 -0
- package/locale/en-ID.json.d.ts +4 -0
- package/locale/en-ID.json.js +219 -0
- package/locale/en-IT/package.json +16 -0
- package/locale/en-IT.json +219 -0
- package/locale/en-IT.json.d.ts +4 -0
- package/locale/en-IT.json.js +219 -0
- package/locale/en-NO/package.json +16 -0
- package/locale/en-NO.json +219 -0
- package/locale/en-NO.json.d.ts +4 -0
- package/locale/en-NO.json.js +219 -0
- package/locale/en-PL/package.json +16 -0
- package/locale/en-PL.json +219 -0
- package/locale/en-PL.json.d.ts +4 -0
- package/locale/en-PL.json.js +219 -0
- package/locale/en-PT/package.json +16 -0
- package/locale/en-PT.json +219 -0
- package/locale/en-PT.json.d.ts +4 -0
- package/locale/en-PT.json.js +219 -0
- package/locale/en-RO/package.json +16 -0
- package/locale/en-RO.json +219 -0
- package/locale/en-RO.json.d.ts +4 -0
- package/locale/en-RO.json.js +219 -0
- package/locale/en-SK/package.json +16 -0
- package/locale/en-SK.json +219 -0
- package/locale/en-SK.json.d.ts +4 -0
- package/locale/en-SK.json.js +219 -0
- package/locale/es-MX.json +12 -9
- package/locale/es-MX.json.js +12 -9
- package/locale/es-SV.json +2 -2
- package/locale/es-SV.json.js +2 -2
- package/locale/eu.json +24 -24
- package/locale/eu.json.js +24 -24
- package/locale/ha.json +119 -65
- package/locale/ha.json.js +119 -65
- package/locale/he.json +0 -6
- package/locale/he.json.js +0 -6
- package/locale/hi-Latn.json +2 -2
- package/locale/hi-Latn.json.js +2 -2
- package/locale/ht/package.json +16 -0
- package/locale/ht.json +207 -0
- package/locale/ht.json.d.ts +4 -0
- package/locale/ht.json.js +207 -0
- package/locale/id.json +1 -1
- package/locale/id.json.js +1 -1
- package/locale/ie/package.json +16 -0
- package/locale/ie.json +159 -0
- package/locale/ie.json.d.ts +4 -0
- package/locale/ie.json.js +159 -0
- package/locale/ig.json +17 -17
- package/locale/ig.json.js +17 -17
- package/locale/ii/package.json +16 -0
- package/locale/ii.json +165 -0
- package/locale/ii.json.d.ts +4 -0
- package/locale/ii.json.js +165 -0
- package/locale/is.json +4 -1
- package/locale/is.json.js +4 -1
- package/locale/kaa/package.json +16 -0
- package/locale/kaa.json +159 -0
- package/locale/kaa.json.d.ts +4 -0
- package/locale/kaa.json.js +159 -0
- package/locale/kok-Latn/package.json +16 -0
- package/locale/kok-Latn.json +159 -0
- package/locale/kok-Latn.json.d.ts +4 -0
- package/locale/kok-Latn.json.js +159 -0
- package/locale/kok.json +8 -8
- package/locale/kok.json.js +8 -8
- package/locale/ks.json +9 -9
- package/locale/ks.json.js +9 -9
- package/locale/ku.json +55 -46
- package/locale/ku.json.js +55 -46
- package/locale/lld/package.json +16 -0
- package/locale/lld.json +291 -0
- package/locale/lld.json.d.ts +4 -0
- package/locale/lld.json.js +291 -0
- package/locale/mi.json +22 -22
- package/locale/mi.json.js +22 -22
- package/locale/ms-Arab/package.json +16 -0
- package/locale/ms-Arab.json +159 -0
- package/locale/ms-Arab.json.d.ts +4 -0
- package/locale/ms-Arab.json.js +159 -0
- package/locale/nb.json +2 -2
- package/locale/nb.json.js +2 -2
- package/locale/ne.json +1 -1
- package/locale/ne.json.js +1 -1
- package/locale/nn.json +4 -4
- package/locale/nn.json.js +4 -4
- package/locale/no.json +2 -2
- package/locale/no.json.js +2 -2
- package/locale/nqo/package.json +16 -0
- package/locale/nqo.json +159 -0
- package/locale/nqo.json.d.ts +4 -0
- package/locale/nqo.json.js +159 -0
- package/locale/om/package.json +16 -0
- package/locale/om.json +213 -0
- package/locale/om.json.d.ts +4 -0
- package/locale/om.json.js +213 -0
- package/locale/or.json +1 -1
- package/locale/or.json.js +1 -1
- package/locale/qu.json +46 -46
- package/locale/qu.json.js +46 -46
- package/locale/rhg/package.json +16 -0
- package/locale/rhg.json +159 -0
- package/locale/rhg.json.d.ts +4 -0
- package/locale/rhg.json.js +159 -0
- package/locale/sl.json +12 -12
- package/locale/sl.json.js +12 -12
- package/locale/sr-Cyrl-BA.json +2 -2
- package/locale/sr-Cyrl-BA.json.js +2 -2
- package/locale/sr-Latn-BA.json +2 -2
- package/locale/sr-Latn-BA.json.js +2 -2
- package/locale/sv.json +1 -1
- package/locale/sv.json.js +1 -1
- package/locale/syr/package.json +16 -0
- package/locale/syr.json +303 -0
- package/locale/syr.json.d.ts +4 -0
- package/locale/syr.json.js +303 -0
- package/locale/ti.json +6 -6
- package/locale/ti.json.js +6 -6
- package/locale/tn/package.json +16 -0
- package/locale/tn.json +159 -0
- package/locale/tn.json.d.ts +4 -0
- package/locale/tn.json.js +159 -0
- package/locale/to.json +26 -26
- package/locale/to.json.js +26 -26
- package/locale/uk.json +1 -1
- package/locale/uk.json.js +1 -1
- package/locale/ur-IN.json +9 -6
- package/locale/ur-IN.json.js +9 -6
- package/locale/vec/package.json +16 -0
- package/locale/vec.json +267 -0
- package/locale/vec.json.d.ts +4 -0
- package/locale/vec.json.js +267 -0
- package/locale/xnr/package.json +16 -0
- package/locale/xnr.json +159 -0
- package/locale/xnr.json.d.ts +4 -0
- package/locale/xnr.json.js +159 -0
- package/locale/yo-BJ.json +17 -17
- package/locale/yo-BJ.json.js +17 -17
- package/locale/yo.json +17 -17
- package/locale/yo.json.js +17 -17
- 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/LocaleDataStore.js +2 -3
- package/modules/LocaleDataStore.js.map +1 -1
- package/modules/PropTypes.js +11 -7
- package/modules/PropTypes.js.map +1 -1
- package/modules/TimeAgo.js +299 -237
- package/modules/TimeAgo.js.map +1 -1
- package/modules/TimeAgo.test.js +217 -119
- package/modules/TimeAgo.test.js.map +1 -1
- package/modules/cache.js +6 -22
- package/modules/cache.js.map +1 -1
- package/modules/cache.test.js.map +1 -1
- package/modules/isStyleObject.js +9 -7
- package/modules/isStyleObject.js.map +1 -1
- package/modules/isStyleObject.test.js.map +1 -1
- package/modules/locale.js +6 -16
- package/modules/locale.js.map +1 -1
- package/modules/locale.test.js +0 -4
- package/modules/locale.test.js.map +1 -1
- package/modules/round.js +2 -3
- package/modules/round.js.map +1 -1
- package/modules/steps/approximate.js +6 -2
- package/modules/steps/approximate.js.map +1 -1
- package/modules/steps/approximate.test.js +0 -2
- package/modules/steps/approximate.test.js.map +1 -1
- package/modules/steps/getStep.js +36 -48
- package/modules/steps/getStep.js.map +1 -1
- package/modules/steps/getStep.test.js +6 -2
- package/modules/steps/getStep.test.js.map +1 -1
- package/modules/steps/getStepDenominator.js +2 -3
- package/modules/steps/getStepDenominator.js.map +1 -1
- package/modules/steps/getStepDenominator.test.js.map +1 -1
- package/modules/steps/getStepMinTime.js +31 -36
- package/modules/steps/getStepMinTime.js.map +1 -1
- package/modules/steps/getStepMinTime.test.js.map +1 -1
- package/modules/steps/getTimeToNextUpdate.js +28 -32
- package/modules/steps/getTimeToNextUpdate.js.map +1 -1
- package/modules/steps/getTimeToNextUpdate.test.js +27 -18
- package/modules/steps/getTimeToNextUpdate.test.js.map +1 -1
- package/modules/steps/getTimeToNextUpdateForUnit.js +4 -10
- package/modules/steps/getTimeToNextUpdateForUnit.js.map +1 -1
- package/modules/steps/getTimeToNextUpdateForUnit.test.js +0 -4
- package/modules/steps/getTimeToNextUpdateForUnit.test.js.map +1 -1
- package/modules/steps/helpers.js +1 -1
- package/modules/steps/helpers.js.map +1 -1
- package/modules/steps/helpers.test.js.map +1 -1
- package/modules/steps/index.js +2 -1
- package/modules/steps/index.js.map +1 -1
- package/modules/steps/renameLegacyProperties.js +6 -13
- package/modules/steps/renameLegacyProperties.js.map +1 -1
- package/modules/steps/renameLegacyProperties.test.js.map +1 -1
- package/modules/steps/round.js.map +1 -1
- package/modules/steps/round.test.js +0 -3
- package/modules/steps/round.test.js.map +1 -1
- package/modules/steps/units.js +5 -9
- package/modules/steps/units.js.map +1 -1
- package/modules/style/approximate.js +5 -1
- package/modules/style/approximate.js.map +1 -1
- package/modules/style/approximateTime.js +6 -2
- package/modules/style/approximateTime.js.map +1 -1
- package/modules/style/approximateTime.test.js +115 -70
- package/modules/style/approximateTime.test.js.map +1 -1
- package/modules/style/getStyleByName.js +4 -17
- package/modules/style/getStyleByName.js.map +1 -1
- package/modules/style/mini.js +8 -4
- package/modules/style/mini.js.map +1 -1
- package/modules/style/mini.test.js +14 -20
- package/modules/style/mini.test.js.map +1 -1
- package/modules/style/miniMinute.js +6 -6
- package/modules/style/miniMinute.js.map +1 -1
- package/modules/style/miniMinute.test.js +10 -12
- package/modules/style/miniMinute.test.js.map +1 -1
- package/modules/style/miniMinuteNow.js +6 -6
- package/modules/style/miniMinuteNow.js.map +1 -1
- package/modules/style/miniMinuteNow.test.js +10 -12
- package/modules/style/miniMinuteNow.test.js.map +1 -1
- package/modules/style/miniNow.js +6 -6
- package/modules/style/miniNow.js.map +1 -1
- package/modules/style/miniNow.test.js +10 -12
- package/modules/style/miniNow.test.js.map +1 -1
- package/modules/style/renameLegacyProperties.js +8 -10
- package/modules/style/renameLegacyProperties.js.map +1 -1
- package/modules/style/renameLegacyProperties.test.js +0 -1
- package/modules/style/renameLegacyProperties.test.js.map +1 -1
- package/modules/style/round.js +3 -2
- package/modules/style/round.js.map +1 -1
- package/modules/style/round.test.js +50 -62
- package/modules/style/round.test.js.map +1 -1
- package/modules/style/roundMinute.js +8 -7
- package/modules/style/roundMinute.js.map +1 -1
- package/modules/style/roundMinute.test.js +19 -22
- package/modules/style/roundMinute.test.js.map +1 -1
- package/modules/style/twitter.js +36 -33
- package/modules/style/twitter.js.map +1 -1
- package/modules/style/twitter.test.js +53 -52
- package/modules/style/twitter.test.js.map +1 -1
- package/modules/style/twitterFirstMinute.js +8 -7
- package/modules/style/twitterFirstMinute.js.map +1 -1
- package/modules/style/twitterFirstMinute.test.js +15 -18
- package/modules/style/twitterFirstMinute.test.js.map +1 -1
- package/modules/style/twitterMinute.js +6 -6
- package/modules/style/twitterMinute.js.map +1 -1
- package/modules/style/twitterMinute.test.js +19 -20
- package/modules/style/twitterMinute.test.js.map +1 -1
- package/modules/style/twitterMinuteNow.js +6 -6
- package/modules/style/twitterMinuteNow.js.map +1 -1
- package/modules/style/twitterMinuteNow.test.js +19 -20
- package/modules/style/twitterMinuteNow.test.js.map +1 -1
- package/modules/style/twitterNow.js +6 -6
- package/modules/style/twitterNow.js.map +1 -1
- package/modules/style/twitterNow.test.js +17 -19
- package/modules/style/twitterNow.test.js.map +1 -1
- package/package.json +275 -13
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,66 @@ timeAgo.format(Date.now() - 24 * 60 * 60 * 1000)
|
|
|
57
63
|
// "1 day ago"
|
|
58
64
|
```
|
|
59
65
|
|
|
60
|
-
|
|
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
|
-
|
|
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
|
-
|
|
76
|
+
<!-- or `TimeAgo.setDefaultLocale("en")`. By default, the default `locale` is `"en"`, although it still has to be added manually. -->
|
|
65
77
|
|
|
66
|
-
|
|
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
|
-
|
|
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
|
|
83
|
+
import en from 'javascript-time-ago/locale/en'
|
|
72
84
|
import de from 'javascript-time-ago/locale/de'
|
|
73
|
-
import
|
|
85
|
+
import fr from 'javascript-time-ago/locale/fr'
|
|
74
86
|
|
|
75
|
-
TimeAgo.
|
|
87
|
+
TimeAgo.addDefaultLocale(en)
|
|
76
88
|
TimeAgo.addLocale(de)
|
|
77
|
-
TimeAgo.
|
|
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
|
-
|
|
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
|
|
103
|
+
import en from 'javascript-time-ago/locale/en'
|
|
84
104
|
import de from 'javascript-time-ago/locale/de'
|
|
85
|
-
import
|
|
105
|
+
import fr from 'javascript-time-ago/locale/fr'
|
|
86
106
|
|
|
87
|
-
TimeAgo.addLocale(
|
|
107
|
+
TimeAgo.addLocale(en)
|
|
88
108
|
TimeAgo.addLocale(de)
|
|
89
|
-
TimeAgo.addLocale(
|
|
109
|
+
TimeAgo.addLocale(fr)
|
|
90
110
|
|
|
91
|
-
TimeAgo.setDefaultLocale('
|
|
111
|
+
TimeAgo.setDefaultLocale('en')
|
|
92
112
|
```
|
|
93
113
|
|
|
94
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
175
|
+
Intervals follow each other without any gaps, so the entire time range is divided into such intervals.
|
|
138
176
|
|
|
139
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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://
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
464
|
+
`labels` property value should be the type of labels to output. There're several types of labels available:
|
|
423
465
|
|
|
424
|
-
|
|
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
|
-
|
|
474
|
+
The default value for the `labels` property is `"long"`.
|
|
427
475
|
|
|
428
|
-
|
|
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
|
-
|
|
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
|
-
|
|
524
|
+
`steps` property should define a set of intervals that cover the time difference from `0` to `±∞`.
|
|
469
525
|
|
|
470
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
535
|
+
// Starting from the time difference `0`,
|
|
536
|
+
// use "second" labels.
|
|
478
537
|
formatAs: 'second'
|
|
479
538
|
},
|
|
480
539
|
{
|
|
481
|
-
//
|
|
482
|
-
|
|
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
|
-
//
|
|
488
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
568
|
+
<details>
|
|
569
|
+
<summary>More on <code>minTime</code></summary>
|
|
509
570
|
|
|
510
|
-
|
|
571
|
+
######
|
|
511
572
|
|
|
512
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
596
|
+
<details>
|
|
597
|
+
<summary>More on <code>formatAs</code></summary>
|
|
544
598
|
|
|
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"
|
|
599
|
+
######
|
|
554
600
|
|
|
555
|
-
|
|
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
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
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
|
|
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
|
-
|
|
672
|
+
## Refreshing
|
|
635
673
|
|
|
636
|
-
|
|
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
|
-
|
|
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(
|
|
662
|
-
// "in 5 minutes"
|
|
679
|
+
const [text, refreshDelay] = timeAgo.format(date, { getTimeToNextUpdate: true })
|
|
663
680
|
```
|
|
664
681
|
|
|
665
|
-
|
|
682
|
+
Example:
|
|
666
683
|
|
|
667
684
|
```js
|
|
668
|
-
|
|
685
|
+
let timerId
|
|
669
686
|
|
|
670
|
-
|
|
671
|
-
|
|
687
|
+
function renderLoop() {
|
|
688
|
+
const [text, refreshDelay] = timeAgo.format(date, { getTimeToNextUpdate: true })
|
|
672
689
|
|
|
673
|
-
|
|
674
|
-
|
|
690
|
+
// Update the label text.
|
|
691
|
+
timeLabel.textContent = text
|
|
675
692
|
|
|
676
|
-
|
|
693
|
+
if (refreshDelay !== undefined) {
|
|
694
|
+
timerId = setTimeout(renderLoop, refreshDelay)
|
|
695
|
+
}
|
|
696
|
+
}
|
|
677
697
|
|
|
678
|
-
|
|
679
|
-
//
|
|
698
|
+
// Render the label.
|
|
699
|
+
// It will automatically be refreshed when needed
|
|
700
|
+
timeLabel = createTimeLabelElement()
|
|
701
|
+
renderLoop()
|
|
680
702
|
|
|
681
|
-
|
|
682
|
-
|
|
703
|
+
// And when the label is no longer rendered, one should manually stop the periodic refresh.
|
|
704
|
+
clearTimeout(timerId)
|
|
683
705
|
```
|
|
684
706
|
|
|
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.
|
|
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(
|
|
691
|
-
//
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
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
|
-
|
|
718
|
+
// Render the label.
|
|
719
|
+
// It will automatically be refreshed when needed.
|
|
720
|
+
timeLabel = createTimeLabelElement()
|
|
721
|
+
timeLabel.textContent = text
|
|
699
722
|
|
|
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?
|
|
723
|
+
// And when the label is no longer rendered, one should manually stop the periodic refresh.
|
|
724
|
+
stopRefreshing()
|
|
718
725
|
```
|
|
719
726
|
|
|
720
|
-
|
|
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
|
|
728
|
-
// Format the date.
|
|
729
|
-
const [
|
|
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
|
-
|
|
734
|
-
// Schedule
|
|
735
|
-
// `timeToNextUpdate`
|
|
736
|
-
|
|
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
|
-
//
|
|
740
|
-
|
|
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
|
-
|
|
743
|
-
function
|
|
744
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
851
|
+
When given a past date, `.format()` returns an `"... ago"` label.
|
|
766
852
|
|
|
767
|
-
|
|
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
|
-
|
|
856
|
+
timeAgo.format(Date.now() + 5 * 60 * 1000)
|
|
857
|
+
// "in 5 minutes"
|
|
772
858
|
```
|
|
773
859
|
|
|
774
|
-
|
|
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
|
-
|
|
862
|
+
By default, it treats it as `-0`, meaning that it will return `"0 seconds ago"` rather than `"in 0 seconds"`.
|
|
777
863
|
|
|
778
|
-
|
|
864
|
+
To instruct `.format()` to treat it as `+0`, pass `future: true` parameter.
|
|
779
865
|
|
|
780
|
-
|
|
866
|
+
```js
|
|
867
|
+
// Without `future: true`
|
|
868
|
+
timeAgo.format(Date.now())
|
|
869
|
+
// "just now"
|
|
781
870
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
871
|
+
// With `future: true`
|
|
872
|
+
timeAgo.format(Date.now(), { future: true })
|
|
873
|
+
// "in a moment"
|
|
874
|
+
```
|
|
785
875
|
|
|
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>
|
|
876
|
+
## `now` parameter
|
|
811
877
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
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
|
-
##
|
|
885
|
+
## Full Date Formatter
|
|
818
886
|
|
|
819
|
-
|
|
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
|
-
|
|
889
|
+
This library exports a "helper" date formatter for just that:
|
|
822
890
|
|
|
823
|
-
|
|
891
|
+
```js
|
|
892
|
+
import FullDateFormatter from 'javascript-time-ago/full-date-formatter'
|
|
824
893
|
|
|
825
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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.
|