Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust "days" #139

Closed
Nemo64 opened this issue Aug 11, 2017 · 20 comments

Comments

@Nemo64
Copy link

commented Aug 11, 2017

I noticed something.
If I do timeago().format(new Date("2017-08-09 18:00")) (it is 2017-08-11 12:39 right now) I get 1 day ago. But in human terms, thats wrong. For me it was actually 2 days ago, even if it weren't 48 hours. If you say 1 day ago I assume that it was yesterday, not the day before late in the night.

@Nemo64

This comment has been minimized.

Copy link
Author

commented Aug 11, 2017

The same could be true for months, if I use timeago().format(new Date("2017-06-11 18:00")) I get 1 month ago. Actually it was 2 months ago.

@hustcc

This comment has been minimized.

Copy link
Owner

commented Aug 11, 2017

Close to two days, but less than two days, so it was a day ago.

@Nemo64

This comment has been minimized.

Copy link
Author

commented Aug 11, 2017

I know that it is "technically correct" (which is the best kind of correct) but it feels wrong.
I have a small blog and noticed that even google says in one of my entries "6 days ago" while timeago only say "5 days ago". I posted something on saturday evening so i'd consider 6 days ago correct. Tomorrow it will be a week ago.

The implementation I'd consider "more human like" would be (pseudocode)

if (hourDifference >= 24) {
    return now.getDay() - date.getDay(); // sort of ~ it needs to repect month/year breaks
}
@hustcc

This comment has been minimized.

Copy link
Owner

commented Aug 11, 2017

There are some truths.

But Google's index time is quite special, Google through the crawler to obtain the published time is not necessarily accurate time.

In addition, we can refer to github, gitlab, Facebook or wechat, and how they do with it?

@Nemo64

This comment has been minimized.

Copy link
Author

commented Aug 12, 2017

The google indexer can't find my page before it was published... I hope. so their date difference is either equal (if they use my metadata) or lower (if they use the first index time).

To your question, If I understand correctly you want to check how others implement it?
Here is githubs implementation: https://github.com/github/time-elements/blob/v0.6.1/time-elements.js#L150
It's kind of wanky and not accurate at all, they round to the nearest value. 36 hours = 2 days.

@hustcc

This comment has been minimized.

Copy link
Owner

commented Aug 12, 2017

github used Math.round, include the star count of project.

@Nemo64

This comment has been minimized.

Copy link
Author

commented Aug 15, 2017

So, what now?

