Skip to content

Commit e37c684

Browse files
committed
Cleanup:
- Allow to override the time zone per invocation - Docs: Mention capability to reference formats by name, specify custom anchors, more compact formatting in example - Bump sizes
1 parent b8ff0db commit e37c684

File tree

6 files changed

+84
-30
lines changed

6 files changed

+84
-30
lines changed

docs/pages/docs/usage/dates-times.mdx

+19-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import PartnerContentLink from 'components/PartnerContentLink';
55

66
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.
77

8-
## Formatting dates and times
8+
## Formatting dates and times [#dates-times]
99

1010
You can format plain dates that are not part of a message with the `dateTime` function that is returned from the `useFormatter` hook:
1111

@@ -30,6 +30,12 @@ function Component() {
3030

3131
See [the MDN docs about `DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat#Using_options) to learn more about the options that you can provide to the `dateTime` function or [try the interactive explorer for `Intl.DateTimeFormat`](https://www.intl-explorer.com/DateTimeFormat).
3232

33+
If you have [global formats](/docs/usage/configuration#formats) configured, you can reference them by passing a name as the second argument:
34+
35+
```js
36+
format.dateTime(dateTime, 'short');
37+
```
38+
3339
<details>
3440
<summary>How can I parse dates or manipulate them?</summary>
3541

@@ -49,7 +55,7 @@ const twoDaysAgo = subDays(date, 2);
4955

5056
</details>
5157

52-
## Formatting relative time
58+
## Formatting relative times [#relative-times]
5359

5460
You can format plain dates that are not part of a message with the `relativeTime` function:
5561

@@ -124,28 +130,33 @@ function Component() {
124130
}
125131
```
126132

127-
## Formatting time ranges
133+
## Formatting date and time ranges [#date-time-ranges]
128134

129-
You can format date ranges with the `dateTimeRange` function:
135+
You can format ranges of dates and times with the `dateTimeRange` function:
130136

131137
```js
132138
import {useFormatter} from 'next-intl';
133139

134140
function Component() {
135141
const format = useFormatter();
136142
const dateTimeA = new Date('2020-11-20T08:30:00.000Z');
137-
const dateTimeB = new Date('2020-11-24T08:30:00.000Z');
143+
const dateTimeB = new Date('2021-01-24T08:30:00.000Z');
138144

145+
// Renders "Nov 20, 2020 – Jan 24, 2021"
139146
format.dateTimeRange(dateTimeA, dateTimeB, {
140-
weekday: 'long',
141147
year: 'numeric',
142-
month: 'long',
148+
month: 'short',
143149
day: 'numeric'
144150
});
145-
// this will render "Friday, November 20 – Tuesday, November 24, 2020"
146151
}
147152
```
148153

154+
If you have [global formats](/docs/usage/configuration#formats) configured, you can reference them by passing a name as the trailing argument:
155+
156+
```js
157+
format.dateTimeRange(dateTimeA, dateTimeB, 'short');
158+
```
159+
149160
## Dates and times within messages
150161

151162
Dates and times can be embedded within messages by using the ICU syntax.

docs/pages/docs/usage/numbers.mdx

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ function Component() {
2828

2929
See [the MDN docs about `NumberFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat#Using_options) to learn more about the options you can pass to the `number` function or [try the interactive explorer for `Intl.NumberFormat`](https://www.intl-explorer.com/NumberFormat).
3030

31+
If you have [global formats](/docs/usage/configuration#formats) configured, you can reference them by passing a name as the second argument:
32+
33+
```js
34+
format.number(499.9, 'precise');
35+
```
36+
3137
## Numbers within messages
3238

3339
Numbers can be embedded within messages by using the ICU syntax.

packages/next-intl/package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,11 @@
114114
"size-limit": [
115115
{
116116
"path": "dist/production/index.react-client.js",
117-
"limit": "12.99 KB"
117+
"limit": "13.055 KB"
118118
},
119119
{
120120
"path": "dist/production/index.react-server.js",
121-
"limit": "13.75 KB"
121+
"limit": "13.765 KB"
122122
},
123123
{
124124
"path": "dist/production/navigation.react-client.js",
@@ -134,7 +134,7 @@
134134
},
135135
{
136136
"path": "dist/production/server.react-server.js",
137-
"limit": "12.945 KB"
137+
"limit": "13.05 KB"
138138
},
139139
{
140140
"path": "dist/production/middleware.js",

packages/use-intl/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
"size-limit": [
9191
{
9292
"path": "dist/production/index.js",
93-
"limit": "12.5 kB"
93+
"limit": "12.565 kB"
9494
}
9595
]
9696
}

packages/use-intl/src/core/createFormatter.tsx

+19-17
Original file line numberDiff line numberDiff line change
@@ -78,21 +78,25 @@ export default function createFormatter({
7878
onError = defaultOnError,
7979
timeZone: globalTimeZone
8080
}: Props) {
81-
function applyGlobalTimeZone(options?: DateTimeFormatOptions) {
82-
if (globalTimeZone) {
83-
options = {...options, timeZone: globalTimeZone};
84-
} else {
85-
onError(
86-
new IntlError(
87-
IntlErrorCode.ENVIRONMENT_FALLBACK,
88-
process.env.NODE_ENV !== 'production'
89-
? `The \`timeZone\` parameter wasn't provided and there is no global default configured. Consider adding a global default to avoid markup mismatches caused by environment differences. Learn more: https://next-intl-docs.vercel.app/docs/configuration#time-zone`
90-
: undefined
91-
)
92-
);
81+
function applyTimeZone(options?: DateTimeFormatOptions) {
82+
if (!options?.timeZone) {
83+
if (globalTimeZone) {
84+
options = {...options, timeZone: globalTimeZone};
85+
} else {
86+
onError(
87+
new IntlError(
88+
IntlErrorCode.ENVIRONMENT_FALLBACK,
89+
process.env.NODE_ENV !== 'production'
90+
? `The \`timeZone\` parameter wasn't provided and there is no global default configured. Consider adding a global default to avoid markup mismatches caused by environment differences. Learn more: https://next-intl-docs.vercel.app/docs/configuration#time-zone`
91+
: undefined
92+
)
93+
);
94+
}
9395
}
96+
9497
return options;
9598
}
99+
96100
function resolveFormatOrOptions<Options>(
97101
typeFormats: Record<string, Options> | undefined,
98102
formatOrOptions?: string | Options
@@ -153,8 +157,7 @@ export default function createFormatter({
153157
formatOrOptions,
154158
formats?.dateTime,
155159
(options) => {
156-
options = applyGlobalTimeZone(options);
157-
160+
options = applyTimeZone(options);
158161
return new Intl.DateTimeFormat(locale, options).format(value);
159162
},
160163
() => String(value)
@@ -166,16 +169,15 @@ export default function createFormatter({
166169
start: Date | number,
167170
/** If a number is supplied, this is interpreted as a UTC timestamp. */
168171
end: Date | number,
169-
/** If a time zone is supplied, the `value` is converted to that time zone.
172+
/** If a time zone is supplied, the values are converted to that time zone.
170173
* Otherwise the user time zone will be used. */
171174
formatOrOptions?: string | DateTimeFormatOptions
172175
) {
173176
return getFormattedValue(
174177
formatOrOptions,
175178
formats?.dateTime,
176179
(options) => {
177-
options = applyGlobalTimeZone(options);
178-
180+
options = applyTimeZone(options);
179181
return new Intl.DateTimeFormat(locale, options).formatRange(start, end);
180182
},
181183
() => [dateTime(start), dateTime(end)].join(' – ')

packages/use-intl/test/core/createFormatter.test.tsx

+36-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ describe('dateTime', () => {
1414
})
1515
).toBe('Nov 20, 2020');
1616
});
17+
18+
it('allows to override a time zone', () => {
19+
const formatter = createFormatter({
20+
locale: 'en',
21+
timeZone: 'Europe/Berlin'
22+
});
23+
expect(
24+
formatter.dateTime(parseISO('2020-11-20T10:36:01.516Z'), {
25+
timeStyle: 'medium',
26+
dateStyle: 'medium',
27+
timeZone: 'America/New_York'
28+
})
29+
).toBe('Nov 20, 2020, 5:36:01 AM');
30+
});
1731
});
1832

1933
describe('number', () => {
@@ -284,8 +298,11 @@ describe('dateTimeRange', () => {
284298
minute: 'numeric'
285299
}
286300
)
287-
).toBe('1/10/06, 11:00 AM – 12:00 PM'); // 1 hour more given that the timezone is Europe/Berlin and the date is in UTC
301+
)
302+
// 1 hour more given that the timezone is Europe/Berlin and the date is in UTC
303+
.toBe('1/10/06, 11:00 AM – 12:00 PM');
288304
});
305+
289306
it('returns a reasonable fallback if an invalid format is provided', () => {
290307
const formatter = createFormatter({
291308
locale: 'en',
@@ -299,6 +316,24 @@ describe('dateTimeRange', () => {
299316
)
300317
).toBe('1/10/2007 – 1/10/2008');
301318
});
319+
320+
it('allows to override the time zone', () => {
321+
const formatter = createFormatter({
322+
locale: 'en',
323+
timeZone: 'Europe/Berlin'
324+
});
325+
expect(
326+
formatter.dateTimeRange(
327+
new Date(2007, 0, 10, 10, 0, 0),
328+
new Date(2008, 0, 10, 11, 0, 0),
329+
{
330+
timeStyle: 'medium',
331+
dateStyle: 'medium',
332+
timeZone: 'America/New_York'
333+
}
334+
)
335+
).toBe('Jan 10, 2007, 4:00:00 AM – Jan 10, 2008, 5:00:00 AM');
336+
});
302337
});
303338

304339
describe('list', () => {

0 commit comments

Comments
 (0)