Skip to content

feat: Support numberingSystem and style for relative time formatting #1057

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

Merged
merged 2 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/next-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
"size-limit": [
{
"path": "dist/production/index.react-client.js",
"limit": "15.735 KB"
"limit": "15.765 KB"
},
{
"path": "dist/production/index.react-server.js",
Expand All @@ -134,7 +134,7 @@
},
{
"path": "dist/production/server.react-server.js",
"limit": "15.645 KB"
"limit": "15.675 KB"
},
{
"path": "dist/production/middleware.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/use-intl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
"size-limit": [
{
"path": "dist/production/index.js",
"limit": "15.235 kB"
"limit": "15.26 kB"
}
]
}
3 changes: 3 additions & 0 deletions packages/use-intl/src/core/RelativeTimeFormatOptions.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
type RelativeTimeFormatOptions = {
now?: number | Date;
unit?: Intl.RelativeTimeFormatUnit;
numberingSystem?: string;
style?: Intl.RelativeTimeFormatStyle;
// We don't support the `numeric` property by design (see https://github.com/amannn/next-intl/pull/765)
};

export default RelativeTimeFormatOptions;
66 changes: 35 additions & 31 deletions packages/use-intl/src/core/createFormatter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,49 +212,53 @@ export default function createFormatter({
}
}

function extractNowDate(
nowOrOptions?: RelativeTimeFormatOptions['now'] | RelativeTimeFormatOptions
) {
if (nowOrOptions instanceof Date || typeof nowOrOptions === 'number') {
return new Date(nowOrOptions);
}
if (nowOrOptions?.now !== undefined) {
return new Date(nowOrOptions.now);
}
return getGlobalNow();
}

function relativeTime(
/** The date time that needs to be formatted. */
date: number | Date,
/** The reference point in time to which `date` will be formatted in relation to. */
nowOrOptions?: RelativeTimeFormatOptions['now'] | RelativeTimeFormatOptions
) {
try {
let nowDate: Date | undefined,
unit: Intl.RelativeTimeFormatUnit | undefined;
const opts: Intl.RelativeTimeFormatOptions = {};
if (nowOrOptions instanceof Date || typeof nowOrOptions === 'number') {
nowDate = new Date(nowOrOptions);
} else if (nowOrOptions) {
if (nowOrOptions.now != null) {
nowDate = new Date(nowOrOptions.now);
} else {
nowDate = getGlobalNow();
}
unit = nowOrOptions.unit;
opts.style = nowOrOptions.style;
// @ts-expect-error -- Types are slightly outdated
opts.numberingSystem = nowOrOptions.numberingSystem;
}

if (!nowDate) {
nowDate = getGlobalNow();
}

const dateDate = new Date(date);
const nowDate = extractNowDate(nowOrOptions);
const seconds = (dateDate.getTime() - nowDate.getTime()) / 1000;

const unit =
typeof nowOrOptions === 'number' ||
nowOrOptions instanceof Date ||
nowOrOptions?.unit === undefined
? resolveRelativeTimeUnit(seconds)
: nowOrOptions.unit;
if (!unit) {
unit = resolveRelativeTimeUnit(seconds);
}

const value = calculateRelativeTimeValue(seconds, unit);
// `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. The only exception
// is the formatting of times <1 second as "now".
opts.numeric = unit === 'second' ? 'auto' : 'always';

return new Intl.RelativeTimeFormat(locale, {
// `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. The only exception
// is the formatting of times <1 second as "now".
numeric: unit === 'second' ? 'auto' : 'always'
}).format(value, unit);
const value = calculateRelativeTimeValue(seconds, unit);
return new Intl.RelativeTimeFormat(locale, opts).format(value, unit);
} catch (error) {
onError(
new IntlError(IntlErrorCode.FORMATTING_ERROR, (error as Error).message)
Expand Down
19 changes: 17 additions & 2 deletions packages/use-intl/test/core/createFormatter.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,19 @@ describe('relativeTime', () => {
).toBe('in 2 years');
});

it('formats a relative time with a different style', () => {
const formatter = createFormatter({
locale: 'en',
timeZone: 'Europe/Berlin'
});
expect(
formatter.relativeTime(parseISO('2020-03-20T08:30:00.000Z'), {
now: parseISO('2020-11-22T10:36:00.000Z'),
style: 'narrow'
})
).toBe('8mo ago');
});

it('formats a relative time with options', () => {
const formatter = createFormatter({
locale: 'en',
Expand All @@ -235,9 +248,11 @@ describe('relativeTime', () => {
expect(
formatter.relativeTime(parseISO('2020-03-20T08:30:00.000Z'), {
now: parseISO('2020-11-22T10:36:00.000Z'),
unit: 'day'
unit: 'day',
numberingSystem: 'arab',
style: 'narrow'
})
).toBe('247 days ago');
).toBe('٢٤٧d ago');
});

it('supports the quarter unit', () => {
Expand Down
Loading