Without the Intl.DateTimeFormat timeZone option, a date is formatted in the
time zone of whichever runtime renders it: UTC on most servers, the visitor's
zone in the browser. no-timezone-without-explicit-timezone reports date
formatting without an explicit timeZone, and local-time getters such as
getHours(), in render code.
| Rule | hydration-proof/no-timezone-without-explicit-timezone |
|---|---|
| What it reports | Require an explicit timeZone when dates are formatted or split into parts during render |
| recommended / next | Warning |
| strict | Error |
| Server Components | Skipped with the next preset (they never hydrate) |
| Suggestions | Yes |
| Options | defaultTimeZone |
What it reports
In render code:
toLocaleDateString()andtoLocaleTimeString()without atimeZoneoption.new Intl.DateTimeFormat()/Intl.DateTimeFormat()without atimeZoneoption.toLocaleString()without atimeZoneoption when the value is clearly a date: the receiver isnew Date(...)(or a variable initialized with it in the same function), or the options contain date or time fields (dateStyle,timeStyle,year,month,day,hour,minute, ...).getHours(),getMinutes(),getDate(),getDay(),getMonth(),getFullYear(),getTimezoneOffset(),toDateString(),toTimeString()andtoString()onnew Date(...)or a variable initialized with it in the same function.Intl.DateTimeFormat().resolvedOptions().timeZone, which reads the runtime's time zone.
When the options are not an object literal
(toLocaleDateString('en-US', options)), the rule cannot see them and does
not report.
Suggestions (never automatic fixes):
- add
timeZone: 'UTC'(or thedefaultTimeZoneoption) to the options object, or add an options object when there is none; - replace
getHours()and the other local getters with their UTC versions (getUTCHours()), whendefaultTimeZoneis'UTC'.
Why the Intl.DateTimeFormat timeZone option matters
Dates are formatted in the time zone of the runtime. Servers usually run in UTC, browsers in the visitor's zone:
server HTML: <p>Signed in at 5:00 AM</p> (UTC)
client render: <p>Signed in at 10:00 AM</p> (Asia/Karachi)Hydration fails whenever the two zones give a different result, which for times is almost always, and for dates near midnight. Timezone hydration mismatches explains how to pick the zone both sides use.
Incorrect
function LastLogin({ at }) {
return <p>Signed in at {new Date(at).toLocaleTimeString("en-US")}</p>;
}
function Posted({ at }) {
const date = new Date(at);
return (
<p>
{date.getDate()}/{date.getMonth() + 1}
</p>
);
}
function Schedule({ at }) {
const format = new Intl.DateTimeFormat("en-US", {
dateStyle: "medium",
timeStyle: "short",
});
return <p>{format.format(at)}</p>;
}Correct
function LastLogin({ at, timeZone }) {
// The user's zone, stored in their profile or a cookie and passed from the server.
return (
<p>Signed in at {new Date(at).toLocaleTimeString("en-US", { timeZone })}</p>
);
}
function Posted({ at }) {
const date = new Date(at);
return (
<p>
{date.getUTCDate()}/{date.getUTCMonth() + 1}
</p>
);
}
function Schedule({ at }) {
const format = new Intl.DateTimeFormat("en-US", {
timeZone: "UTC",
dateStyle: "medium",
timeStyle: "short",
});
return <p>{format.format(at)}</p>;
}If the visitor's own zone is required and the server cannot know it, render
the date after hydration (in useEffect) or put suppressHydrationWarning on
the element that shows it.
Options
| Option | Type | Default | Description |
|---|---|---|---|
| defaultTimeZone | string | 'UTC' | Time zone inserted by the suggestion. |
{
rules: {
'hydration-proof/no-timezone-without-explicit-timezone': ['warn', { defaultTimeZone: 'Europe/Berlin' }],
},
}defaultTimeZone(string, default'UTC'): the zone the suggestion inserts. The UTC getter suggestion is only offered when this is'UTC'.
Messages
What ESLint prints for this rule, word for word:
<call>formats the date in the time zone of whichever runtime renders it. Servers usually run in UTC and browsers in the visitor's zone, so the text does not match during hydration. Pass an explicittimeZoneoption.<call>reads the date in the runtime's local time zone, which differs between the server and the browser. Use the UTC methods, or format with an explicittimeZone.<call>reads the runtime's time zone, which differs between the server and the browser. Pass the time zone from the server, or read it after hydration.- Add timeZone: '<timeZone>'.
- Use <method>() (UTC).
When not to use it
When the servers and all visitors share one time zone, or the runtime's default zone is set explicitly on both sides.
Related
no-locale-without-explicit-locale:date.toLocaleDateString()without arguments is missing a locale and a time zone. Both rules report it: this one on the method name, that one on the whole call. They are two separate fixes.no-date-in-render:new Date().getHours()reads the current time (that rule) and splits it in the local zone (this rule).- HP1001: text differs between server and client, how
hydration-proof testreports a timezone difference - Timezone hydration mismatches: the cause and its fixes