ScreenshotNeo

BlogHow-to

How to Set a Time Zone for a Page in Puppeteer

Set a Puppeteer page’s time zone with emulateTimezone, verify it in the browser, handle multiple pages, and troubleshoot common launch and ICU errors.

By the ScreenshotNeo team1 October 20266 min read

Use await page.emulateTimezone('America/Los_Angeles') before navigating or evaluating page code. Puppeteer applies the emulation to that Page and returns a promise, so await it. The value must be an ICU time zone identifier such as America/Los_Angeles or Europe/London. Pass null to disable emulation.

Official references: Page.emulateTimezone(), the Page class, and Puppeteer’s launch options.

1. Minimal working example

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

await page.emulateTimezone('America/Los_Angeles');
await page.goto('https://example.com', {waitUntil: 'networkidle2'});

const result = await page.evaluate(() => ({
  localTime: new Date().toString(),
  isoTime: new Date().toISOString(),
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
}));

console.log(result);
await browser.close();

toISOString() always represents UTC. Use toString(), Intl.DateTimeFormat, or application-specific formatting to observe the emulated local zone. The browser-side value returned by Intl.DateTimeFormat().resolvedOptions().timeZone is a useful verification check.

2. Install Puppeteer

mkdir timezone-demo
cd timezone-demo
npm init -y
npm install puppeteer
# package.json: add "type": "module"
node index.js

The full puppeteer package downloads a compatible Chrome for Testing build during installation. Puppeteer documents that its bundled browser is the supported compatibility path. If you use puppeteer-core or manage Chrome yourself, provide executablePath or channel; see the installation guide.

3. Choosing the time zone identifier

Input Use Example
Region identifier Recommended; includes daylight-saving rules where applicable America/Los_Angeles
Another region Test a user or deployment in a different location Europe/London
null Disable timezone emulation and return to the browser default await page.emulateTimezone(null)

The accepted set comes from ICU’s supported time zone identifiers. Use names from the ICU metaZones.txt data rather than inventing offset strings such as UTC-8. The exact set can depend on the browser and runtime build, so validate identifiers in the same environment that runs your tests.

4. When to call emulateTimezone

Call it before navigation

Set the timezone before goto when the page reads the clock during initial HTML generation, hydration, or startup scripts.

const page = await browser.newPage();
await page.emulateTimezone('Asia/Tokyo');
await page.goto('https://example.com', {waitUntil: 'domcontentloaded'});

Changing zones during one workflow

You can change the emulation on the same page, then reload or trigger the code that reads the clock again.

await page.emulateTimezone('Europe/London');
await page.reload({waitUntil: 'networkidle2'});

await page.emulateTimezone('Australia/Sydney');
await page.reload({waitUntil: 'networkidle2'});

Resetting the page

await page.emulateTimezone(null);

The method is page-scoped. A browser can contain multiple pages, and each page needs its own setting.

5. Multiple pages, contexts, and workers

const pages = await Promise.all([
  browser.newPage(),
  browser.newPage(),
]);

await Promise.all([
  pages[0].emulateTimezone('America/New_York'),
  pages[1].emulateTimezone('Europe/Berlin'),
]);

await Promise.all([
  pages[0].goto('https://example.com'),
  pages[1].goto('https://example.com'),
]);

Do not assume a setting on one tab changes another tab. In a test runner, set the timezone in the page or context setup hook that creates each page. If your code opens a popup with window.open, configure that new Page as well.

6. What timezone emulation changes (and what it does not)

  • Browser JavaScript APIs that derive local time, including Date string formatting and Intl.DateTimeFormat, observe the emulated zone.
  • UTC values such as Date.prototype.toISOString() remain UTC.
  • Server-side time, database time, cron schedules, and API responses are not changed by this browser setting.
  • A cookie or header that your application uses to select a locale or timezone is separate configuration; set it explicitly when the application requires it.
  • Timezone emulation does not automatically change language, currency, viewport, geolocation, or user agent. Configure those independently.

7. Verifying the setting in a test

