tc39/proposal-intl-relative-time

Current bestFit returns wrong textual output

rxaviers opened this issue · 2 comments

The "bestFit" unit is currently specified as following:

  • If units.[[Second]] is less than 60, return "Second".
  • If units.[[Minute]] is less than 60, return "Minute".
  • If units.[[Hour]] is less than 24, return "Hour".
  • If units.[[Day]] is less than 7, return "Day".
  • If units.[[Week]] is less than 4, return "Week".
  • If units.[[Month]] is less than 12, return "Month".
  • Return "Year".

For numeric type output, this works nicely, i.e., the following table shows the output for past tense, but we obviously have the equivalent for the future tense.

Distance from now Numeric output
0 seconds ago 0 seconds ago
<2 second 1 second ago
<1 minute 59 seconds ago
<2 minutes 1 minute ago
<1 hour 59 minutes ago
<2 hours 1 hour ago
<1 day 23 hours ago
<2 days 1 day ago
<1 week 6 days ago
<2 weeks 1 week ago
<1 month 3 weeks ago
<2 months 1 month ago
<1 year 11 months ago
else 1 year ago

For the textual output, we have the following.

Distance from now Verbose output
0 seconds ago now
<2 second 1 second ago
<1 minute 59 seconds ago
<2 minutes 1 minute ago
<1 hour 59 minutes ago
<2 hours 1 hour ago
<1 day 23 hours ago
<2 days yesterday
<1 week 6 days ago
<2 weeks last week
<1 month 3 weeks ago
<2 months last month
<1 year 11 months ago
else last year

Problem

The verbose outputs can be misleading depending on the context. For example:

Subject date Now Verbose output Confusion
The day before yesterday 10 pm (i.e., 36 hours ago) Today 10 am "yesterday" It should be "2 days ago"!
The Thu before the past Thu (i.e., 11 days ago) Mon "last week" It should be "2 weeks ago"! (additional details)
The day 20 of the month before the past month. (i.e., ~40 days ago) Day 10 "last month" It should be "2 months ago"
Jul of the year before the past year. (i.e., 18 months ago) Jan "last year" It should be "2 years ago"

Diagnose

For the numeric output, the continuous distance basted on seconds (or milliseconds) works fine. Although for the textual output, the continuous distance can lead to incorrect results, instead it should be used a discrete count based on the subject unit. For example, see the 4 tables below that corresponds to the 4 items above respectively.

Subject date Now Distance (continuous) Distance (discrete, based on unit) Current bestFit Output Improved bestFit output
Today 00:00 am Today 10 am 10 hours ago (-0.416 days) 0 days "10 hours ago" "10 hours ago"
Yesterday 11:59 pm Today 10 am 10 hours + 1 minute ago (-0.416 days) -1 day "10 hours ago" "10 hours ago" / "yesterday"⁉️
Yesterday 00:00 am Today 10 am 34 hours ago (-1.416 days) -1 day "yesterday" "yesterday"
The day before yesterday 11:59pm Today 10 am 34 hours ago (-1.416 days) -2 day "yesterday" Wrong‼️ "2 days ago"

The last row of the table above shows a case where "yesterday" is wrong. Note it happens when the continuous distance !== discrete distance in days. The second row shows a case where both "10 hours ago" or "yesterday" could be used without prejudice. Note when the continuous distance === discrete distance in days, there's no doubt.

The similar occurs to the following units.

Subject date Now Distance (continuous) Distance (discrete, based on unit) Current bestFit Output Improved bestFit output
Sun (note first day of week a localized info) Mon 1 day ago (-0.14 weeks) 0 weeks "yesterday" "yesterday"
Past Sat Mon 2 days ago (-0.29 weeks) -1 week "2 days ago" "2 days ago" / "last week"⁉️
Past Sun Mon 8 days ago (-1.14 weeks) -1 week "last week" "last week"
The Sat before past Sat Mon 9 days ago (-1.25 weeks) -2 weeks "last week" Wrong‼️ "2 weeks ago"
Subject date Now Distance (continuous) Distance (discrete, based on unit) Current bestFit Output Improved bestFit output
The first day of this month Day 10 10 days ago (-0.33 months) 0 months "10 days ago" "10 days ago"
The last day of the previous month Day 10 11 days ago (-0.34 months) -1 month "11 days ago" "11 days ago" / "last month"⁉️
Day 10 of the past month Day 10 ~30 days ago (-1 month) -1 month "last month" "last month"
The day 20 of the month before the past month Day 10 40 days ago (-1.33 months) -2 months "last month" Wrong‼️ "2 months ago"
Subject date Now Distance (continuous) Distance (discrete, based on unit) Current bestFit Output Improved bestFit output
Jan Feb 1 month ago (-0.08 years) 0 years "last month" "last month"
Past Dec Feb 2 months ago (-0.17 years) -1 year "2 months ago" "2 months ago" / "last year"⁉️
Past Jan Feb 12 months ago (-1 year) -1 year "last year" "last year"
Aug of the year before the past year Feb 18 months ago (-1.5 years) -2 years "last year" Wrong‼️ "2 years ago"

