Timezone emulation is a browser automation setting that makes the page's JavaScript believe it runs in a chosen time zone, so Date, Intl.DateTimeFormat and every clock on the page show local time for that zone.
It matters for any capture that contains a time: order histories, calendars, booking pages, dashboards, "posted 3 hours ago" labels. Without it, the browser uses the time zone of the server it runs on. This entry is part of the screenshot and rendering glossary.
A plain explanation
A web page does not know where you are. It asks the browser, and the browser answers from the operating system's clock settings. Emulation overrides that answer for one page or context. Your visitors in Tokyo and New York see different clock times for the same instant; emulation lets one machine produce both views.
One instant, three zones
We rendered a test page that formats one fixed instant, 06:30 UTC on 8 March 2026, with the browser's own settings, then switched the emulated zone between captures:
page.emulateTimezone() on 2026-10-15. The page formats Date.UTC(2026, 2, 8, 6, 30) with toLocaleString('en-US').| Setting | Intl reports |
Local time shown | getTimezoneOffset() |
|---|---|---|---|
| Nothing (host clock in Toronto) | America/Toronto |
Mar 8, 2026, 1:30 AM | 300 |
UTC |
UTC |
Mar 8, 2026, 6:30 AM | 0 |
America/New_York |
America/New_York |
Mar 8, 2026, 1:30 AM | 300 |
Asia/Kolkata |
Asia/Calcutta |
Mar 8, 2026, 12:00 PM | -330 |
Asia/Tokyo |
Asia/Tokyo |
Mar 8, 2026, 3:30 PM | -540 |
Mars/Base (invalid), set after Tokyo |
Asia/Tokyo |
Mar 8, 2026, 3:30 PM | -540 |
Three details are worth knowing:
- Invalid IDs fail silently once a zone is set. On a fresh page, Puppeteer 24 threw
Invalid timezone ID: Mars/Base. After a valid zone had been set on the same page, the same call returned without an error and the page kept Tokyo time. Validate IDs yourself. - Canonical names can differ. Both Puppeteer and Playwright accepted
Asia/Kolkataand then reported the older aliasAsia/Calcutta. A test that compares the string fails. - The offset sign is inverted.
getTimezoneOffset()is UTC minus local, so zones east of UTC are negative.
The instant sits two hours before US clocks changed that day. That is deliberate: test pages near a daylight-saving switch are where hard-coded offsets go wrong.
How it works technically
Chrome exposes the override through the DevTools Protocol command Emulation.setTimezoneOverride. It replaces the zone that the JavaScript engine's time zone database uses for that page, so Date, Intl and libraries built on them follow. It does not move the system clock, change your IP address, or alter times that the server formatted into the HTML. For server-side zones, the site usually reads an account setting, a cookie or IP-based location; see geolocation emulation for the location side.
How to set it in Puppeteer, Playwright and RenderScreenshot
Puppeteer sets it per page, and it applies to documents loaded after the call:
import puppeteer from 'puppeteer'; const zone = 'Asia/Tokyo'; // Throws RangeError for unknown IDs. Do not use Intl.supportedValuesOf('timeZone'): // in Node 22 it omits UTC and lists Asia/Calcutta instead of Asia/Kolkata. new Intl.DateTimeFormat('en-US', { timeZone: zone }); const browser = await puppeteer.launch(); try { const page = await browser.newPage(); await page.emulateTimezone(zone); await page.goto('https://example.com'); console.log(await page.evaluate(() => Intl.DateTimeFormat().resolvedOptions().timeZone)); await page.screenshot({ path: 'tokyo.png' }); } finally { await browser.close(); }
Playwright sets it on the context with timezoneId, usually next to locale:
import { chromium } from 'playwright'; const browser = await chromium.launch(); try { const context = await browser.newContext({ timezoneId: 'Asia/Tokyo', locale: 'ja-JP' }); const page = await context.newPage(); await page.goto('https://example.com'); await page.screenshot({ path: 'tokyo.png' }); } finally { await browser.close(); }
RenderScreenshot takes an IANA name in browser.timezone, or timezone as a query parameter. See the browser options.
curl --get https://api.renderscreenshot.com/v1/screenshot \ -H "Authorization: Bearer $RS_API_KEY" \ --data-urlencode 'url=https://example.com' \ --data-urlencode 'timezone=Asia/Tokyo' \ -d locale=ja-JP \ --output tokyo.png
import { Client, TakeOptions } from 'renderscreenshot'; const client = new Client(process.env.RS_API_KEY); const image = await client.take( TakeOptions.url('https://example.com').timezone('Asia/Tokyo').locale('ja-JP'), );
The table and both browser scripts were tested on 2026-10-15 with Node 22.18, Puppeteer 24.43.1 (headless shell mode, Chrome 148) and Playwright 1.64.1 (Chromium 156). The RenderScreenshot snippets follow the API docs and were not run for this page.
Common mistakes
- Trusting the default. A capture that looks right on your laptop shows UTC times on a server. Set the zone on every capture that shows a clock.
- Emulating after navigation. In Puppeteer, the page you already loaded keeps the times it rendered. Set the zone first, then
goto. - Using offsets instead of names.
UTC+9is not an IANA ID, and a fixed offset ignores daylight saving. UseAsia/Tokyo. - Setting the zone but not the locale. The time is right but the format is American. The localized screenshots guide sets both.
Related terms
- Accept-Language: the language half of a localized capture.
- Geolocation emulation: coordinates for location-aware pages.
- Headless Chrome: the browser that applies the override.
For locale and time zone together in a Puppeteer project, read emulating time zone and locale in Puppeteer. To capture one page in several zones, get a free API key.