---
title: "Timezones and DST — Opening Hours for Laravel | Roundly"
description: "How a calendar’s timezone is resolved, how answers are reported in any zone, and the one explicit rule for clock changes."
url: https://roundly-consulting.com/open-source/docs/opening-hours-for-laravel/timezones-and-dst
language: en
---

[All packages](https://roundly-consulting.com/open-source.md)

[Opening Hours for Laravel](https://roundly-consulting.com/open-source/docs/opening-hours-for-laravel.md)

# Timezones and DST

A calendar is evaluated in its own timezone → the owner’s openingHoursTimezone() → opening-hours.timezone → app.timezone. Only IANA names are accepted; fixed offsets such as +02:00 are rejected with InvalidTimezoneException. withOutputTimezone() changes only how instants are reported, never the answer:

```php
$hours = OpeningHours::for($clinic);   // or $clinic->openingHours()

$hours->timezone();                                // the zone the calendar is evaluated in
$hours->withOutputTimezone('UTC')->nextClose();    // same instant, reported in UTC

$visitor = $hours->withOutputTimezone('America/New_York');
$visitor->nextOpen();                              // reported in New York time
$visitor->outputTimezone();                        // DateTimeZone('America/New_York')
```

## The boundary rule

Every wall-clock boundary follows one rule: it resolves to the first instant at which the local clock reads that time or later.

- Spring forward (02:30 does not exist): a boundary at 02:30 is the moment the clock jumps (03:00). A 01:00-02:30 range lasts one real hour that day.
- Fall back (02:30 happens twice): the first occurrence — for starts and ends, in every zone.
- A 00:00-24:00 day is 23 or 25 hours long on transition days; durations are real time.
- Dates given as instants are converted to the calendar timezone first; the weekday is the local one.

```php
use Carbon\CarbonImmutable;
use RoundlyConsulting\OpeningHours\OpeningHours;

// Europe/Bratislava falls back on Sunday 2026-10-25 (03:00 → 02:00)
$sundays = OpeningHours::make(['week' => ['sunday' => ['00:00-24:00']]], 'Europe/Bratislava');
$sunday = CarbonImmutable::parse('2026-10-25 00:00', 'Europe/Bratislava');
$sundays->openSecondsBetween($sunday, $sunday->addDay());   // 90000 — 25 real hours

// … and springs forward on Sunday 2026-03-29 (02:00 → 03:00)
$gap = OpeningHours::make(['week' => ['sunday' => ['01:00-02:30']]], 'Europe/Bratislava');
$period = $gap->currentPeriod(CarbonImmutable::parse('2026-03-29 01:30', 'Europe/Bratislava'));
$period?->end->format('c');                                 // 2026-03-29T03:00:00+02:00 — one real hour
```

PHP itself resolves ambiguous times to different occurrences in different zones, so the engine computes boundaries from each zone’s transition table instead. It is checked against an independent brute-force oracle across real transitions — including a 30-minute shift (Australia/Lord\_Howe), a +05:45 offset (Asia/Kathmandu), a midnight transition (America/Santiago) and a skipped day (Pacific/Apia).

## Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

[More ways to support, including crypto](https://roundly-consulting.com/support-us.md)

By donating, you agree to our [donation terms](https://roundly-consulting.com/donation-terms.md).

[Support our open source work (opens in a new tab)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Join us on Patreon (opens in a new tab)](https://www.patreon.com/cw/roundly)

## Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.

[Get a quote in 48 hours](https://roundly-consulting.com/contact.md) [Browse all packages](https://roundly-consulting.com/open-source/docs/opening-hours-for-laravel.md)