Is this rejected? Is this on the todo list? (I'd try to implement it if you agree with the behavior change)

@hustcc

This comment has been minimized.

Copy link
Owner

commented Aug 15, 2017

This is a huge change. We must be careful, or will affect the user which has been used it.

👏 Need to vote for it.

1. Accurate calculation

This is the current mode.

time diff formatted string
61 seconds 1 minute ago
110 seconds 1 minute ago
170 seconds 2 minute ago

2. Human calculation

@Nemo64 's thoughts.

time diff formatted string
61 seconds 1 minute ago
90 seconds 2 minutes ago
181 seconds 3 minutes ago

Means: when time diff >= n * 60 * 1.5, then formatted string will be n+1 minutes ago, or wil be n minutes ago.


👏 Everyone can write your thoughts or just vote for 1 or 2. Thank you.

@likerRr

This comment has been minimized.

Copy link
Collaborator

commented Aug 17, 2017

Can we implement this not by remaking current behavior, but by adding a new option to constructor to have ability to switch between formats?

@likerRr

This comment has been minimized.

Copy link
Collaborator

commented Aug 17, 2017

I think it also can be solved by adding corresponding locale files. Every locale has access to diff, index, and total_sec. I think these data is enough to change returned time intervals. @Nemo64 Am I right?

@Nemo64

This comment has been minimized.

Copy link
Author

commented Aug 21, 2017

@likerRr I honestly don't know, I don't see how that would work. Or do you mean it should be implemented that way?

In this case I'd rather be for a useful default. I mean what am i deciding on when using this library? Fuzzy dates or slightly different fuzzy dates. I'd have to think about what I really want and how should I know? If you put an options there I suspect 99% of installations won't use it simply because they don't know or care about it. I had trouble finding good examples here and someone who is using this library won't stop and think 15 minutes about what kind of fuzzy dates he wants.

I'd definitely prefer one implementation only and that should be the superior one. Which one that is: my opinion should be clear but If it's the current implementation than im fine with that.

@hustcc

This comment has been minimized.

Copy link
Owner

commented Aug 22, 2017

@Nemo64

It can be implemented by customized locales, but it is not the perfect way.

We can only choose one mode from Accurate calculation and Human calculation which is the most suitable for timeago , but no matter which one, all the locales maybe rewrite or review.

And which one to choose is not only by our decision, it will has great influence on all the users, so if there is no strong objection of most users, my opinion is to keep the status quo, and implement it by customized locales temporarily.

@likerRr

This comment has been minimized.

Copy link
Collaborator

commented Aug 24, 2017

@Nemo64 I meant it can be implemented using locale files.

But, @hustcc I tried to implement it with locales and honestly we cant do it at least because in locales we can't change resulting number. So to solve this we should pass agoin variable to locales function too. In this case we would be able to use locales to customize displaying of dates like @Nemo64 wants.

The example of updated lib and expected by @Nemo64 behavior: https://jsbin.com/jebozacewo/1/edit?js,console

It's just an example of how it could be done and it doesn't mean that we should do it this way, because it leads to additional calculations which can be expensive in some cases.

The only thing I changed in original lib is:

// was 
return locales[locale](diff, i, total_sec)[agoin].replace('%s', diff);

// becomes
return locales[locale](diff, i, total_sec, agoin).replace('%s', diff);

This change DOES require to update all the other locales by adding agoin:

// was
const regularEn = (number, index, diff) => {
  return [
    ...
  ][index];
};

// will become
const regularEn = (number, index, diff, agoin) => {
  return [
    ...
  ][index][agoin];
};

This is an updated answer

@hustcc

This comment has been minimized.

Copy link
Owner

commented Aug 25, 2017

@likerRr Function humanyfy is really black technology. ^_^

Move agoin into locale function is reasonable. I do not know why the parameter agoin is treated differently.

After move the agoin, Main version should be update, and the usage of locales will has a bit of difference.

@likerRr

This comment has been minimized.

Copy link
Collaborator

commented Aug 25, 2017

Function humanyfy is really black technology. ^_^

haha, yeah, It just parses diff time to the state it was before passing to locales method.

Also I found that formula:

Means: when time diff >= n * 60 * 1.5, then formatted string will be n+1 minutes ago, or wil be n minutes ago.

is not accurate. We should compare diff time with the nearest period multiple n plus half of nearest period. Nearest period is a result of multiply of [60, 60, 24, 7, 365/7/12, 12]. So basically nearest period is a second(s) or minute(s) or hour(s) and so on, which is lower then diff time.

So for example. Diff time is 151 seconds (2 minutes, 31 seconds). Here we know that n is 2 (minutes). We should decide what to show to user: 2 or 3 minutes. Nearest period will be 60, because 60*60 from this table [60, 60, 24, 7, 365/7/12, 12] > 150. Then we multiply nearest period with n: 60 * 2 = 120. 120 is a bottom representation of current period (minutes). And finally we add half of period in order to get a mark after which we should display n+1: 120 + 60 / 2 = 150. If the diff is higher or equals to that number, then we should display n+1. The rest of magic just makes sure that index is within locales array.

I tried to explain it as how I see it. If you have better options share them with us :)

@axelboc

This comment has been minimized.

Copy link

commented Dec 11, 2017

I feel like the problem is not about rounding the difference up or down but more about how that difference is calculated in the first place. The original issue and first few comments relate specifically to days, because this is the most obvious case.

Let's say that it is now 2pm on Monday 11 December, and that I published an article three days ago, on Friday 8 December at 5pm.

var friday5pm = new Date(2017, 11, 8, 17);
console.log(ago(friday5pm));
// => 2 days ago

In human terms, Friday is 3 days ago, but s-ago shows "2 days ago" because the time difference is lower than 3 * 24h.

Could the way around this be to recompute the time difference but with the end of today as the reference point (or the start of tomorrow)? So instead of doing Monday 11 December @ 2pm <minus> Friday 8 December @ 5pm, do Tuesday 12 December @ 00:00am <minus> Friday 8 December @ 5pm. You then end up with a time difference greater than 3 * 24h, so "3 days ago".

The algorithm required for this to work for all periods is not too difficult to imagine: going through each unit of time starting with seconds, round now to the start of the next unit period (e.g. for minutes: round 14h 30m 05s to 14h 31m 00s) and compute the difference, making sure to round it down; if the difference is lower than the next unit (e.g. for minutes, lower than 60 min), output the "time ago" string; otherwise continue to the next unit.

I'm probably missing some subtleties here, and looping through the time units is probably not required, but I hope the idea makes sense: the reference point changes for each unit.

@acusti

This comment has been minimized.

Copy link

commented Sep 19, 2018

