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?