API Reference

useFluxTime()

Returns the current time corrected for clock skew between this device and your server — one number, computed once at bootstrap, so a countdown or expiry timer is accurate even on a device with a drifting system clock.

Prerequisites
  • FluxProvider mounted above this component — see api/with-flux
  • At least one successful bootstrap() call, for clockSkewMs to be non-zero — see step 6
1

Basic usage

Call useFluxTime() anywhere you'd otherwise reach for Date.now() — a countdown, a booking window, an expiry display.
typescript
1import { useFluxTime } from '@tsworldtech/flux-next'
2
3function BookingCountdown({ expiresAt }: { expiresAt: number }) {
4 const now = useFluxTime() // Date.now() + flux.clockSkewMs
5
6 const remainingMs = expiresAt - now
7 return <span>{formatCountdown(remainingMs)}</span>
8}
2

What it actually returns

It's a direct calculation, not a live subscription — calling it gives you the corrected time at that instant, nothing more.
typescript
1// useFluxTime() is a single, direct calculation — not a subscription:
2function useFluxTime() {
3 return Date.now() + flux.clockSkewMs
4}
5
6// It reflects the correction available at the exact moment it's called.
7// It doesn't tick on its own — pair it with your own interval if a
8// component needs to keep re-reading it every second.
It won't tick on its own

A component reading useFluxTime() only gets a fresh value when it re-renders. For a visibly ticking countdown, drive re-renders yourself with an interval, as shown next.

3

Driving a ticking countdown

Force a re-render on your own interval, then read useFluxTime() fresh each tick.
typescript
1function BookingCountdown({ expiresAt }: { expiresAt: number }) {
2 const [, forceTick] = useReducer((c) => c + 1, 0)
3
4 useEffect(() => {
5 const id = setInterval(forceTick, 1000)
6 return () => clearInterval(id)
7 }, [])
8
9 const now = useFluxTime()
10 return <span>{formatCountdown(expiresAt - now)}</span>
11}
4

Where clockSkewMs comes from

Computed once per session, during flux.bootstrap(), by comparing the response's Date header against local time with a half-latency correction.
typescript
1// clockSkewMs is computed once, during flux.bootstrap(), from the
2// response's Date header and the request's own timing:
3
4const serverTime = new Date(response.headers.get('Date')).getTime()
5const latency = (Date.now() - requestStartTime) / 2
6const clockSkewMs = serverTime - (requestStartTime + latency)
7
8// Stored on the engine as flux.clockSkewMs — a single number for
9// the rest of the session, not recomputed on every call.
5

Why this exists

The same correction that powers this hook is also used internally, wherever Flux itself needs to reason about time relative to the server rather than the device.
typescript
1// A device with a fast or slow system clock would otherwise show
2// incorrect countdowns, booking windows, or token-expiry timers —
3// even though the server's own sense of time is correct.
4
5// Documented uses inside Flux itself:
6// - countdown timers / booking windows -> useFluxTime()
7// - auth token expiry display -> useFluxTime()
8// - handshake replay timestamp correction -> flux.clockSkewMs
9// (entry.timestamp + clockSkewMs, before building the manifest
10// sent to revalidateFn — see api/replay)
6

Before the first bootstrap resolves

If a component reads this hook before bootstrap() has completed at least once, there's no correction to apply yet — it behaves like plain Date.now() until then.
typescript
1// Until the first successful bootstrap() call computes clockSkewMs,
2// useFluxTime() simply returns local device time (correction of 0).
3// This matters if you're rendering a countdown before bootstrap has
4// resolved — expect it to snap to the corrected value once it does.
5const now = useFluxTime() // == Date.now() until clockSkewMs is set

For how clockSkewMs is computed and stored, see api/bootstrap. For its other consumer — handshake replay timestamp correction — see api/replay.