You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
dayjs.tz(ms, 'UTC').startOf('day') returns the start of the previous day in UTC instead of the correct day when the host's local timezone has a DST transition on the date being processed.
The Dayjs returned by dayjs.tz(ms, 'UTC') is supposed to be pinned to UTC for chained operations, but startOf('day') appears to fall back to host-local-time semantics. Using the UTC plugin directly (dayjs.utc(ms)) produces the correct result.
Reproducing test case
Run with the host's TZ set to a zone that has a DST transition on 2024-11-03 (e.g. TZ=America/Los_Angeles):
constdayjs=require('dayjs');constutc=require('dayjs/plugin/utc');consttimezone=require('dayjs/plugin/timezone');dayjs.extend(utc);dayjs.extend(timezone);// 2024-11-03 23:00 UTC. Local TZ America/Los_Angeles ends DST at// 02:00 PDT on this date (PDT -> PST), so 2024-11-03 straddles a// DST transition in local time.constms=newDate('2024-11-03T23:00:00Z').getTime();constbuggy=dayjs.tz(ms,'UTC').startOf('day').valueOf();constcorrect=dayjs.utc(ms).startOf('day').valueOf();constexpected=newDate('2024-11-03T00:00:00Z').getTime();console.log('buggy ->',newDate(buggy).toISOString());console.log('correct ->',newDate(correct).toISOString());console.log('expected->',newDate(expected).toISOString());// Expected output:// buggy -> 2024-11-03T00:00:00.000Z// correct -> 2024-11-03T00:00:00.000Z// expected-> 2024-11-03T00:00:00.000Z//// Actual output (on a host with US DST on 2024-11-03):// buggy -> 2024-11-02T23:00:00.000Z <-- off by one hour// correct -> 2024-11-03T00:00:00.000Z// expected-> 2024-11-03T00:00:00.000Z
Expected behavior
dayjs.tz(ms, 'UTC').startOf('day').valueOf() should equal dayjs.utc(ms).startOf('day').valueOf() — i.e., the start of the UTC day containing ms. The choice of host-local timezone should not affect the result when the chained timezone is explicitly UTC.
Additional notes
The bug only reproduces when the host's local timezone has a DST transition on the date under test. UTC hosts and non-DST hosts hide it.
Other IANA-named zones (US/Pacific, US/Eastern, Europe/Berlin, etc.) work correctly even on DST host dates — sweeping a fix-test matrix across US/Pacific, US/Eastern, US/Hawaii, and UTC on 2024-11-03, only the UTC case fails.
Symptom in real code: a day-by-day boundary walk where each iteration computes dayjs.tz(watermark, 'UTC').startOf('day').add(1, 'day') gets pinned at a single moment, because startOf('day') returns the prior day's midnight and +1 day lands exactly on watermark.
The .startOf('millisecond') workaround that fixes Inconsistent results on 25 hour day after time change #2957 does not fix this bug — confirmed by direct probing. The two issues share a "chained operations don't respect .tz()" root cause but they hit different internal code paths.
Information
Day.js Version: 1.11.21
OS: macOS 25.4.0
Browser: Node 24.16.0
Time zone: America/Los_Angeles (host); requested zone in the buggy call: UTC
Describe the bug
dayjs.tz(ms, 'UTC').startOf('day')returns the start of the previous day in UTC instead of the correct day when the host's local timezone has a DST transition on the date being processed.The Dayjs returned by
dayjs.tz(ms, 'UTC')is supposed to be pinned to UTC for chained operations, butstartOf('day')appears to fall back to host-local-time semantics. Using the UTC plugin directly (dayjs.utc(ms)) produces the correct result.Reproducing test case
Run with the host's
TZset to a zone that has a DST transition on 2024-11-03 (e.g.TZ=America/Los_Angeles):Expected behavior
dayjs.tz(ms, 'UTC').startOf('day').valueOf()should equaldayjs.utc(ms).startOf('day').valueOf()— i.e., the start of the UTC day containingms. The choice of host-local timezone should not affect the result when the chained timezone is explicitly UTC.Additional notes
US/Pacific,US/Eastern,Europe/Berlin, etc.) work correctly even on DST host dates — sweeping a fix-test matrix acrossUS/Pacific,US/Eastern,US/Hawaii, andUTCon 2024-11-03, only the UTC case fails.dayjs.tz(watermark, 'UTC').startOf('day').add(1, 'day')gets pinned at a single moment, becausestartOf('day')returns the prior day's midnight and+1 daylands exactly onwatermark..startOf('millisecond')workaround that fixes Inconsistent results on 25 hour day after time change #2957 does not fix this bug — confirmed by direct probing. The two issues share a "chained operations don't respect.tz()" root cause but they hit different internal code paths.Information
America/Los_Angeles(host); requested zone in the buggy call:UTC