Matched signals
- expected.*UTC
- got.*local time
- timezone.*mismatch
- assertion.*datetime
- Expected.*+0000
- received.*+0
- wrong timezone
- AssertionError.*datetime
Timezone differences causing test failures
What this failure means
A test that compares date or time values fails because the CI runner operates
in a different timezone than the development machine. Code that implicitly
calls datetime.now(), new Date(), or similar without a timezone argument
returns local time, which differs across environments.
Symptoms
Faultline looks for one or more of these log fragments:
expected.*UTC
got.*local time
timezone.*mismatch
assertion.*datetime
Expected.*+0000
received.*+0
wrong timezone
AssertionError.*datetime
Diagnosis
CI runners commonly default to UTC or a cloud region timezone. Developer machines use local timezones. Tests that construct expected values with date helpers or hardcode timestamps without UTC anchoring will produce different results on each environment.
Common patterns that cause timezone failures:
datetime.now()ornew Date()withouttimezone.utc/Zsuffix- Date formatting that produces locale-dependent output
- Database timestamps stored and retrieved without explicit timezone handling
- Assertions comparing
strftimeoutput that includes timezone offset
Check the runner timezone:
date
echo $TZ
timedatectl show --property=Timezone --value # Linux systemd
Fix steps
-
Force the CI environment to UTC:
# GitHub Actions env: TZ: UTC # GitLab CI variables: TZ: UTC -
In application code, always use UTC-aware datetime objects and convert to local time only at the presentation layer:
# Python — always UTC from datetime import datetime, timezone now = datetime.now(tz=timezone.utc)// JavaScript — use ISO strings or UTC methods const now = new Date().toISOString(); // "2025-01-01T12:00:00.000Z" const timestamp = Date.UTC(2025, 0, 1, 12, 0, 0); // explicit UTC epoch -
In tests, freeze or mock the clock with a fixed UTC time rather than calling the real system clock:
# Python with freezegun from freezegun import freeze_time @freeze_time("2025-01-01 12:00:00") def test_something(): ... # pytest-mock mocker.patch("mymodule.datetime").now.return_value = fixed_utc_dt// Jest jest.useFakeTimers().setSystemTime(new Date("2025-01-01T12:00:00Z")); -
Store and compare all database timestamps in UTC; use
TIMESTAMPTZin PostgreSQL and UTC-mode drivers in MySQL. -
If tests must exercise timezone conversions, parameterize the timezone explicitly rather than relying on
TZenvironment state.
Validation
- Set
TZ=UTClocally and re-run the failing tests to reproduce them. - After applying fixes, run the suite with several different
TZvalues:TZ=America/New_York,TZ=Asia/Kolkata,TZ=Australia/Sydney. - Confirm all assertions pass regardless of system timezone.
Why it matters
Timezone bugs often go unnoticed locally and surface only in CI or production in cloud regions different from the development team’s location. Daylight saving time transitions can additionally break tests that pass most of the year but fail twice annually.
Prevention
- Standardize on UTC throughout the stack and only convert to local time in templates or UI code.
- Set
TZ=UTCin CI environment configuration. - Use a clock-mocking library in tests instead of calling the system clock directly.
- Run the test suite locally with
TZ=UTCas part of pre-commit or local CI enforcement.
How Faultline detects it
Use faultline explain timezone-diff to see the full playbook.
faultline analyze build.log
faultline explain timezone-diff
Generated from playbooks/bundled/log/test/timezone-diff.yaml. Do not edit directly.