How to Capture Website Screenshots in a Specific Timezone for Visual Tests
Set the browser timezone, stabilize the page, and compare repeatable screenshots with Playwright. Includes Puppeteer, Selenium, and ScreenshotNeo options.
To capture a website screenshot in a specific timezone for a visual test, set the timezone on the browser context that renders the page, then navigate to a deterministic UI state and capture it. In Playwright Test, configure timezoneId globally or per test and use toHaveScreenshot() to compare the result with a reviewed baseline. Configure locale separately if language or date formatting conventions matter. Browser emulation does not set the test runner, server, or database timezone.
1. Configure Playwright and capture a baseline
Install Playwright Test if it is not already in the project, then create or update playwright.config.ts. The example uses Paris time, British English, and a fixed viewport:
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
baseURL: 'http://127.0.0.1:3000',
locale: 'en-GB',
timezoneId: 'Europe/Paris',
viewport: { width: 1280, height: 720 },
},
});
Start the app as usual, then add a visual test such as tests/schedule.spec.ts:
import { test, expect } from '@playwright/test';
test('renders the schedule in Paris time', async ({ page }) => {
await page.goto('/schedule');
await expect(page).toHaveScreenshot('schedule-paris.png');
});
Run the test once to create the reference screenshot, inspect it, and commit the approved baseline. Later runs compare against that reference. Treat an update to a baseline as a reviewable change: inspect the actual and expected images before accepting it. Playwright documents baseline updates and comparison controls such as maxDiffPixels and capture styles for intentionally volatile regions in its visual comparisons documentation.
Set the timezone for only one test
For a test-specific override, use test.use() in a separate test file or describe block so the setting applies to the intended tests:
import { test, expect } from '@playwright/test';
test.describe('Paris schedule', () => {
test.use({ timezoneId: 'Europe/Paris', locale: 'en-GB' });
test('shows local event times', async ({ page }) => {
await page.goto('/schedule');
await expect(page).toHaveScreenshot('schedule-paris.png');
});
});
Use a project-level use setting when all tests in a project share the same environment. Prefer a named region such as Europe/Paris when the scenario represents a place; named zones account for regional daylight-saving rules. Playwright’s documented examples also include Europe/Berlin. Confirm that the identifier is supported by the browser and framework version in use.
2. Make the rendered page deterministic
A timezone setting controls the browser’s timezone behavior. It does not freeze the date, provide stable data, or configure the server. Make the test’s inputs repeatable as well:
- Seed or mock time-dependent API responses so each run receives the same timestamps and records.
- If application code running in the test process depends on local time, configure the process timezone separately, for example with the
TZenvironment variable for Node.js. Browser timezone emulation alone does not change it. - Configure backend, database, and remote API behavior independently when those systems determine the displayed time or content.
- Wait for the intended UI state, including fonts and images. For lazy-loaded content, scroll the relevant area into view or otherwise trigger loading before capture.
- Keep viewport, browser version, operating system, and rendering settings consistent with the environment used to create the baseline.
- Mask or suppress a region only when its variability is intentional and unrelated to the visual behavior under test. Do not hide a difference that may be a real regression.
Timezone and locale are separate inputs: timezone affects local-time interpretation, while locale can affect language and formatting conventions. Pin both when the requirement covers a particular market presentation. Playwright notes that these settings affect the browser, not the test runner, in its browser emulation documentation.
3. Choose capture and comparison options
| Need | Playwright approach |
|---|---|
| All tests in a project use one timezone | Set use.timezoneId in the shared configuration. |
| Only a group of tests uses a timezone | Set timezoneId and, if needed, locale with test.use() in that scope. |
| Compare a page screenshot | Use await expect(page).toHaveScreenshot('name.png'). |
| Compare one component | Use the locator screenshot assertion, for example await expect(page.locator('[data-testid="schedule"]')).toHaveScreenshot(). |
| Handle small known rendering differences | Use documented assertion options such as maxDiffPixels only with a justified tolerance; a looser threshold can hide meaningful changes. |
| Ignore a deliberately volatile region | Use a capture stylesheet or masking approach supported by the installed Playwright version, and document why the region is excluded. |
Pin the browser and operating environment for reliable comparisons. Playwright warns that host OS, browser version, settings, hardware, power source, and headless mode can affect rendering. Generate and compare baselines in the same environment where practical, and review diffs before updating snapshots.
4. Use Puppeteer or Selenium if they are already in your stack
The timezone must be applied before the page is captured. These alternatives provide timezone controls, but the cited APIs do not establish the same built-in baseline comparison workflow as Playwright Test; use the visual comparison tooling already chosen by your project.
Puppeteer
import puppeteer from 'puppeteer';
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage({
viewport: { width: 1280, height: 720 },
});
await page.emulateTimezone('Europe/Paris');
await page.goto('http://127.0.0.1:3000/schedule', {
waitUntil: 'networkidle0',
});
await page.screenshot({ path: 'schedule-paris.png', fullPage: true });
} finally {
await browser.close();
}
Puppeteer documents timezone emulation through page.emulateTimezone() and capture through page.screenshot(). See the timezone API and screenshot API. Check your installed version for the exact option types and navigation behavior.
Selenium Python with BiDi
Selenium’s Python BiDi emulation API exposes set_timezone_override for selected browsing contexts. The context must be created and BiDi enabled according to the Selenium version and browser driver in use; the exact setup varies across bindings and versions. Apply the override to the relevant context before navigating and capture with the WebDriver screenshot API:
# BiDi setup and context creation depend on the installed Selenium version.
# Once `driver` and the target browsing context are available:
await driver.bidi_connection.session.execute(
"emulation.setTimezoneOverride",
{"timezone": "Europe/Paris", "contexts": [context_id]},
)
driver.get("http://127.0.0.1:3000/schedule")
driver.save_screenshot("schedule-paris.png")
Verify the BiDi command shape against your installed Python binding and browser. Selenium documents its Python method and supported timezone forms in the BiDi emulation API.
5. Troubleshoot mismatches and capture failures
| Symptom | Likely cause | Fix |
|---|---|---|
| The browser shows the expected time, but Node assertions or test logs use another timezone | Browser emulation does not change the test runner timezone. | Set the runner process timezone separately when needed, and keep browser configuration explicit. |
| The clock is right but the date or text format differs | Locale differs, or the test data is generated at runtime. | Pin locale and seed or mock the date and API data. |
| The screenshot is consistently one day ahead or behind | UTC timestamps are converted using a different zone than expected, or server-side data uses another timezone. | Check the timestamp, browser zone, backend zone, and formatting code separately. Do not assume browser emulation configures the server. |
| Only some runs have missing images or content | Capture occurs before image loading, especially for lazy-loaded content. | Wait for the target content and image state, trigger lazy loading, then capture. |
| Many unrelated pixels differ between machines | Browser, operating system, fonts, headless mode, or other rendering settings differ from the baseline environment. | Run baseline generation and comparison in a consistent environment and inspect the diff before updating. |
| Timezone setup fails or is ignored | The identifier, framework version, browser, or Selenium BiDi context setup is unsupported or incorrect. | Use a supported named zone, apply it to the correct page/context before capture, and check the installed API documentation. |
| A screenshot assertion fails after a product change | The UI may have changed, or the capture state may be unstable. | Compare expected and actual images, verify inputs and environment, then update the baseline only if the change is intended. |
6. Performance, reliability, and cost
Visual tests spend time launching browsers, navigating, waiting for stable content, and writing or comparing images. Reuse the test runner’s normal worker and browser configuration, avoid waiting for a global network-idle condition when a specific UI signal is more reliable, and capture only the page or component needed. Full-page screenshots and large pages with many images require more rendering and image data than a small component capture.
Reliability comes from controlling the complete input set: timezone, locale, date and API data, viewport, browser, and capture state. A timezone alone cannot make a screenshot deterministic. Keep baselines under version control and review each intentional change. There are no universal accuracy or flakiness figures for this workflow; results depend on the application and rendering environment.
Local browser capture uses your own development and CI resources. Hosted capture can reduce browser setup work, while usage-based service pricing depends on the chosen plan. For an API option with timezone configuration, see ScreenshotNeo and its API documentation.
Or skip the browser setup
ScreenshotNeo can take a website screenshot with a single API request. For a Paris-localized page, pass its timezone and locale options as documented:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-d timezone=Europe/Paris \
-d locale=en-GB \
-o shot.webp
See the ScreenshotNeo API docs for authentication and supported parameter names. The equivalent request patterns in Python and Node.js are:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://stripe.com",
"timezone": "Europe/Paris",
"locale": "en-GB",
},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com',
timezone: 'Europe/Paris',
locale: 'en-GB',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; those steps can be turned off. Bot checks, blank pages, and failed loads are never billed, and response headers report the page verdict and billing status. Its MCP server lets AI agents use screenshot tools. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
FAQ
Does changing the browser timezone change the machine clock?
No. It emulates timezone behavior in the browser context. It does not change the host system clock, test runner, or backend.
Should I use a UTC offset or a named timezone?
Use a named region such as Europe/Paris when the scenario represents a place and its daylight-saving rules. Use a fixed offset only when the test specifically requires a fixed offset and the framework supports it.
Can one test compare several timezones?
Yes. Run separate tests or projects with separate context settings and distinct baseline names, such as schedule-paris.png and schedule-tokyo.png.
Will a correct timezone guarantee identical screenshots across machines?
No. Browser and operating-system rendering, fonts, page data, viewport, and capture timing also affect pixels. Keep the environment and page state consistent.


