Skip to content

Commit 85bad14

Browse files
Support capitalization, remove localizeRelativeDays, default to "auto" numeric, and fix EventPage isPastEvent
1 parent d792844 commit 85bad14

5 files changed

Lines changed: 71 additions & 36 deletions

File tree

app/web/features/communities/events/EventPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export default function EventPage({
239239
? Temporal.Instant.compare(
240240
timestampToInstant(event.endTime),
241241
Temporal.Now.instant(),
242-
) >= 0
242+
) < 0
243243
: false;
244244

245245
const isCreator = currentUserId === event?.creatorUserId;

app/web/features/dashboard/DashboardPublicTripCard.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { DASHBOARD, PUBLIC_TRIPS } from "i18n/namespaces";
1414
import Link from "next/link";
1515
import { myPublicTripsRoute, routeToCommunity } from "routes";
1616
import { Temporal } from "temporal-polyfill";
17-
import { localizeDateTimeRange, localizeRelativeDays } from "utils/date";
17+
import { localizeDateTimeRange, localizeRelativeTimeUnit } from "utils/date";
1818
import dayjs from "utils/dayjs";
1919

2020
export const CARD_WIDTH = 220;
@@ -120,7 +120,7 @@ function WhenChip({
120120
if (isOngoing) {
121121
label = t("dashboard:public_trips.when_now");
122122
} else if (daysUntil === 0 || daysUntil === 1) {
123-
label = localizeRelativeDays(daysUntil, locale);
123+
label = localizeRelativeTimeUnit(daysUntil, "days", locale);
124124
}
125125

126126
if (!label) return null;

app/web/features/dashboard/UpcomingStayCard.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { routeToHostRequest } from "routes";
1313
import { Temporal } from "temporal-polyfill";
1414
import {
1515
localizeDateTimeRange,
16-
localizeRelativeDays,
16+
localizeRelativeTimeUnit,
1717
UTC_TIMEZONE,
1818
} from "utils/date";
1919
import dayjs from "utils/dayjs";
@@ -152,10 +152,12 @@ export default function UpcomingStayCard({
152152
const daysUntilEnd = toDate.diff(today, "day");
153153
const isOngoing = daysUntil <= 0 && daysUntilEnd >= 0;
154154
const isImminent = daysUntil <= 3;
155-
const relativeDaysLabel = (() => {
156-
const label = localizeRelativeDays(daysUntil, locale);
157-
return label.charAt(0).toUpperCase() + label.slice(1);
158-
})();
155+
const relativeDaysLabel = localizeRelativeTimeUnit(
156+
daysUntil,
157+
"days",
158+
locale,
159+
{ standalone: true },
160+
);
159161

160162
const dateRange = localizeDateTimeRange(
161163
Temporal.PlainDateTime.from(hostRequest.fromDate),

app/web/utils/date.test.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ describe("localizeDurationLargestUnit", () => {
173173
localizeDurationLargestUnit(
174174
Temporal.Duration.from({ milliseconds: 1 }),
175175
"en",
176+
{ numeric: "always" },
176177
),
177178
).toBe("in 0 seconds");
178179
expect(
@@ -292,7 +293,10 @@ describe("localizeDurationLargestUnit", () => {
292293
localizeDurationLargestUnit(
293294
Temporal.Duration.from({ seconds: 1 }),
294295
"en",
295-
{ smallestUnit: "minutes" },
296+
{
297+
numeric: "always",
298+
smallestUnit: "minutes",
299+
},
296300
),
297301
).toBe("in 0 minutes");
298302
expect(
@@ -322,6 +326,16 @@ describe("localizeDurationLargestUnit", () => {
322326
),
323327
).toBe("1 minute ago");
324328
});
329+
330+
it("supports capitalizing", () => {
331+
expect(
332+
localizeDurationLargestUnit(
333+
Temporal.Duration.from({ seconds: 1 }),
334+
"en",
335+
{ standalone: true },
336+
),
337+
).toBe("In 1 second");
338+
});
325339
});
326340

327341
describe("localizeRelativeInstant", () => {
@@ -360,14 +374,20 @@ describe("localizeRelativeInstant", () => {
360374
localizeRelativeInstant(
361375
new Temporal.Instant(nanosecondsPerDay * 7n),
362376
"en",
363-
{ relativeTo: instantZero },
377+
{
378+
numeric: "always",
379+
relativeTo: instantZero,
380+
},
364381
),
365382
).toBe("in 1 week");
366383
expect(
367384
localizeRelativeInstant(
368385
new Temporal.Instant(nanosecondsPerDay * 13n),
369386
"en",
370-
{ relativeTo: instantZero },
387+
{
388+
numeric: "always",
389+
relativeTo: instantZero,
390+
},
371391
),
372392
).toBe("in 1 week");
373393
expect(
@@ -388,7 +408,10 @@ describe("localizeRelativeInstant", () => {
388408
localizeRelativeInstant(
389409
new Temporal.Instant(nanosecondsPerDay * 30n),
390410
"en",
391-
{ relativeTo: instantZero },
411+
{
412+
numeric: "always",
413+
relativeTo: instantZero,
414+
},
392415
),
393416
).toBe("in 1 month");
394417
expect(
@@ -402,7 +425,10 @@ describe("localizeRelativeInstant", () => {
402425
localizeRelativeInstant(
403426
new Temporal.Instant(nanosecondsPerDay * 365n),
404427
"en",
405-
{ relativeTo: instantZero },
428+
{
429+
numeric: "always",
430+
relativeTo: instantZero,
431+
},
406432
),
407433
).toBe("in 1 year"); // We approximate years as 365 days
408434
});
@@ -412,14 +438,20 @@ describe("localizeRelativeInstant", () => {
412438
localizeRelativeInstant(
413439
new Temporal.Instant(nanosecondsPerDay * -13n),
414440
"en",
415-
{ relativeTo: instantZero },
441+
{
442+
numeric: "always",
443+
relativeTo: instantZero,
444+
},
416445
),
417446
).toBe("1 week ago");
418447
expect(
419448
localizeRelativeInstant(
420449
new Temporal.Instant(nanosecondsPerDay * -14n),
421450
"en",
422-
{ relativeTo: instantZero },
451+
{
452+
numeric: "always",
453+
relativeTo: instantZero,
454+
},
423455
),
424456
).toBe("2 weeks ago");
425457
});

app/web/utils/date.ts

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -274,41 +274,44 @@ export function timestampToPlainDateTime(
274274
return instantToPlainDateTime(timestampToInstant(timestamp), timezone);
275275
}
276276

277+
export interface LocalizeRelativeTimeOptions {
278+
style?: Intl.RelativeTimeFormatStyle;
279+
/// We override the default to "auto"
280+
numeric?: Intl.RelativeTimeFormatNumeric;
281+
/// If true, capitalize if the script supports it.
282+
standalone?: boolean;
283+
}
284+
285+
// Creating Intl objects every time is slow, so cache them.
277286
const relativeTimeFormatCache = new Map<string, Intl.RelativeTimeFormat>();
278287

279288
export function localizeRelativeTimeUnit(
280289
value: number,
281290
unit: Intl.RelativeTimeFormatUnit,
282291
locale: string,
283-
options?: {
284-
style?: Intl.RelativeTimeFormatStyle;
285-
numeric?: Intl.RelativeTimeFormatNumeric;
286-
},
292+
options?: LocalizeRelativeTimeOptions,
287293
): string {
288-
const cacheKey = JSON.stringify({ locale, ...options });
294+
const intlOptions = {
295+
style: options?.style ?? "long",
296+
numeric: options?.numeric ?? "auto",
297+
};
298+
const cacheKey = JSON.stringify({ locale, ...intlOptions });
289299
let formatter = relativeTimeFormatCache.get(cacheKey);
290300
if (!formatter) {
291-
formatter = new Intl.RelativeTimeFormat(locale, options);
301+
formatter = new Intl.RelativeTimeFormat(locale, intlOptions);
292302
relativeTimeFormatCache.set(cacheKey, formatter);
293303
}
294-
return formatter.format(value, unit);
295-
}
296-
297-
/// Localizes a number of days as a relative time string (e.g. "today", "tomorrow", "in 3 days").
298-
export function localizeRelativeDays(days: number, locale: string): string {
299-
return new Intl.RelativeTimeFormat(locale, { numeric: "auto" }).format(
300-
days,
301-
"day",
302-
);
304+
let result = formatter.format(value, unit);
305+
if (options?.standalone === true)
306+
result = capitalizeFirstLetter(result, locale);
307+
return result;
303308
}
304309

305310
/// Localizes a duration (positive or negative)'s largest unit (e.g. "4 days" ignoring minutes).
306311
export function localizeDurationLargestUnit(
307312
delta: Temporal.Duration,
308313
locale: string,
309-
options?: {
310-
style?: Intl.RelativeTimeFormatStyle;
311-
numeric?: Intl.RelativeTimeFormatNumeric;
314+
options?: LocalizeRelativeTimeOptions & {
312315
smallestUnit?: Temporal.PluralizeUnit<Temporal.TimeUnit>;
313316
t?: TFunction<"global", undefined>;
314317
},
@@ -351,10 +354,8 @@ export function localizeDurationLargestUnit(
351354
export function localizeRelativeInstant(
352355
instant: Temporal.Instant | Timestamp.AsObject,
353356
locale: string,
354-
options?: {
357+
options?: LocalizeRelativeTimeOptions & {
355358
relativeTo?: Temporal.Instant;
356-
style?: Intl.RelativeTimeFormatStyle;
357-
numeric?: Intl.RelativeTimeFormatNumeric;
358359
smallestUnit?: Temporal.PluralizeUnit<Temporal.TimeUnit>;
359360
t?: TFunction<"global", undefined>;
360361
},

0 commit comments

Comments
 (0)