Code Monkey home page Code Monkey logo

relative-time's Introduction

Relative Time

Formats JavaScript dates to relative time strings (e.g., "3 hours ago").

Based on the Unicode CLDR locale data. Powered by globalizejs/globalize.

Why

Leverages Unicode CLDR

Leverages Unicode CLDR (via Globalize), the largest and most extensive standard repository of locale data available.

It also means messages like "today", "yesterday", "last month" are available and properly localized in the various CLDR supported locales.

IANA time zone support

hr.  | | | | | | | | | | | | | | | | | | | | | | | | | |
day  | x .  .              N |   .  .                |
PDT  .   .  Mar 21 PDT       .   .  Mar 23, 00:00 PDT
EDT  .   Mar 21 EDT          .   Mar 22, 00:00 EDT
UTC  Mar 21                  Mar 22, 00:00

The relative time between x and now N is:

time zone relative-time result
America/New_York "yesterday"
America/Los_Angeles "21 hours ago"

What you get is correct

day

       Mar 21, 00:00           Mar 22, 00:00           Mar 23, 00:00           Mar 24, 00:00
hr.  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
day    |                    b  |          a            |        N              |
       Mar 21                  Mar 22                  Mar 23                  Mar 24

Let's assume now (N) is Mar 23, 9 AM.

relative-time moment.js
Mar 22, 11 AM (a) "yesterday" "a day ago"
Mar 21, 8 PM (b) "2 days ago" "a day ago"

Note relative-time checks for the actual day change instead of counting on approximate number of hours to turn the unit.

month

    Feb 1                        Mar 1                           Apr 1
day  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
mo.  |       d                  c|b   a                         N|

Let's assume now (N) is Mar 31.

relative-time moment.js
Mar 5 (a) "26 days ago" "a month ago"
Mar 1 (b) "30 days ago" "a month ago"
Feb 28 (c) "last month" "a month ago"
Feb 9 (d) "last month" "2 months ago"

Note relative-time checks for the actual month change instead of counting on approximate number of days to turn the unit.

Usage

npm install --save relative-time globalize cldr-data
var cldrData = require("cldr-data");
var Globalize = require("globalize");
var RelativeTime = require("relative-time").default;

// Feed Globalize on CLDR.
Globalize.load(cldrData.entireSupplemental(), cldrData.entireMainFor("en"));
Globalize.locale("en");

var relativeTime = new RelativeTime();
console.log(relativeTime.format(new Date()));
// > now

IANA time zone support

In addition to the above, install iana-tz-data.

npm install --save iana-tz-data

The example below assume now is 2016-04-10T12:00:00Z, i.e.,

UTC America/Los_Angeles Europe/Berlin
date 2016-04-10T00:00:00Z 2016-04-09 17:00:00 GMT-7 (PDT) 2016-04-10 14:00:00 GMT+2 (Central European Summer Time)
now 2016-04-10T12:00:00Z 2016-04-10 05:00:00 GMT-7 (PDT) 2016-04-10 14:00:00 GMT+2 (Central European Summer Time)
var ianaTzData = require("iana-tz-data");
var date = new Date("2016-04-10T00:00:00Z");

// Target: 2016-04-09 17:00:00 GMT-7 (PDT)
// Now: 2016-04-10 05:00:00 GMT-7 (PDT)
relativeTime.format(date, {
  timeZoneData: ianaTzData.zoneData.America.Los_Angeles
});
// > "yesterday"

// Target: 2016-04-10 14:00:00 GMT+2 (Central European Summer Time)
// Now: 2016-04-10 14:00:00 GMT+2 (Central European Summer Time)
relativeTime.format(date, {
  timeZoneData: ianaTzData.zoneData.Europe.Berlin
});
// > "12 hours ago"

API

format(date{, options})

date

A JavaScript date object, i.e., new Date().

options.unit (optional)

Unit for formatting. If the unit is not provided, "best-fit" is used.

  • "best-fit" (default)
  • "second"
  • "minute"
  • "hour"
  • "day"
  • "month"
  • "year"

The "best-fit" unit

It automatically picks a unit based on the relative time scale. Basically, it looks like this:

  • If absDiffYears > 0 && absDiffMonths > threshold.month, return "year".
  • If absDiffMonths > 0 && absDiffWeeks > threshold.week, return "month".
  • If absDiffWeeks > 0 && absDiffDays > threshold.day, return "week".
  • If absDiffDays > 0 && absDiffHours > threshold.hour, return "day".
  • If absDiffHours > 0 && absDiffMinutes > threshold.minute, return "hour".
  • If absDiffMinutes > 0 && absDiffSeconds > threshold.second, return "minutes".
  • Return "second".

options.timeZoneData (optional)

The zdumped IANA timezone data (found on the iana-tz-data package) for the desired timeZoneId.

If not provided, the user's environment time zone is used (default).

Return

