Cloudflare Workers Date API and Intl.DateTimeFormat: Setting a Default Time Zone for Request Processing
0 reputation · 30 Aug 2025, 00:07 UTC
0 reputation · 30 Aug 2025, 00:07 UTC
Goal: Achieve consistent regional timestamp handling in a Cloudflare Worker so that all business logic sees times in a chosen zone while the underlying Date object remains UTC.
Constraints: The Workers runtime provides only the ECMAScript Date API, which always returns UTC values and ignores any TZ environment variable; explicit zone conversion must be performed with Intl.DateTimeFormat using an IANA identifier. There is no documented mechanism to set a default time zone for the Worker script, leaving developers to decide whether to apply conversion per request or globally, which can lead to inconsistent handling across services.
Uncertainty: The bundled V8 IANA time‑zone database may lag behind the latest zone changes, potentially causing off‑by‑one‑hour errors for rare historical or future dates, and it is unclear whether a Worker‑wide default zone can be established without repeating the zone argument in every Intl call.
34775 reputation · 30 Aug 2025, 01:48 UTC
No. Cloudflare Workers does not provide a supported way to set a Worker-wide default IANA time zone for Intl.DateTimeFormat. The timeZone option must be passed to every formatter instance; if it is omitted, the formatter uses UTC.
Keep all internal timestamps as UTC Date objects and apply zone conversion only at the presentation boundary. A helper that reads the desired zone from a request header—such as X-Request-Time-Zone—and returns a cached formatter gives you a single place to enforce this rule without repeating the option everywhere.
const formatters = new Map();
function getFormatter(timeZone) {
let fmt = formatters.get(timeZone);
if (!fmt) {
fmt = new Intl.DateTimeFormat('en-US', { timeZone });
formatters.set(timeZone, fmt);
}
return fmt;
}
export default {
async fetch(request) {
const zone = request.headers.get('X-Request-Time-Zone') || 'UTC';
const fmt = getFormatter(zone);
const now = new Date();
return new Response(fmt.format(now));
}
};
Because the Map lives in module scope, formatters are reused across requests in the same isolate. If an invalid zone is supplied, Intl.DateTimeFormat throws a RangeError, so validate the header against a known allow-list or catch the error and fall back to UTC.
Workers run on V8, which bundles an IANA time-zone database. For ordinary civil dates—roughly the last few decades through the near future—the offsets are reliable. The bundled version can lag behind recent IANA releases, and historical dates before the mid-20th century may be mis-offset if the database is stale. If your business logic depends on pre-1970 timestamps or on future zone transitions, verify a sample of dates against a current tz database or an external library rather than relying solely on the runtime.
To check your deployment, log new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York' }).format(new Date('1900-01-01T00:00:00Z')) and compare it with a reference implementation using an up-to-date tzdata package. If the offsets differ, move the critical formatting into a library that ships its own zone data.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.