For the numeric output, the continuous distance basted on seconds (or milliseconds) works fine. Although for the textual output, the continuous distance can lead to incorrect results, instead it should be used a discrete count based on the subject unit. For example, see the 4 tables below that corresponds to the 4 items above respectively.

Subject date Now Distance (continuous) Distance (discrete, based on unit) Current bestFit Output Improved bestFit output
Today 00:00 am Today 10 am 10 hours ago (-0.416 days) 0 days "10 hours ago" "10 hours ago"
Yesterday 11:59 pm Today 10 am 10 hours + 1 minute ago (-0.416 days) -1 day "10 hours ago" "10 hours ago" / "yesterday"⁉️
Yesterday 00:00 am Today 10 am 34 hours ago (-1.416 days) -1 day "yesterday" "yesterday"
The day before yesterday 11:59pm Today 10 am 34 hours ago (-1.416 days) -2 day "yesterday" Wrong‼️ "2 days ago"

The last row of the table above shows a case where "yesterday" is wrong. Note it happens when the continuous distance !== discrete distance in days. The second row shows a case where both "10 hours ago" or "yesterday" could be used without prejudice. Note when the continuous distance === discrete distance in days, there's no doubt.

The similar occurs to the following units.

Subject date Now Distance (continuous) Distance (discrete, based on unit) Current bestFit Output Improved bestFit output
Sun (note first day of week a localized info) Mon 1 day ago (-0.14 weeks) 0 weeks "yesterday" "yesterday"
Past Sat Mon 2 days ago (-0.29 weeks) -1 week "2 days ago" "2 days ago" / "last week"⁉️
Past Sun Mon 8 days ago (-1.14 weeks) -1 week "last week" "last week"
The Sat before past Sat Mon 9 days ago (-1.25 weeks) -2 weeks "last week" Wrong‼️ "2 weeks ago"
Subject date Now Distance (continuous) Distance (discrete, based on unit) Current bestFit Output Improved bestFit output
The first day of this month Day 10 10 days ago (-0.33 months) 0 months "10 days ago" "10 days ago"
The last day of the previous month Day 10 11 days ago (-0.34 months) -1 month "11 days ago" "11 days ago" / "last month"⁉️
Day 10 of the past month Day 10 ~30 days ago (-1 month) -1 month "last month" "last month"
The day 20 of the month before the past month Day 10 40 days ago (-1.33 months) -2 months "last month" Wrong‼️ "2 months ago"
Subject date Now Distance (continuous) Distance (discrete, based on unit) Current bestFit Output Improved bestFit output
Jan Feb 1 month ago (-0.08 years) 0 years "last month" "last month"
Past Dec Feb 2 months ago (-0.17 years) -1 year "2 months ago" "2 months ago" / "last year"⁉️
Past Jan Feb 12 months ago (-1 year) -1 year "last year" "last year"
Aug of the year before the past year Feb 18 months ago (-1.5 years) -2 years "last year" Wrong‼️ "2 years ago"

Conclusion

For textual output, we need an additional handling for the bestFit that takes into account what I'm calling the discrece distance in the respective unit.

For the case where the output is wrong, the fix is a clear requirement. For the cases where both are ok, it is subject to additional analysis. Ideally the API should handle this nicely. I mean, if now is "10 AM", the output "11 hours ago" should be ok instead of "yesterday". Although, I would definitely prefer seeing "yesterday" than "19 hours ago". There should be a threshold based on the discrete distance for the switch.

FWIW, https://github.com/rxaviers/relative-time/ handles discrete distance.

This also depends on #9.

@rxaviers this is a wonderful analysis, thanks a lot for it. We have been struggling with this, @ericf and I discussed this extensibly in the past when looking at momentjs without finding a good solution, but this one seems to be good enough. The next step is to codify this into the current spec, and spend more time on the analysis when both values are the same.

Additionally, do you have any previous art reference with respect to discrete distance other than your library?