NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky
Opening Hours for Laravel

Pracovné dni a hodiny

Termíny a SLA sa počítajú v otvorenom čase, nie v kalendárnom. addOpenDays() vráti rovnaký miestny čas v n-tý otvorený dátum po dátume začiatku („2 pracovné dni“: piatok 15:00 → utorok 15:00); addOpenTime() vráti okamih po uplynutí zadaného CarbonInterval otvoreného času („4 pracovné hodiny“). Obe preskočia zatvorené dni v týždni, sviatky, výnimky aj dynamické výnimky a obe vracajú CarbonImmutable vo výstupnom časovom pásme kalendára:

use Carbon\CarbonImmutable;
use Carbon\CarbonInterval;
use RoundlyConsulting\OpeningHours\Facades\OpeningHours;

OpeningHours::sync($office, [
    'timezone' => 'Europe/Bratislava',
    'week' => array_fill_keys(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], ['09:00-17:00']),
    'exceptions' => [['date' => '2026-11-09']],                                 // a holiday Monday
]);
OpeningHours::sync($office, ['timezone' => 'Europe/Bratislava', 'week' => ['saturday' => ['10:00-14:00']]], 'weekend');

$friday = CarbonImmutable::parse('2026-11-06 10:00', 'Europe/Bratislava');

// "N business days": the same local time on the n-th open date after the start's date
OpeningHours::addOpenDays($office, $friday, 2);                                // Wed 2026-11-11 10:00 — the holiday skipped
OpeningHours::addOpenDays($office, $friday, 1, 'weekend');                     // Sat 2026-11-07 10:00

// "N business hours": the instant that much open time later
OpeningHours::addOpenTime($office, $friday->setTime(16, 0), CarbonInterval::hours(2)); // Tue 2026-11-10 10:00
OpeningHours::addOpenTime($office, $friday, CarbonInterval::hours(5), 'weekend');      // Sat 2026-11-14 11:00

Všetky vstupné body

Fasáda aj injektovaný manažér prijímajú vlastníka a voliteľný kľúč kalendára; dotazovací objekt, trait vlastníka aj akcie spúšťajú ten istý kód — ak hostiteľ v kontajneri previaže AddOpenDaysAction alebo AddOpenTimeAction, platí to za všetkými z nich:

use Carbon\CarbonInterval;
use RoundlyConsulting\OpeningHours\Actions\AddOpenTimeAction;
use RoundlyConsulting\OpeningHours\Facades\OpeningHours;
use RoundlyConsulting\OpeningHours\OpeningHoursManager;

// The query object and the owner trait — shorthand over the same manager:
OpeningHours::for($office)->addOpenDays($friday, 2);                           // Wed 10:00
$office->addOpenDays($friday, 2);                                              // Wed 10:00
$office->addOpenTime($friday, CarbonInterval::hours(8));                       // Tue 10:00
$office->addOpenTime($friday, CarbonInterval::hour(), 'weekend');              // Sat 11:00

// The injected manager and the raw action:
app(OpeningHoursManager::class)->addOpenDays($office, $friday, 2);             // Wed 10:00
app(AddOpenTimeAction::class)->execute(OpeningHours::for($office), $friday, CarbonInterval::hours(8)); // Tue 10:00

Ako počítajú

  • Otvorený dátum je taký, v ktorý začína nejaký rozsah — rovnaké pravidlo ako pri isOpenOnDate() — s uplatnenými výnimkami aj dynamickými výnimkami, takže sviatok sa preskočí a výnimka, ktorá otvorí inak zatvorený deň, sa započíta.
  • addOpenDays() počíta otvorené dátumy striktne po miestnom dátume začiatku a zachová čas začiatku podľa miestnych hodín, aj keď je v tom čase zatvorené.
  • addOpenTime() spotrebúva len otvorený čas. Obdobia sú polootvorené, takže začiatok presne v čase zatvorenia sa počíta ako zatvorené (pondelok 17:00 + 30 min → utorok 09:30) a nočný rozsah sa spotrebuje aj cez polnoc.
  • addOpenTime() počíta celé sekundy intervalu — zlomok sekundy sa zahodí.
  • Začiatok môže byť ľubovoľný DateTimeInterface a číta sa v časovom pásme kalendára: piatok 23:30 v New Yorku je v Bratislave už sobota.
  • Nula alebo záporná hodnota vráti začiatok bez zmeny, vo výstupnom časovom pásme.
