From f07319f24a722ed03c52d9a823ecae211fc398c4 Mon Sep 17 00:00:00 2001 From: Jan Amann Date: Tue, 9 Jan 2024 14:28:15 +0100 Subject: [PATCH 1/4] fix: Change `numeric` option from `auto` to `always` for `format.relativeTime` --- .../use-intl/src/core/createFormatter.tsx | 9 +- .../test/core/createFormatter.test.tsx | 425 ++++++++++-------- 2 files changed, 256 insertions(+), 178 deletions(-) diff --git a/packages/use-intl/src/core/createFormatter.tsx b/packages/use-intl/src/core/createFormatter.tsx index ac6bfc35f..8d51be939 100644 --- a/packages/use-intl/src/core/createFormatter.tsx +++ b/packages/use-intl/src/core/createFormatter.tsx @@ -219,7 +219,14 @@ export default function createFormatter({ const value = calculateRelativeTimeValue(seconds, unit); return new Intl.RelativeTimeFormat(locale, { - numeric: 'auto' + // `numeric: 'auto'` can theoretically produce output like "yesterday", + // but it only works with integers. E.g. -1 day will produce "yesterday", + // but -1.1 days will produce "-1.1 days". Rounding before formatting is + // not desired, as the given dates might cross a threshold were the + // output isn't correct anymore. Example: 2024-01-08T23:00:00.000Z and + // 2024-01-08T01:00:00.000Z would produce "yesterday", which is not the + // case. By using `always` we can ensure correct output. + numeric: 'always' }).format(value, unit); } catch (error) { onError( diff --git a/packages/use-intl/test/core/createFormatter.test.tsx b/packages/use-intl/test/core/createFormatter.test.tsx index b5ce28407..e67c10dba 100644 --- a/packages/use-intl/test/core/createFormatter.test.tsx +++ b/packages/use-intl/test/core/createFormatter.test.tsx @@ -1,206 +1,277 @@ import {parseISO} from 'date-fns'; -import {it, expect} from 'vitest'; +import {it, expect, describe} from 'vitest'; import {createFormatter} from '../../src'; -it('formats a date and time', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' +describe('dateTime', () => { + it('formats a date and time', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.dateTime(parseISO('2020-11-20T10:36:01.516Z'), { + dateStyle: 'medium' + }) + ).toBe('Nov 20, 2020'); }); - expect( - formatter.dateTime(parseISO('2020-11-20T10:36:01.516Z'), { - dateStyle: 'medium' - }) - ).toBe('Nov 20, 2020'); }); -it('formats a number', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' +describe('number', () => { + it('formats a number', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect(formatter.number(123456)).toBe('123,456'); }); - expect(formatter.number(123456)).toBe('123,456'); -}); -it('formats a bigint', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' + it('formats a bigint', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect(formatter.number(123456789123456789n)).toBe( + '123,456,789,123,456,789' + ); }); - expect(formatter.number(123456789123456789n)).toBe('123,456,789,123,456,789'); -}); -it('formats a number as currency', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' + it('formats a number as currency', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.number(123456.789, {style: 'currency', currency: 'USD'}) + ).toBe('$123,456.79'); }); - expect( - formatter.number(123456.789, {style: 'currency', currency: 'USD'}) - ).toBe('$123,456.79'); -}); -it('formats a bigint as currency', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.number(123456789123456789n, { - style: 'currency', - currency: 'USD' - }) - ).toBe('$123,456,789,123,456,789.00'); + it('formats a bigint as currency', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.number(123456789123456789n, { + style: 'currency', + currency: 'USD' + }) + ).toBe('$123,456,789,123,456,789.00'); + }); }); -it('formats a relative time with the second unit', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime( - parseISO('2020-11-20T00:00:00.000Z'), - parseISO('2020-11-20T00:00:10.000Z') - ) - ).toBe('10 seconds ago'); -}); +describe('relativeTime', () => { + it('formats a relative time with the second unit', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime( + parseISO('2020-11-20T00:00:00.000Z'), + parseISO('2020-11-20T00:00:10.000Z') + ) + ).toBe('10 seconds ago'); + }); -it('formats a relative time with the minute unit', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime( - parseISO('2020-11-20T00:00:00.000Z'), - parseISO('2020-11-20T00:01:10.000Z') - ) - ).toBe('1 minute ago'); -}); + it('formats a relative time with the minute unit', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime( + parseISO('2020-11-20T00:00:00.000Z'), + parseISO('2020-11-20T00:01:10.000Z') + ) + ).toBe('1 minute ago'); + }); -it('formats a relative time with the hour unit', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime( - parseISO('2020-11-20T10:36:01.516Z'), - parseISO('2020-11-20T12:30:01.516Z') - ) - ).toBe('2 hours ago'); -}); + it('formats a relative time with the hour unit', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime( + parseISO('2020-11-20T10:36:01.516Z'), + parseISO('2020-11-20T12:30:01.516Z') + ) + ).toBe('2 hours ago'); + }); -it('formats a relative time with the day unit', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime( - parseISO('2020-11-20T00:00:00.000Z'), - parseISO('2020-11-22T00:10:00.000Z') - ) - ).toBe('2 days ago'); -}); + it('formats a relative time with the day unit', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime( + parseISO('2020-11-20T00:00:00.000Z'), + parseISO('2020-11-22T00:10:00.000Z') + ) + ).toBe('2 days ago'); + }); -it('formats a relative time with the month unit', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime( - parseISO('2022-12-01T00:00:00.000Z'), - parseISO('2023-01-01T00:00:00.000Z') - ) - ).toBe('last month'); -}); + it('formats a relative time with the month unit', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime( + parseISO('2022-11-01T00:00:00.000Z'), + parseISO('2023-01-01T00:00:00.000Z') + ) + ).toBe('2 months ago'); + }); -it('formats a relative time with the year unit', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime( - parseISO('2022-01-01T00:00:00.000Z'), - parseISO('2024-01-01T00:00:00.000Z') - ) - ).toBe('2 years ago'); -}); + it('formats a relative time with the year unit', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime( + parseISO('2022-01-01T00:00:00.000Z'), + parseISO('2024-01-01T00:00:00.000Z') + ) + ).toBe('2 years ago'); + }); -it('supports the future relative time', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime( - parseISO('2023-01-01T00:00:00.000Z'), - parseISO('2022-01-01T00:00:00.000Z') - ) - ).toBe('next year'); -}); + describe('numeric representation', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + const now = new Date('2024-01-09T15:00:00.000Z'); -it('formats a relative time with options', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime(parseISO('2020-11-20T08:30:00.000Z'), { - now: parseISO('2020-11-20T10:36:00.000Z'), - unit: 'day' - }) - ).toBe('today'); -}); + it.only.each([ + ['2022-07-10T15:00:00.000Z', '2 years ago'], + ['2022-07-11T15:00:00.000Z', '1 year ago'], + ['2023-01-09T15:00:00.000Z', '1 year ago'], + ['2023-01-10T15:00:00.000Z', '12 months ago'], + ['2023-07-09T15:00:00.000Z', '6 months ago'], + ['2023-12-09T15:00:00.000Z', '1 month ago'], + ['2023-12-10T15:00:00.000Z', '4 weeks ago'], + ['2024-01-02T15:00:00.000Z', '1 week ago'], + ['2024-01-03T15:00:00.000Z', '6 days ago'], + ['2024-01-08T15:00:00.000Z', '1 day ago'], + ['2024-01-08T15:01:00.000Z', '24 hours ago'], + ['2024-01-09T14:00:00.000Z', '1 hour ago'], + ['2024-01-09T14:01:00.000Z', '59 minutes ago'], + ['2024-01-09T14:59:00.000Z', '1 minute ago'], + ['2024-01-09T14:59:01.000Z', '59 seconds ago'], + ['2024-01-09T14:59:59.000Z', '1 second ago'], -it('supports the quarter unit', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime(parseISO('2020-01-01T00:00:00.000Z'), { - now: parseISO('2020-11-01T01:00:00.000Z'), - unit: 'quarter' - }) - ).toBe('3 quarters ago'); -}); + ['2024-01-09T15:00:01.000Z', 'in 1 second'], + ['2024-01-09T15:00:59.000Z', 'in 59 seconds'], + ['2024-01-09T15:01:00.000Z', 'in 1 minute'], + ['2024-01-09T15:59:00.000Z', 'in 59 minutes'], + ['2024-01-09T16:00:00.000Z', 'in 1 hour'], + ['2024-01-09T23:59:00.000Z', 'in 9 hours'], + ['2024-01-10T00:00:00.000Z', 'in 9 hours'], + ['2024-01-10T14:59:00.000Z', 'in 24 hours'], + ['2024-01-10T15:00:00.000Z', 'in 1 day'], + ['2024-01-10T23:59:00.000Z', 'in 1 day'], + ['2024-01-11T00:00:00.000Z', 'in 1 day'], + ['2024-01-11T01:00:00.000Z', 'in 1 day'], + ['2024-01-15T00:00:00.000Z', 'in 5 days'], + ['2024-01-16T00:00:00.000Z', 'in 6 days'], + ['2024-01-17T00:00:00.000Z', 'in 1 week'], + ['2024-01-30T00:00:00.000Z', 'in 3 weeks'], + ['2024-02-06T00:00:00.000Z', 'in 4 weeks'], + ['2024-02-06T15:00:00.000Z', 'in 4 weeks'], + ['2024-02-09T00:00:00.000Z', 'in 4 weeks'], + ['2024-02-09T01:00:00.000Z', 'in 1 month'], + ['2024-04-09T00:00:00.000Z', 'in 3 months'], + ['2024-12-09T00:00:00.000Z', 'in 11 months'], + ['2024-12-31T00:00:00.000Z', 'in 12 months'], + ['2025-01-08T00:00:00.000Z', 'in 12 months'], + ['2025-01-09T00:00:00.000Z', 'in 1 year'], + ['2025-07-09T00:00:00.000Z', 'in 1 year'], + ['2025-07-11T00:00:00.000Z', 'in 2 years'], + ['2026-01-09T00:00:00.000Z', 'in 2 years'], + ['2026-07-09T00:00:00.000Z', 'in 2 years'], + ['2026-07-11T00:00:00.000Z', 'in 3 years'] + ])('%s: %s', (value, expected) => { + expect(formatter.relativeTime(parseISO(value), now)).toBe(expected); + }); + }); -it('formats a relative time with a globally defined `now`', () => { - const formatter = createFormatter({ - locale: 'en', - now: parseISO('2020-11-20T01:00:00.000Z'), - timeZone: 'Europe/Berlin' - }); - expect( - formatter.relativeTime(parseISO('2020-11-20T00:00:00.000Z'), { - unit: 'day' - }) - ).toBe('today'); -}); + it('supports the future relative time', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime( + parseISO('2024-01-01T00:00:00.000Z'), + parseISO('2022-01-01T00:00:00.000Z') + ) + ).toBe('in 2 years'); + }); + + it('formats a relative time with options', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime(parseISO('2020-11-20T08:30:00.000Z'), { + now: parseISO('2020-11-20T10:36:00.000Z'), + unit: 'day', + numeric: 'auto' + }) + ).toBe('today'); + }); -it('formats a list', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' + it('supports the quarter unit', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime(parseISO('2020-01-01T00:00:00.000Z'), { + now: parseISO('2020-11-01T01:00:00.000Z'), + unit: 'quarter' + }) + ).toBe('3 quarters ago'); + }); + + it('formats a relative time with a globally defined `now`', () => { + const formatter = createFormatter({ + locale: 'en', + now: parseISO('2020-11-20T01:00:00.000Z'), + timeZone: 'Europe/Berlin' + }); + expect( + formatter.relativeTime(parseISO('2020-11-20T00:00:00.000Z'), { + unit: 'day' + }) + ).toBe('today'); }); - expect( - formatter.list(['apple', 'banana', 'orange'], {type: 'disjunction'}) - ).toBe('apple, banana, or orange'); }); -it('formats a set', () => { - const formatter = createFormatter({ - locale: 'en', - timeZone: 'Europe/Berlin' +describe('list', () => { + it('formats a list', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.list(['apple', 'banana', 'orange'], {type: 'disjunction'}) + ).toBe('apple, banana, or orange'); + }); + + it('formats a set', () => { + const formatter = createFormatter({ + locale: 'en', + timeZone: 'Europe/Berlin' + }); + expect( + formatter.list(new Set(['apple', 'banana', 'orange']), { + type: 'disjunction' + }) + ).toBe('apple, banana, or orange'); }); - expect( - formatter.list(new Set(['apple', 'banana', 'orange']), { - type: 'disjunction' - }) - ).toBe('apple, banana, or orange'); }); From 65126607045044578ecff7ee5b3725039f9609d9 Mon Sep 17 00:00:00 2001 From: Jan Amann Date: Tue, 9 Jan 2024 14:55:36 +0100 Subject: [PATCH 2/4] Fix tests --- packages/use-intl/src/core/createFormatter.tsx | 5 +++-- .../use-intl/test/core/createFormatter.test.tsx | 15 ++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/use-intl/src/core/createFormatter.tsx b/packages/use-intl/src/core/createFormatter.tsx index 8d51be939..ba4bf29b8 100644 --- a/packages/use-intl/src/core/createFormatter.tsx +++ b/packages/use-intl/src/core/createFormatter.tsx @@ -225,8 +225,9 @@ export default function createFormatter({ // not desired, as the given dates might cross a threshold were the // output isn't correct anymore. Example: 2024-01-08T23:00:00.000Z and // 2024-01-08T01:00:00.000Z would produce "yesterday", which is not the - // case. By using `always` we can ensure correct output. - numeric: 'always' + // case. By using `always` we can ensure correct output. The only exception + // is the formatting of times <1 second as "now". + numeric: unit === 'second' ? 'auto' : 'always' }).format(value, unit); } catch (error) { onError( diff --git a/packages/use-intl/test/core/createFormatter.test.tsx b/packages/use-intl/test/core/createFormatter.test.tsx index e67c10dba..2c245600a 100644 --- a/packages/use-intl/test/core/createFormatter.test.tsx +++ b/packages/use-intl/test/core/createFormatter.test.tsx @@ -145,7 +145,7 @@ describe('relativeTime', () => { }); const now = new Date('2024-01-09T15:00:00.000Z'); - it.only.each([ + it.each([ ['2022-07-10T15:00:00.000Z', '2 years ago'], ['2022-07-11T15:00:00.000Z', '1 year ago'], ['2023-01-09T15:00:00.000Z', '1 year ago'], @@ -162,7 +162,9 @@ describe('relativeTime', () => { ['2024-01-09T14:59:00.000Z', '1 minute ago'], ['2024-01-09T14:59:01.000Z', '59 seconds ago'], ['2024-01-09T14:59:59.000Z', '1 second ago'], + ['2024-01-09T14:59:59.999Z', 'now'], + ['2024-01-09T15:00:00.001Z', 'now'], ['2024-01-09T15:00:01.000Z', 'in 1 second'], ['2024-01-09T15:00:59.000Z', 'in 59 seconds'], ['2024-01-09T15:01:00.000Z', 'in 1 minute'], @@ -218,11 +220,10 @@ describe('relativeTime', () => { }); expect( formatter.relativeTime(parseISO('2020-11-20T08:30:00.000Z'), { - now: parseISO('2020-11-20T10:36:00.000Z'), - unit: 'day', - numeric: 'auto' + now: parseISO('2020-11-22T10:36:00.000Z'), + unit: 'day' }) - ).toBe('today'); + ).toBe('2 days ago'); }); it('supports the quarter unit', () => { @@ -245,10 +246,10 @@ describe('relativeTime', () => { timeZone: 'Europe/Berlin' }); expect( - formatter.relativeTime(parseISO('2020-11-20T00:00:00.000Z'), { + formatter.relativeTime(parseISO('2020-11-22T00:00:00.000Z'), { unit: 'day' }) - ).toBe('today'); + ).toBe('in 2 days'); }); }); From 3b0db670f3ef99703fd8854d43d4596d2dbf274f Mon Sep 17 00:00:00 2001 From: Jan Amann Date: Tue, 9 Jan 2024 15:17:14 +0100 Subject: [PATCH 3/4] Docs and fix another test --- docs/pages/docs/usage/configuration.mdx | 12 ++-- docs/pages/docs/usage/dates-times.mdx | 55 ++++++++++++------- .../tests/main.spec.ts | 2 +- .../test/core/createFormatter.test.tsx | 4 +- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/docs/pages/docs/usage/configuration.mdx b/docs/pages/docs/usage/configuration.mdx index c8acf0f06..bdee8ef39 100644 --- a/docs/pages/docs/usage/configuration.mdx +++ b/docs/pages/docs/usage/configuration.mdx @@ -15,7 +15,7 @@ Depending on if you handle [internationalization in Client- or Server Components `i18n.ts` can be used to provide configuration for **Server Components**. ```tsx filename="i18n.ts" -import {notFound} from "next/navigation"; +import {notFound} from 'next/navigation'; import {getRequestConfig} from 'next-intl/server'; // Can be imported from a shared config @@ -462,9 +462,12 @@ function getMessageFallback({namespace, key, error}) { } } - + ... - +; ``` @@ -504,11 +507,12 @@ return ( As a convenience, there are a couple of hooks that allow you to read global configuration. ```tsx -import {useLocale, useTimeZone, useMessages} from 'next-intl'; +import {useLocale, useTimeZone, useMessages, useNow} from 'next-intl'; function Component() { const locale = useLocale(); const timeZone = useTimeZone(); const messages = useMessages(); + const now = useNow(); } ``` diff --git a/docs/pages/docs/usage/dates-times.mdx b/docs/pages/docs/usage/dates-times.mdx index 89f0e4e98..16416830a 100644 --- a/docs/pages/docs/usage/dates-times.mdx +++ b/docs/pages/docs/usage/dates-times.mdx @@ -3,11 +3,11 @@ import PartnerContentLink from 'components/PartnerContentLink'; # Date and time formatting -The formatting of dates and times varies greatly between locales (e.g. "Apr 24, 2023" in `en-US` vs. "24 квіт. 2023 р." in `uk-UA`). By using the formatting capabilities of `next-intl`, you can handle all i18n differences in your Next.js app automatically. +The formatting of dates and times varies greatly between locales (e.g. "Apr 24, 2023" in `en-US` vs. "24 квіт. 2023 р." in `uk-UA`). By using the formatting capabilities of `next-intl`, you can handle i18n differences in your Next.js app automatically. ## Formatting dates and times -You can format plain dates that are not part of a message with the `useFormatter` hook: +You can format plain dates that are not part of a message with the `dateTime` function that is returned from the `useFormatter` hook: ```js import {useFormatter} from 'next-intl'; @@ -51,7 +51,7 @@ const twoDaysAgo = subDays(date, 2); ## Formatting relative time -Relative time durations can be formatted with a separate function: +You can format plain dates that are not part of a message with the `relativeTime` function: ```js import {useFormatter} from 'next-intl'; @@ -59,16 +59,18 @@ import {useFormatter} from 'next-intl'; function Component() { const format = useFormatter(); const dateTime = new Date('2020-11-20T08:30:00.000Z'); - const now = new Date('2020-11-20T10:36:00.000Z'); - // Renders "2 hours ago" - format.relativeTime(dateTime, now); + // At 2020-11-20T10:36:00.000Z, + // this will render "2 hours ago" + format.relativeTime(dateTime); } ``` -Note that values are rounded, so e.g. if 100 seconds have passed, "2 minutes ago" will be returned. +Note that values are rounded, so e.g. if 126 minutes have passed, "2 hours ago" will be returned. -If you want to use a specific unit, you can pass options with the second argument: +### Supplying `now` + +By default, `relativeTime` will use [the global value for `now`](/docs/usage/configuration#now). If you want to use a different value, you can explicitly pass this as the second parameter. ```js import {useFormatter} from 'next-intl'; @@ -78,29 +80,23 @@ function Component() { const dateTime = new Date('2020-11-20T08:30:00.000Z'); const now = new Date('2020-11-20T10:36:00.000Z'); - // Renders "today" - format.relativeTime(dateTime, { now, unit: 'day' }); + // Renders "2 hours ago" + format.relativeTime(dateTime, now); } ``` -Supplying `now` is necessary for the function to return consistent results. If you have [configured a global value for `now`](/docs/usage/configuration#now), you can omit the second argument: - -```js -format.relativeTime(dateTime); -``` - -### `useNow` - -If you want the relative time value to update over time, you can do so with the `useNow` hook: +If you want the relative time value to update over time, you can do so with [the `useNow` hook](/docs/usage/configuration#retrieve-global-configuration): ```js import {useNow, useFormatter} from 'next-intl'; function Component() { + // Use the global now value initially … const now = useNow({ - // Update every 10 seconds + // … and update it every 10 seconds updateInterval: 1000 * 10 }); + const format = useFormatter(); const dateTime = new Date('2020-11-20T10:36:01.516Z'); @@ -109,6 +105,25 @@ function Component() { } ``` +### Customizing the unit + +By default, `relativeTime` will pick a unit based on the difference between the passed date and `now` (e.g. 3 seconds, 40 minutes, 4 days, etc.). + +If you want to use a specific unit, you can provide options via the second argument: + +```js +import {useFormatter} from 'next-intl'; + +function Component() { + const format = useFormatter(); + const dateTime = new Date('2020-03-20T08:30:00.000Z'); + const now = new Date('2020-11-22T10:36:00.000Z'); + + // Renders "247 days ago" + format.relativeTime(dateTime, {now, unit: 'day'}); +} +``` + ## Dates and times within messages Dates and times can be embedded within messages by using the ICU syntax. diff --git a/examples/example-app-router-playground/tests/main.spec.ts b/examples/example-app-router-playground/tests/main.spec.ts index 41d2a7d13..bcf8edfea 100644 --- a/examples/example-app-router-playground/tests/main.spec.ts +++ b/examples/example-app-router-playground/tests/main.spec.ts @@ -195,7 +195,7 @@ it('can use `getMessageFallback`', async ({page}) => { it('can use the core library', async ({page}) => { await page.goto('/en'); const element = page.getByTestId('CoreLibrary'); - await expect(element).toHaveText('Relative time: tomorrow'); + await expect(element).toHaveText('Relative time: in 1 day'); }); it('can use `Link` on the server', async ({page}) => { diff --git a/packages/use-intl/test/core/createFormatter.test.tsx b/packages/use-intl/test/core/createFormatter.test.tsx index 2c245600a..f72f8f1ca 100644 --- a/packages/use-intl/test/core/createFormatter.test.tsx +++ b/packages/use-intl/test/core/createFormatter.test.tsx @@ -219,11 +219,11 @@ describe('relativeTime', () => { timeZone: 'Europe/Berlin' }); expect( - formatter.relativeTime(parseISO('2020-11-20T08:30:00.000Z'), { + formatter.relativeTime(parseISO('2020-03-20T08:30:00.000Z'), { now: parseISO('2020-11-22T10:36:00.000Z'), unit: 'day' }) - ).toBe('2 days ago'); + ).toBe('247 days ago'); }); it('supports the quarter unit', () => { From 6d97bcd825b218321a6704f97415cd8dc98220ad Mon Sep 17 00:00:00 2001 From: Jan Amann Date: Tue, 9 Jan 2024 15:26:24 +0100 Subject: [PATCH 4/4] Increase size limit --- packages/next-intl/package.json | 2 +- packages/use-intl/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/next-intl/package.json b/packages/next-intl/package.json index b47eebab9..f0772c960 100644 --- a/packages/next-intl/package.json +++ b/packages/next-intl/package.json @@ -114,7 +114,7 @@ "size-limit": [ { "path": "dist/production/index.react-client.js", - "limit": "12.841 KB" + "limit": "12.855 KB" }, { "path": "dist/production/index.react-server.js", diff --git a/packages/use-intl/package.json b/packages/use-intl/package.json index 1a9810108..ce8323c97 100644 --- a/packages/use-intl/package.json +++ b/packages/use-intl/package.json @@ -90,7 +90,7 @@ "size-limit": [ { "path": "dist/production/index.js", - "limit": "12.385 kB" + "limit": "12.4 kB" } ] }