Signature
timezone_transitions_get(
DateTimeZone $object,
int $timestampBegin = PHP_INT_MIN,
int $timestampEnd = PHP_INT_MAX
): array|falsePurpose
timezone_transitions_get() is the procedural alias of DateTimeZone::getTransitions(). It reports when a time zone changed its UTC offset, for example when daylight saving time (DST) began or ended. You can use it to explain why a local day is 23 or 25 hours long, to find the next clock change, or to check that two zones behave the same over a period.
Both spellings do the same thing. Use the function form in procedural code and the method form in object-oriented code.
Signature
Parameters and defaults
$objectis theDateTimeZoneto inspect. It is required. Pass an object, not a string such as"Europe/Paris".$timestampBeginis the Unix timestamp where the range starts. The default isPHP_INT_MIN, which means "from the earliest data available".$timestampEndis the Unix timestamp where the range ends. The default isPHP_INT_MAX.
The defaults can produce a long list, because it covers the whole history stored in the timezone database. In most real code you should pass both bounds.
Return value and failure
On success the function returns a list of arrays. Each entry has these keys:
ts: the Unix timestamp of the transition.time: the same moment as an ISO 8601 string in UTC, such as2024-03-10T07:00:00+00:00.offset: the offset from UTC in seconds, for example-18000for UTC-5.isdst: a boolean that istruewhile daylight saving time is in effect.abbr: the abbreviation, such asESTorEDT.
The first element describes the zone state at the start of the range you asked for. Later elements are the actual changes inside the range. The signature allows false on failure, so production code that cannot tolerate a missing result should check for it. Passing something that is not a DateTimeZone is a type error, not a false return.
Example 1: transitions in one year
New York changed its offset twice in 2024. The first entry below is the starting state at the beginning of the range, and the next two are the real changes.
date_default_timezone_set('UTC');
$zone = new DateTimeZone('America/New_York');
$start = 1704067200; // 2024-01-01T00:00:00Z
$end = 1735689599; // 2024-12-31T23:59:59Z
$transitions = timezone_transitions_get($zone, $start, $end);
foreach ($transitions as $transition) {
echo json_encode($transition, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
}{"ts":1704067200,"time":"2024-01-01T00:00:00+00:00","offset":-18000,"isdst":false,"abbr":"EST"}
{"ts":1710054000,"time":"2024-03-10T07:00:00+00:00","offset":-14400,"isdst":true,"abbr":"EDT"}
{"ts":1730613600,"time":"2024-11-03T06:00:00+00:00","offset":-18000,"isdst":false,"abbr":"EST"}
Example 2: alias equality and a DST summary
The function and the method return identical data. This example also turns the result into a short list of the dates when the clocks changed, skipping the first entry because it is only the start of the range.
date_default_timezone_set('UTC');
$zone = new DateTimeZone('America/New_York');
$start = 1704067200;
$end = 1735689599;
$fromFunction = timezone_transitions_get($zone, $start, $end);
$fromMethod = $zone->getTransitions($start, $end);
$changes = [];
foreach (array_slice($fromFunction, 1) as $transition) {
$changes[] = substr($transition['time'], 0, 10) . ' ' . $transition['abbr'];
}
echo json_encode(['same' => $fromFunction === $fromMethod, 'changes' => $changes], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"same":true,"changes":["2024-03-10 EDT","2024-11-03 EST"]}
Example 3: a zone without changes
UTC never changes its offset. A bounded query therefore returns only the starting entry.
date_default_timezone_set('UTC');
$zone = new DateTimeZone('UTC');
$transitions = timezone_transitions_get($zone, 0, 100000);
echo json_encode(['count' => count($transitions), 'offset' => $transitions[0]['offset']], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"count":1,"offset":0}
Edge cases
- The first element is not a real change. It is a snapshot of the zone at
$timestampBegin. Count it separately from actual transitions. - Boundaries matter. A change that happens exactly at your bounds may be included or treated as the starting state, so test boundary timestamps in your own code.
- Fixed-offset zones have no real changes, so you typically get a single entry.
- Offsets are seconds. Divide by 3600 for hours, and remember that some zones use half-hour or 45-minute offsets.
- Data comes from the timezone database bundled with PHP, so results for future dates depend on the rules known to that version and can change after an update.
Common mistakes
- Calling it without bounds and then looping over a very large history when only one year was needed.
- Passing a timezone name string instead of a
DateTimeZoneobject. - Treating the first entry as a DST change and reporting a clock change that did not happen.
- Assuming
isdstmeans the offset is positive. It only says whether the zone is on its daylight rule. - Parsing
timeas a local time. It is expressed in UTC. - Hard-coding transition dates instead of reading them from the zone data.
Related functions
DateTimeZone::getTransitions(), the method this function aliases.timezone_offset_get()andDateTimeZone::getOffset()for the offset at a single moment.timezone_open()for creating aDateTimeZoneprocedurally.timezone_name_get()for reading the zone name.DateTimeImmutable::setTimezone()for converting a moment to another zone.
Recap
timezone_transitions_get() returns the offset history of a DateTimeZone between two timestamps. Always bound the range, remember that the first entry is the starting state, read offsets as seconds, and treat time as UTC.
Tested examples
date_default_timezone_set('UTC');
$zone = new DateTimeZone('America/New_York');
$start = 1704067200; // 2024-01-01T00:00:00Z
$end = 1735689599; // 2024-12-31T23:59:59Z
$transitions = timezone_transitions_get($zone, $start, $end);
foreach ($transitions as $transition) {
echo json_encode($transition, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
}
{"ts":1704067200,"time":"2024-01-01T00:00:00+00:00","offset":-18000,"isdst":false,"abbr":"EST"}
{"ts":1710054000,"time":"2024-03-10T07:00:00+00:00","offset":-14400,"isdst":true,"abbr":"EDT"}
{"ts":1730613600,"time":"2024-11-03T06:00:00+00:00","offset":-18000,"isdst":false,"abbr":"EST"}
date_default_timezone_set('UTC');
$zone = new DateTimeZone('America/New_York');
$start = 1704067200;
$end = 1735689599;
$fromFunction = timezone_transitions_get($zone, $start, $end);
$fromMethod = $zone->getTransitions($start, $end);
$changes = [];
foreach (array_slice($fromFunction, 1) as $transition) {
$changes[] = substr($transition['time'], 0, 10) . ' ' . $transition['abbr'];
}
echo json_encode(['same' => $fromFunction === $fromMethod, 'changes' => $changes], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"same":true,"changes":["2024-03-10 EDT","2024-11-03 EST"]}
date_default_timezone_set('UTC');
$zone = new DateTimeZone('UTC');
$transitions = timezone_transitions_get($zone, 0, 100000);
echo json_encode(['count' => count($transitions), 'offset' => $transitions[0]['offset']], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"count":1,"offset":0}