I used the timeago.register() custom locale API to implement more UX-friendly output for various awkward times ago where the default behavior is lacking. This includes:

  • Counting days ago based on the day the datetime occurred on relative to the current day; e.g.. if the datetime is from Wednesday, and it’s currently Friday, it will show “2 days ago”, regardless of when during the day it was and when during the day on Friday it is. I believe this addresses @axelboc’s suggestion and the desired behavior of the original poster (@Nemo64)
  • Showing “_ days ago” for datetimes that are substantially more than 1 week ago and less than 2 weeks ago, i.e. 9-12 days ago
  • Rounding “13 days ago” up to “2 weeks ago”

I built a codepen project that includes a configureTimeago function that implements the custom locale against v3.0.2. I named the local 'ux' for demonstration purposes, but if you name it 'en' and don’t import any other locales, I believe it will become the defacto default locale for your project. The app.js file builds out fixtures to demonstrate the customized functionality and its variations from the default behavior (using timeago-react for the fixtures): https://codepen.io/acusti/project/editor/AVazpg

Caveats: my implementation is only for the english locale, and would need be customized for any additional language locale. Also, all of the adjustments should be user tested across multiple variations to actually determine what proves to be overall the most user-friendly behavior.

@acusti

This comment has been minimized.

Copy link

commented Sep 24, 2018

In case the codepen project is too confusing, here’s just the custom locale:

const SECONDS = 60;
const MINUTES = 60;
const HOURS = 24;
const SECONDS_IN_DAY = HOURS * MINUTES * SECONDS;

timeago.register('en', (number, index, total_sec) => {
    // Between 105s - 120s, round up to 2 minutes
    // Won’t work for already mounted components because won’t update between 60-120s
    if (index === 2 && total_sec >= 105) {
        return ['2 minutes ago', 'in 2 minutes'];
    }

    // 1-6 days ago should be based on actual days of the week (from 0:00 - 23:59)
    if (index === 6 || index === 7) {
        // Calculate seconds since midnight for right now
        const now = new Date();
        const secondsSinceMidnight =
            now.getSeconds() + (now.getMinutes() * SECONDS) + (now.getHours() * MINUTES * SECONDS);

        // Subtract seconds since midnight from total_sec, divide by seconds in a day, round down
        // Result is off-by-one number of days since datetime (unless time was at midnight)
        const daysFloored = Math.floor((total_sec - secondsSinceMidnight) / SECONDS_IN_DAY);
        // If time was at midnight (00:00), it will divide evenly with SECONDS_IN_DAY
        // That will make it count as from the previous day, which we do not want
        const remainder = (total_sec - secondsSinceMidnight) % SECONDS_IN_DAY;
        const days = remainder >= 1 ? daysFloored + 1 : daysFloored;
        const noun = days === 1 ? 'day' : 'days';
        return [`${days} ${noun} ago`, `in ${days} ${noun}`];
    }

    // For 9-12 days ago, Convert “1 week ago” to “__ days ago”
    // For 13 days, round it to “2 weeks ago”
    if (index === 8) {
        const days = Math.round(total_sec / SECONDS / MINUTES / HOURS);
        if (days > 8) {
            return days === 13
                ? ['2 weeks ago', 'in 2 weeks']
                : [`${days} days ago`, `in ${days} days`];
        }
    }

    return [
        ['just now', 'right now'],
        ['%s seconds ago', 'in %s seconds'],
        ['1 minute ago', 'in 1 minute'],
        ['%s minutes ago', 'in %s minutes'],
        ['1 hour ago', 'in 1 hour'],
        ['%s hours ago', 'in %s hours'],
        ['1 day ago', 'in 1 day'],
        ['%s days ago', 'in %s days'],
        ['1 week ago', 'in 1 week'],
        ['%s weeks ago', 'in %s weeks'],
        ['1 month ago', 'in 1 month'],
        ['%s months ago', 'in %s months'],
        ['1 year ago', 'in 1 year'],
        ['%s years ago', 'in %s years'],
    ][index];
});
@mauron85

This comment has been minimized.

Copy link

commented Jul 20, 2019

I'll be bit harsh here. But IMO, only so called "Human calculation" make sense in context of timeago libraries. Only we human need such a shortcuts when reasoning about time. Computer do not care.

But it's loosing it's purpose when trying to be exactly correct aka Accurate calculation. Because it forces me to think again, that if day ago is actually 1 or 2 days ago.

@hustcc

This comment has been minimized.

Copy link
Owner

commented Oct 12, 2019

Can be solved by customize locale.

@hustcc hustcc closed this Oct 12, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
6 participants
You can’t perform that action at this time.