Skip to content

Commit fdf81c8

Browse files
authored
refactor: re-write timeAgo() to use Intl.DurationFormat (#706)
1 parent ba68c58 commit fdf81c8

File tree

2 files changed

+18
-43
lines changed

2 files changed

+18
-43
lines changed

utils/display.ts

Lines changed: 17 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
// Copyright 2023-2025 the Deno authors. All rights reserved. MIT license.
2-
import { difference } from "$std/datetime/difference.ts";
2+
import { difference, type Unit } from "$std/datetime/difference.ts";
33

4-
/**
5-
* Returns a pluralized string for the given amount and unit.
6-
*
7-
* @example
8-
* ```ts
9-
* import { pluralize } from "@/utils/display.ts";
10-
*
11-
* pluralize(0, "meow"); // Returns "0 meows"
12-
* pluralize(1, "meow"); // Returns "1 meow"
13-
* ```
14-
*/
15-
export function pluralize(amount: number, unit: string) {
16-
return amount === 1 ? `${amount} ${unit}` : `${amount} ${unit}s`;
17-
}
4+
const units = [
5+
"years",
6+
"months",
7+
"weeks",
8+
"days",
9+
"hours",
10+
"minutes",
11+
"seconds",
12+
] as Unit[];
1813

1914
/**
2015
* Returns how long ago a given date is from now.
@@ -28,29 +23,15 @@ export function pluralize(amount: number, unit: string) {
2823
* timeAgo(new Date(Date.now() - 3 * HOUR)); // Returns "3 hours ago"
2924
* ```
3025
*/
31-
export function timeAgo(date: Date) {
26+
export function timeAgo(date: Date): string {
3227
const now = new Date();
3328
if (date > now) throw new Error("Timestamp must be in the past");
34-
const match = Object.entries(
35-
difference(now, date, {
36-
// These units make sense for a web UI
37-
units: [
38-
"seconds",
39-
"minutes",
40-
"hours",
41-
"days",
42-
"weeks",
43-
"months",
44-
"years",
45-
],
46-
}),
47-
)
48-
.toReversed()
49-
.find(([_, amount]) => amount > 0);
50-
if (match === undefined) return "just now";
51-
const [unit, amount] = match;
52-
// Remove the last character which is an "s"
53-
return pluralize(amount, unit.slice(0, -1)) + " ago";
29+
const duration = difference(date, now, { units });
30+
if (duration.seconds === 0) return "just now";
31+
const largestUnit = units.find((unit) => duration[unit]! > 0) || "seconds";
32+
// @ts-ignore - TS doesn't know about this API yet
33+
return new Intl.DurationFormat("en", { style: "long" })
34+
.format({ [largestUnit]: duration[largestUnit] }) + " ago";
5435
}
5536

5637
/**

utils/display_test.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
// Copyright 2023-2025 the Deno authors. All rights reserved. MIT license.
2-
import { formatCurrency, pluralize, timeAgo } from "./display.ts";
2+
import { formatCurrency, timeAgo } from "./display.ts";
33
import { DAY, HOUR, MINUTE, SECOND } from "$std/datetime/constants.ts";
44
import { assertEquals, assertThrows } from "$std/assert/mod.ts";
55

6-
Deno.test("[display] pluralize()", () => {
7-
assertEquals(pluralize(0, "item"), "0 items");
8-
assertEquals(pluralize(1, "item"), "1 item");
9-
assertEquals(pluralize(2, "item"), "2 items");
10-
});
11-
126
Deno.test("[display] timeAgo()", () => {
137
assertEquals(timeAgo(new Date(Date.now())), "just now");
148
assertEquals(timeAgo(new Date(Date.now() - SECOND * 30)), "30 seconds ago");

0 commit comments

Comments
 (0)