const observed = await page.evaluate(() => {
  const date = new Date(2024, 0, 15, 12, 0, 0);
  const formatter = new Intl.DateTimeFormat('en-US', {
    dateStyle: 'full',
    timeStyle: 'long',
  });

  return {
    resolvedTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    formatted: formatter.format(date),
    offsetMinutes: date.getTimezoneOffset(),
  };
});

if (observed.resolvedTimezone !== 'America/Los_Angeles') {
  throw new Error(`Unexpected timezone: ${observed.resolvedTimezone}`);
}

Prefer assertions about the behavior your product needs: a rendered date, a selected schedule, or a formatted string. Avoid asserting a fixed UTC offset for dates that cross daylight-saving transitions.

8. TypeScript and the null reset

The reference prose documents null as the disable value, while some installed TypeScript declarations may expose an optional string parameter only. If your version rejects null, inspect the declaration shipped with that exact Puppeteer release before adding a type workaround. Keep the runtime and type-package versions aligned.

9. Using puppeteer-core or a system browser

import puppeteer from 'puppeteer-core';

const browser = await puppeteer.launch({
  executablePath: process.env.CHROME_PATH,
  // Or use a known installed channel where supported:
  // channel: 'chrome',
});

const page = await browser.newPage();
await page.emulateTimezone('America/Los_Angeles');
await page.goto('https://example.com');

Puppeteer says it is only guaranteed to work with its bundled browser. A system browser can work, but version mismatches may produce launch or protocol errors. Pin compatible versions in CI and provide an explicit executable path or channel when using puppeteer-core.

10. Troubleshooting

Symptom Likely cause Fix
Invalid timezone or protocol error The identifier is not an ICU name supported by that browser build. Use a region identifier such as America/Los_Angeles; verify it against ICU data and the deployed browser.
The page still shows the old local time The timezone was set after page code already ran. Call emulateTimezone before goto, reload after changing it, and wait for the relevant application code.
One tab changed but another did not Timezone emulation is page-scoped. Call it on every Page, including popup pages.
TypeScript rejects null Your installed declaration is narrower than the documented runtime behavior. Check the installed Puppeteer version and declaration; keep package versions aligned.
Could not find Chrome puppeteer-core has no browser download and no executable configured. Set executablePath or channel, or install the full puppeteer package.
Launch or protocol incompatibility A system Chrome version does not match the Puppeteer release. Use Puppeteer’s bundled Chrome for Testing build or pin a compatible browser and package pair.
Dates differ between browser and API Timezone emulation affects browser APIs, not server-side processing. Configure the server, request headers, cookie, or API input separately if that system owns the timezone decision.

11. Performance, reliability, and test design

  • Set the timezone once during page setup and reuse the page when your test isolation allows it.
  • Use a small matrix of representative zones, including one with daylight-saving changes and one without, instead of assuming a single fixed offset covers every case.
  • Use deterministic application clocks or test data alongside timezone emulation when assertions depend on a particular date. Timezone selection changes interpretation; it does not freeze time.
  • Wait for the page state that displays the date, such as a selector or network completion, before reading it.
  • Record the browser, Puppeteer, and timezone identifier in failed test output so environment differences are diagnosable.
  • Close pages and the browser in a finally block in long-running workers to avoid resource leaks.

12. Screenshot the result without managing a browser

If your goal is a rendered image or PDF rather than browser automation, ScreenshotNeo accepts timezone configuration through its screenshot API and handles the capture service for you. See the ScreenshotNeo API documentation for the current parameter names and options.

Or skip the browser setup

Use one request to capture a page:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server lets Claude, Cursor, and other MCP clients take screenshots, inspect pages, and capture PDFs. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

13. FAQ

Can I pass an offset such as UTC+2?

Use an ICU region identifier instead. Region names carry the correct daylight-saving rules where applicable.

Does this change the server’s timezone?

No. It changes timezone behavior in the emulated browser page. Configure server-side systems independently.

Do I need to set it before every navigation?

Set it before the first navigation and keep it on that page. If you change the value, reload or rerun the code that reads local time.

How do I restore the default?

Call await page.emulateTimezone(null), subject to the type declaration shipped with your installed version.

Does it apply to every tab in the browser?

No. Apply it separately to each Puppeteer Page.