use Carbon\CarbonImmutable;
use Carbon\CarbonInterval;
use RoundlyConsulting\OpeningHours\OpeningHours;

$hours = OpeningHours::make([
    'week' => array_fill_keys(['monday', 'tuesday', 'wednesday', 'thursday', 'friday'], ['09:00-17:00']),
], 'UTC');

$hours->addOpenTime(CarbonImmutable::parse('2026-11-06 16:00', 'UTC'), CarbonInterval::hours(2));    // Mon 10:00
$hours->addOpenTime(CarbonImmutable::parse('2026-11-09 17:00', 'UTC'), CarbonInterval::minutes(30)); // Tue 09:30 — 17:00 is already closed
$hours->addOpenDays(CarbonImmutable::parse('2026-11-07 10:00', 'UTC'), 2);                           // Tue 10:00 — Saturday's time kept
$hours->addOpenDays(CarbonImmutable::parse('2026-11-07 10:00', 'UTC'), 0);                           // Sat 10:00 — zero or less returns the start

Zmena času

addOpenDays() zachová v cieľový dátum čas začiatku podľa miestnych hodín. Čas, ktorý nastane dvakrát, berie prvý výskyt, ako všade v engine, no čas v medzere pri prechode na letný čas sa posunie dopredu o dĺžku medzery (02:30 → 03:30, tak ako ho rieši PHP), nie na okamih prechodu — termín si tak zachová odstup od začiatku. addOpenTime() počíta skutočne uplynutý otvorený čas, takže 25-hodinový deň pri prechode na zimný čas obsahuje 25 otvorených hodín:

use Carbon\CarbonImmutable;
use Carbon\CarbonInterval;
use RoundlyConsulting\OpeningHours\OpeningHours;

// Europe/Bratislava springs forward on Sunday 2026-03-29 and falls back on Sunday 2026-10-25
$sundays = OpeningHours::make(['week' => ['sunday' => ['00:00-24:00']]], 'Europe/Bratislava');

$sundays->addOpenDays(CarbonImmutable::parse('2026-03-22 02:30', 'Europe/Bratislava'), 1);
// 2026-03-29 03:30 — 02:30 does not exist that day, so it moves forward by the gap

$sundays->addOpenDays(CarbonImmutable::parse('2026-10-18 02:30', 'Europe/Bratislava'), 1);
// 2026-10-25 02:30 +02:00 — 02:30 happens twice; the first occurrence

$sundays->addOpenTime(CarbonImmutable::parse('2026-10-25 00:00', 'Europe/Bratislava'), CarbonInterval::hours(24));
// 2026-10-25 23:00 — the day holds 25 real open hours

Okno hľadania

Ani jedna metóda nehľadá donekonečna. Odpoveď musí padnúť do opening-hours.search_days miestnych dní od dátumu začiatku — celé obdobie, nie medzera medzi otvorenými dňami. Kalendár, ktorý nie je nikdy otvorený, vlastník bez kalendára alebo obdobie dlhšie než okno vyhodí OpenTimeNotFoundException s hodnotou searchDays:

use RoundlyConsulting\OpeningHours\Exceptions\OpenTimeNotFoundException;
use RoundlyConsulting\OpeningHours\Facades\OpeningHours;

try {
    $due = OpeningHours::addOpenDays($office, now(), 2);
} catch (OpenTimeNotFoundException $e) {
    // The calendar has not enough open time within 366 days of the start (opening-hours.search_days).
    // $e->searchDays === 366
}

Pod OpeningHours::fake() obe metódy naďalej odpovedajú z databázy a nič nezaznamenajú — ide o čítanie.

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.