Returns the formatted relative time string given date and options.

Appendix

Relative time

In this library, we'll define relative time as what makes sense for expressions like "now", "2 days ago", "in 3 months", "last year", "yesterday", and so on. In a more formal definition, relative time is an approximate date distance given a unit. This is, relative time is the date distance of a and b ± error, where error < unit. Please, see the below examples of each unit for clarity.

second

                8:31:38.000         8:31:39.000         8:31:40.000         8:31:41.000
ms  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
sec             |   e             d |   c             b |       a     N     |
                8:31:38             8:31:39             8:31:40             8:31:41

N: The assumed now
a: now / 0 seconds ago
b: 1 second ago
c: 1 second ago
d: 2 seconds ago
e: 2 seconds ago

minute

          8:28:00           8:29:00           8:30:00           8:31:00           8:32:00
sec |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
min       |     g        f  |     e        d  |     c        b  |        a  N     |
          8:28              8:29              8:30              8:31              8:32

N: The assumed now
a: 0 minutes ago
b: 1 minute ago
c: 1 minute ago
d: 2 minutes ago
e: 2 minutes ago
f: 3 minutes ago
g: 3 minutes ago

hour

          5:00              6:00              7:00              8:00              9:00
min |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
hr.       |     g        f  |     e        d  |     c        b  |     a  N        |
          5                 6                 7                 8                 9

N: The assumed now
a: 0 hours ago
b: 1 hour ago
c: 1 hour ago
d: 2 hours ago
e: 2 hours ago
f: 3 hours ago
g: 3 hours ago

day

       Mar 21, 00:00           Mar 22, 00:00           Mar 23, 00:00           Mar 24, 00:00
hr.  | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
day    | e                   d | c                   b |     a N               |
       Mar 21                  Mar 22                  Mar 23                  Mar 24

N: The assumed now
a: today / 0 days ago
b: yesterday / 1 day ago
c: yesterday / 1 day ago
d: 2 days ago
e: 2 days ago

week

                     Wk. 11, Sun, Mar 12  Wk. 12, Sun, Mar 19  Wk. 13, Sun, Mar 26
day   |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
wk.   e           d  |     c             b|       a    N       |
                     Wk 11                Wk 12                Wk 13

N: The assumed now
a: this week
b: last week
c: last week
d: 2 weeks ago
e: 2 weeks ago

month

      Wk. 1       Wk. 5       Wk. 9          Wk. 14
wk.   |  |  |  |  |  |  |  |  |  |  |  |  |  |
mo.   |e         d | c        b|     a   N   |
      Jan          Feb         Mar           Apr

N: The assumed now
a: this month / 0 months ago
b: last month / 1 month ago
c: last month / 1 month ago
d: 2 months ago
e: 2 months ago

Note the months distances doesn't match weeks distance or days distance uniformly.

year

          Jan               Jan               Jan               Jan               Jan
mo. |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
yr.       |g             f  |e             d  |c             b  |a  N             |
          2013              2014              2015              2016              2017

N: The assumed now
a: this year / 0 years ago
b: last year / 1 year ago
c: last year / 1 year ago
d: 2 years ago
e: 2 years ago
f: 3 years ago
g: 3 years ago

Note that (although not shown by the above ruler), the years distances doesn't match weeks distance or days distance uniformly.

License

MIT © Rafael Xavier de Souza

relative-time's People

Contributors

rxaviers avatar jzaefferer avatar ithildir avatar zbraniecki avatar dependabot[bot] avatar

Stargazers

Andrea Giulianelli avatar Alexandre Bonaventure Geissmann avatar Pawan Dogra avatar Volkan Ceylan avatar hyrious avatar Satriyo Aji avatar Muhammad Wildan Aldiansyah avatar Daniel Thomas avatar Dennis Reichenberg avatar Eric Cheung avatar Dave Schumaker avatar 范晓 avatar Edgar Ortega avatar Cyrus Venn Casada avatar Paul McKellar avatar Sean Cooper avatar Sam Carlile avatar Hugo Maestá avatar Mohammad Bagher Ehtemam avatar Tony Dinh avatar Arash Javanmardi avatar Meena Brend avatar Qian Bin avatar Dee Cheung avatar Julian Winkel avatar Trujun Zhang avatar Gregg Lind avatar Thomas Bindzus avatar Sebastián Petrík avatar Edwin Aw avatar  avatar Dave Kozma avatar Scott Kool avatar Jah Raphael avatar Sid Hoda avatar Glenn Jorde avatar Josh Gwosdz avatar Tobi Schäfer avatar Remi Nyborg avatar Nicola Del Gobbo avatar Josh Miller avatar Kasper Lewau avatar Stephen Lacy avatar Matheus Lima avatar Yerko Palma avatar Jason Miller avatar Rafael R avatar

Watchers

 avatar  avatar  avatar James Cloos avatar  avatar

