Closed danieltroger closed 11 months ago
Previously discussed here: https://github.com/tc39/proposal-intl-duration-format/issues/29#issuecomment-682110757
@FrankYFTang made a good observation:
Intl.DurationFormat is chartered to format the "amount" of time, not a "particular time in the timeline" ... A duration itself is a "amount of time" , without any reference point of start or end.
https://github.com/tc39/proposal-intl-duration-format/issues/29#issuecomment-760504931
To get the behavior you are looking for, please use Intl.RelativeTimeFormat, which has been stable since 2018.
@sffc thanks for the reply but excuse my ignorance, the difference to Intl.RelativeTimeFormat
seems to be that it adds "ago"? That fits my use case better, but why is there no way to break down the provided duration into years/weeks/days/minutes/hours/seconds/whatever automatically?
If I do
new Intl.RelativeTimeFormat("en").format(
Math.round((+new Date("2023-11-02T10:28:07.749Z") - +new Date()) / 1000),
"second"
);
I get 21,712 seconds ago
while I'd expect 6 hours, 1 minute, 52 seconds ago
. Is there an API for that?
RelativeTimeFormat has some limitations
Temporal.Duration
inputs (since Temporal.Duration is not fully landed yet)https://github.com/tc39/ecma402/issues/498 tracks a feature improvement to RelativeTimeFormat to add these features. Please upvote that issue to help it get prioritized. In the meantime, you can use RelativeTimeFormat with a single unit at a time. Thanks!
It seems a good idea to champion a new proposal for Intl.RelativeTimeFormat v2 to enhance it for such a feature. I can also see additional different use case to use a different reference time for Intl.RelativeTimeFormat , such as "2 days after December 7, 1941" or "5 days, 23 hours, 54 minutes, and 41 seconds after April 11, 1970, 19:13:00 UTC"
you can use RelativeTimeFormat with a single unit at a time. Thanks!
I tried this but it doesn't really work, because every time one calls RelativeTimeFormat with a unit one gets "ago" again. So it will output 5 days ago, 10 hours ago, 20 minutes ago, 50 seconds ago
whereas one would say 5 days, 10 hours, 20 minutes, 50 seconds ago
Example code:
const secondsInAMinute = 60;
const secondsInAnHour = secondsInAMinute * 60;
const secondsInADay = secondsInAnHour * 24;
function howLongAgo(date: Date, now: Date): string {
const intlObject = new Intl.RelativeTimeFormat("en");
const output: string[] = [];
let seconds = Math.round((+now - +date) / 1000);
const days = Math.floor(seconds / secondsInADay);
seconds %= secondsInADay; // Remainder after subtracting days
const hours = Math.floor(seconds / secondsInAnHour);
seconds %= secondsInAnHour; // Remainder after subtracting hours
const minutes = Math.floor(seconds / secondsInAMinute);
seconds %= secondsInAMinute; // Remainder after subtracting minutes
if (days > 0) output.push(intlObject.format(-days, "day"));
if (hours > 0) output.push(intlObject.format(-hours, "hour"));
if (minutes > 0) output.push(intlObject.format(-minutes, "minute"));
if (seconds > 0 || output.length === 0) output.push(intlObject.format(-seconds, "second"));
return output.join(", ");
}
When I said "you can use RelativeTimeFormat with a single unit at a time", I meant that currently you should just choose which unit you want. It doesn't make sense to glue multiple outputs together (which, by the way, should be using ListFormat, not the code you wrote above).
😅 ah yes, let's tell my users they last saved 3,453 minutes ago
lol. Really looking forward to in a couple years when this is improved. Thanks for clarifying about ListFormat though, updated my code. Now it says "8 minutes ago and 32 seconds ago" which I guess is more localised so I'll keep it.
I believe the thinking is that relative time format has generally been considered to be an approximation of time, so a single unit is sufficient. You say "X seconds ago" until you get to 60 seconds and then you switch to "X minutes ago" until 60 minutes and then "X hours ago", etc. I believe the thinking was that if you need more precision then you should just format the timestamp directly instead of through RelativeTimeFormat.
Hmm, that's an interesting take. Would certainly be a solution, but I feel like especially between "1 hour and 1 minute ago" and "one hour and 59 minutes ago" there's quite a difference that users probably would like to know about, without having to read a full date and time printout
there's quite a difference that users probably would like to know about, without having to read a full date and time printout
You may be right, but that's not how many popular UIs display relative times today. See the dates on the comments on this very site. Same goes with Twitter(-alikes), popular blogging/CMS platforms, email clients, etc. They just display approximations rounded to the largest unit and people seem okay enough with it. Note that it's also common to display the precise timestamp on hover with these UIs.
Yeah I'm already showing the precise time on hover but it takes some mental effort to parse. But I think you're right, it's probably enough with just one unit. However, then why does Intl not select the right unit for me?
@danieltroger You have a good point. That's a common need and we should make sure it's easy. You should open an issue for that.
@michaelficarra if this issue isn't enough, would this repo be the correct one?
~Yes, this repo would be the correct one,~ and a new issue focused on that more pointed topic would be best, in my opinion.
Oops, I misspoke. The correct repo would be either the relative time format repo or https://github.com/tc39/ecma402.
I think a lot of developers will have the following issue: we're tasked with implementing where you can see how long ago something was.
In my current use case I want to say "The last sync occurred 2 minutes and 35 seconds ago". I discovered this API and got delighted because I always prefer using browser native technologies over bloated libraries but unless I'm missing something this API seems to be only for advanced use cases and not for just writing out the difference between two dates?
So I have one Date object, the current Date, and one Date object of the last sync. I want to get the duration between them.
I tried doing
new Intl.DurationFormat("en", { style: "short" }).format({milliseconds: +new Date - new Date("2023-11-02T10:28:07.749Z")});
but it gives me "764,387 ms" which is not what the user expects. Is there another API to split up a duration into seconds/hours/days that I haven't found yet?I think it's very important for adoption that you also consider the simple use cases and not just make something super advanced