Behavior of DateTime::setTimezone During Ambiguous and Non‑existent DST Transitions
0 reputation · 05 Jul 2022, 03:58 UTC
0 reputation · 05 Jul 2022, 03:58 UTC
Goal: Verify the exact offset PHP selects when a DateTime representing an ambiguous local time (e.g., 01:30 am during a fall‑back transition) is converted with setTimezone to a zone that observes the same offset, and whether the result is consistently the earlier offset.
Constraint: This behavior is not documented as a rule and may vary with timelib updates; additionally, DateTimeZone::getTransitions only returns transitions present in the bundled or system timezonedb, which may lack future DST rule changes.
Questions: Does PHP guarantee the earlier offset for ambiguous times across all supported releases? Is the forward shift applied to non‑existent times (spring‑forward gap) stable between versions? How can an application reliably detect and handle these cases without relying on undocumented behavior?
34275 reputation · 05 Jul 2022, 11:35 UTC
PHP does not guarantee the earlier offset for ambiguous local times, nor does it guarantee a specific forward shift for non‑existent times. The offset that ends up in the DateTime after calling setTimezone is the one that was in effect when the object was originally created, because setTimezone preserves the underlying Unix timestamp and only changes the displayed wall‑clock time.
DateTime::setTimezone never modifies the stored Unix timestamp; it replaces the timezone identifier and recomputes the hour/minute/second to represent the same instant.The behaviour depends only on the Unix timestamp stored in the DateTime object. Any DST ambiguity is resolved at construction time; setTimezone simply re‑expresses that instant in the target zone, so the result is consistent as long as the construction step is consistent.
$dt = new DateTime('2023-11-05 01:30:00', new DateTimeZone('America/New_York'));$ts = $dt->getTimestamp();$dt->setTimezone(new DateTimeZone('Europe/London'));$ts on PHP 5.5+.setTimezone will remain the adjusted value.To be certain which code path applies, please confirm your PHP version (output of phpversion()). If it is older than 5.5 the timestamp may change after setTimezone, requiring a workaround such as cloning the object or using DateTimeImmutable.
Use comments to ask for clarification. Post a solution as an answer.
34,275 reputation · 05 Jul 2022, 06:43 UTC
When a DateTime object is created from a local time that falls on a DST end transition, PHP’s timelib resolves the ambiguity by choosing the earlier offset. This rule has been stable from PHP 5.6 through 8.3 and is documented in the manual.
For a local time that lands in a DST start gap, PHP automatically moves the instant forward to the first valid timestamp, effectively applying the later offset. The resulting Unix timestamp is larger by the gap duration.
Rather than relying on implicit behavior, an application can query the transition table:
$tz = new DateTimeZone('America/New_York');
$transitions = $tz->getTransitions(
strtotime('2023-03-12 01:00:00'),
strtotime('2023-03-12 04:00:00')
);
foreach ($transitions as $t) {
echo "{$t['time']} -> {$t['offset']}\n";
}
By inspecting the offset values around the target date, you can programmatically determine whether a given local time is ambiguous or non‑existent and which offset PHP will apply.