relative-time's Issues

Support instance options.threshold to override static RelativeTime.threshold

Currently, it's possible to change the threshold configuration at library-wide level [1]. It should also be possible to change that at an instance level by allowing an options.threshold [2].

1:

const relativeTime = new RelativeTime();
relativeTime.format(aTwoYearsAgoDate));
// > '2 years ago'

RelativeTime.threshold.month = Infinity;
relativeTime.format(aTwoYearsAgoDate);
// > '24 months ago'

2:

const relativeTime1 = new RelativeTime();
relativeTime1.format(aTwoYearsAgoDate));
// > '2 years ago'

const relativeTime2 = new RelativeTime({threshold: {month: Infinity}});
relativeTime2.format(aTwoYearsAgoDate);
// > '24 months ago'

Comparison with Luxon (from Moment.js)

This is more of a curiosity than an issue; how does this project differ from Luxon? In terms of features, functionality and scope of what each of them provide.

More specifically referring to Luxon's docs under Output > Humanization, they use Intl.RelativeTimeFormat which also happens to be mentioned in the currently open PR #5. Would it be possible to append Luxon's output results to the comparison tables in README.md?

Past date gets formatted as being in the future

I've seen this bug pop up in an app recently:

console.log(x)
> Tue Dec 13 2016 18:30:08 GMT+0100 (CET)
console.log(relativeTime.format(x))
> in 11 months

I haven't yet had the chance to dig into this repo to look for the cause. Wanted to at least file this bug report though.

Allow to configure a baseline unit

Currently, it's possible to use this library and ignore large units by using the threshold configuration [1]. It should also be possible to ignore small units potentially by using a baseline (any better word?) configuration [2].

1:

const relativeTime = new RelativeTime();
relativeTime.format(aTwoYearsAgoDate));
// > '2 years ago'

RelativeTime.threshold.month = Infinity;
relativeTime.format(aTwoYearsAgoDate);
// > '24 months ago'

2:

relativeTime.format(new Date()));
// > "now"

RelativeTime.baseline.minute = 0
RelativeTime.threshold.second = -1
relativeTime.format(new Date()));
// > "this minute"

That could be accomplish with the following change:

diff --git a/src/relative-time.js b/src/relative-time.js
index b49e5eb..1fe5a1c 100644
--- a/src/relative-time.js
+++ b/src/relative-time.js
@@ -117,14 +117,15 @@ export default class RelativeTime {
 
 RelativeTime.bestFit = function(absDiff) {
   let threshold = this.threshold;
+  let baseline = this.baseline;
   switch(true) {
-    case absDiff.years > 0 && absDiff.months > threshold.month: return "year";
-    case absDiff.months > 0 && absDiff.days > threshold.day: return "month";
-    // case absDiff.months > 0 && absDiff.weeks > threshold.week: return "month";
-    // case absDiff.weeks > 0 && absDiff.days > threshold.day: return "week";
-    case absDiff.days > 0 && absDiff.hours > threshold.hour: return "day";
-    case absDiff.hours > 0 && absDiff.minutes > threshold.minute: return "hour";
-    case absDiff.minutes > 0 && absDiff.seconds > threshold.second: return "minute";
+    case absDiff.years >= baseline.year && absDiff.months > threshold.month: return "year";
+    case absDiff.months >= baseline.month && absDiff.days > threshold.day: return "month";
+    // case absDiff.months >= 1 && absDiff.weeks > threshold.week: return "month";
+    // case absDiff.weeks >= 1 && absDiff.days > threshold.day: return "week";
+    case absDiff.days >= baseline.day && absDiff.hours > threshold.hour: return "day";
+    case absDiff.hours >= baseline.hour && absDiff.minutes > threshold.minute: return "hour";
+    case absDiff.minutes >= baseline.minute && absDiff.seconds > threshold.second: return "minute";
     default: return "second";
   }
 };
@@ -138,6 +139,15 @@ RelativeTime.threshold = {
   second: 59 // at least 59 seconds before using minute.
 };
 
+RelativeTime.baseline = {
+  year: 1,
+  month: 1, // at least 2 months before using year. TODO
+  day: 1, // at least 6 days before using month. TODO
+  hour: 1, // at least 6 hours before using day. TODO
+  minute: 1 // at least 59 minutes before using hour. TODO
+};
+
 // TODO: Remove redundancy. The only reason this code is that ugly is to get
 // supported by globalize-compiler (which reads the static formatters).
 RelativeTime.initializeFormatters = function(globalize) {

Side-note: It's a good approach to enable "week", because it's possible to keep the existing behavior by configuring threshold and baseline.

Is the opposite possible as well?

I am currently looking for a library that allows me to convert relative strings like 24h or 1d 12h to datetimes relative to now(). Preferably stuff like monday would parse to the next monday from now().

Is this possible with this library? If not, do you know any library that supports this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.