title |
---|
Cannot infer intended usage of current time with `Date.now()`, `Date()`, or `new Date()` |
Reading the current time can be ambiguous. Sometimes you intend to capture the time when something was cached, other times you intend to capture the time of a user Request. You might also be trying to measure runtime performance to track elapsed time.
In this instance Next.js cannot determine your intent from usage so it needs you to clarify your intent. The way you do that depends on your use case. See the possible solutions below for how to move forward.
If you are using the current time for performance tracking with elapsed time use performance.now()
.
Before:
export default async function Page() {
const start = Date.now();
const data = computeDataSlowly(...);
const end = Date.now();
console.log(`somethingSlow took ${end - start} milliseconds to complete`)
return ...
}
After:
export default async function Page() {
const start = performance.now();
const data = computeDataSlowly(...);
const end = performance.now();
console.log(`somethingSlow took ${end - start} milliseconds to complete`)
return ...
}
Note: If you need report an absolute time to an observability tool you can also use
performance.timeOrigin + performance.now()
.
If you want to read the time when some cache entry is created (such as when a Next.js page is rendered at build-time or when revalidating a static page), move the current time read inside a cached function using "use cache"
.
Before:
async function InformationTable() {
const data = await fetch(...)
return (
<section>
<h1>Latest Info...</h1>
<table>{renderData(data)}</table>
</section>
)
}
export default async function Page() {
return (
<main>
<InformationTable />
Last Refresh: {new Date().toString()}
</main>
)
}
After:
async function InformationTable() {
"use cache"
const data = await fetch(...)
return (
<>
<section>
<h1>Latest Info...</h1>
<table>{renderData(data)}</table>
</section>
Last Refresh: {new Date().toString()}
</>
)
}
export default async function Page() {
return (
<main>
<InformationTable />
</main>
)
}
If you want the current time to change and be reactive, consider moving this usage to a Client Component. Note that Server Side Rendering timestamps in a Client Component is also considered ambiguous so to implement this properly Next.js needs you to only read the time in the browser, for instance by using useLayoutEffect()
or useEffect()
.
Before:
function Timestamp() {
return 'current time: ' + new Date().toString()
}
export default async function Page() {
return (
<main>
...
<Timestamp />
</main>
)
}
After:
'use client'
import { useState, useLayoutEffect } from 'react'
export function Timestamp() {
const [time, setTime] = useState(null)
useLayoutEffect(() => {
// You can determine when and how often to update
// the time here. In this example we update it only once
setTime(new Date().toString())
}, [])
if (time) {
return 'current time: ' + time
}
return null
}
import { Timestamp } from './client-components'
export default async function Page() {
return (
<main>
...
<Timestamp />
</main>
)
}
If you want the current time to represent the time of a user Request, add await connection()
before you read the current time. This instructs Next.js that everything after the await connection()
requires there to be a user Request before it can run. If you add await connection()
you will also need to ensure there is a Suspense boundary somewhere above the waiting component that describes a fallback UI React can use. The Suspense can be anywhere in the parent component stack but it is shown here above the waiting component for demonstration purposes.
Before:
export default async function Page() {
const currentTime = Date.now()
if (currentTime > someTriggerDate) {
return <SpecialBanner />
} else {
return <NormalBanner />
}
}
After:
import { connection } from 'next/server'
async function BannerSkeleton() {
...
}
export default async function Page() {
return <Suspense fallback={<BannerSkeleton />}>
<DynamicBanner />
</Suspense>
}
async function DynamicBanner() {
await connection();
const currentTime = Date.now();
if (currentTime > someTriggerDate) {
return <SpecialBanner />
} else {
return <NormalBanner />
}
}