{"id":13579117,"url":"https://github.com/spatie/opening-hours","last_synced_at":"2025-05-12T15:33:06.314Z","repository":{"id":37270509,"uuid":"69368104","full_name":"spatie/opening-hours","owner":"spatie","description":"Query and format a set of opening hours","archived":false,"fork":false,"pushed_at":"2025-04-22T07:32:07.000Z","size":490,"stargazers_count":1671,"open_issues_count":0,"forks_count":117,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-04-23T17:47:20.131Z","etag":null,"topics":["calendar","opening-hours","php","schedule","shop"],"latest_commit_sha":null,"homepage":"https://freek.dev/595-managing-opening-hours-with-php","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/spatie.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2016-09-27T14:57:04.000Z","updated_at":"2025-04-22T07:32:11.000Z","dependencies_parsed_at":"2023-01-30T21:15:50.247Z","dependency_job_id":"e6836348-7e58-4092-ba9d-996d212fda46","html_url":"https://github.com/spatie/opening-hours","commit_stats":{"total_commits":195,"total_committers":33,"mean_commits":5.909090909090909,"dds":0.6717948717948719,"last_synced_commit":"a6b937b56d1561cdd9ebfc329bcfd8541ab7ab16"},"previous_names":[],"tags_count":60,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spatie%2Fopening-hours","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spatie%2Fopening-hours/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spatie%2Fopening-hours/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/spatie%2Fopening-hours/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/spatie","download_url":"https://codeload.github.com/spatie/opening-hours/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253766117,"owners_count":21960847,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["calendar","opening-hours","php","schedule","shop"],"created_at":"2024-08-01T15:01:36.698Z","updated_at":"2025-05-12T15:33:06.260Z","avatar_url":"https://github.com/spatie.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"\u003cdiv align=\"left\"\u003e\n    \u003ca href=\"https://spatie.be/open-source?utm_source=github\u0026utm_medium=banner\u0026utm_campaign=opening-hours\"\u003e\n      \u003cpicture\u003e\n        \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"https://spatie.be/packages/header/opening-hours/html/dark.webp\"\u003e\n        \u003cimg alt=\"Logo for opening-hours\" src=\"https://spatie.be/packages/header/opening-hours/html/light.webp\" height=\"190\"\u003e\n      \u003c/picture\u003e\n    \u003c/a\u003e\n\n\u003ch1\u003eA helper to query and format a set of opening hours\u003c/h1\u003e\n\n[![Latest Version on Packagist](https://img.shields.io/packagist/v/spatie/opening-hours.svg?style=flat-square)](https://packagist.org/packages/spatie/opening-hours)\n[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)\n[![Tests](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fspatie%2Fopening-hours%2Fbadge\u0026style=flat-square\u0026label=Build\u0026logo=none)](https://actions-badge.atrox.dev/spatie/opening-hours/goto)\n[![Coverage](https://img.shields.io/codecov/c/github/spatie/opening-hours.svg?style=flat-square)](https://codecov.io/github/spatie/opening-hours?branch=master)\n[![Quality Score](https://img.shields.io/scrutinizer/g/spatie/opening-hours.svg?style=flat-square)](https://scrutinizer-ci.com/g/spatie/opening-hours)\n[![StyleCI](https://styleci.io/repos/69368104/shield?branch=master)](https://styleci.io/repos/69368104)\n[![Total Downloads](https://img.shields.io/packagist/dt/spatie/opening-hours.svg?style=flat-square)](https://packagist.org/packages/spatie/opening-hours)\n    \n\u003c/div\u003e\n\nWith `spatie/opening-hours` you create an object that describes a business' opening hours, which you can query for `open` or `closed` on days or specific dates, or use to present the times per day.\n\n`spatie/opening-hours` can be used directly on [Carbon](https://carbon.nesbot.com/) thanks\nto [cmixin/business-time](https://github.com/kylekatarnls/business-time) so you can benefit\nopening hours features directly on your enhanced date objects.\n\nA set of opening hours is created by passing in a regular schedule, and a list of exceptions.\n\n```php\n// Add the use at the top of each file where you want to use the OpeningHours class:\nuse Spatie\\OpeningHours\\OpeningHours;\n\n$openingHours = OpeningHours::create([\n    'monday'     =\u003e ['09:00-12:00', '13:00-18:00'],\n    'tuesday'    =\u003e ['09:00-12:00', '13:00-18:00'],\n    'wednesday'  =\u003e ['09:00-12:00'],\n    'thursday'   =\u003e ['09:00-12:00', '13:00-18:00'],\n    'friday'     =\u003e ['09:00-12:00', '13:00-20:00'],\n    'saturday'   =\u003e ['09:00-12:00', '13:00-16:00'],\n    'sunday'     =\u003e [],\n    'exceptions' =\u003e [\n        '2016-11-11' =\u003e ['09:00-12:00'],\n        '2016-12-25' =\u003e [],\n        '01-01'      =\u003e [],                // Recurring on each 1st of January\n        '12-25'      =\u003e ['09:00-12:00'],   // Recurring on each 25th of December\n    ],\n]);\n\n// This will allow you to display things like:\n\n$now = new DateTime('now');\n$range = $openingHours-\u003ecurrentOpenRange($now);\n\nif ($range) {\n    echo \"It's open since \".$range-\u003estart().\"\\n\";\n    echo \"It will close at \".$range-\u003eend().\"\\n\";\n} else {\n    echo \"It's closed since \".$openingHours-\u003epreviousClose($now)-\u003eformat('l H:i').\"\\n\";\n    echo \"It will re-open at \".$openingHours-\u003enextOpen($now)-\u003eformat('l H:i').\"\\n\";\n}\n```\n\nThe object can be queried for a day in the week, which will return a result based on the regular schedule:\n\n```php\n// Open on Mondays:\n$openingHours-\u003eisOpenOn('monday'); // true\n\n// Closed on Sundays:\n$openingHours-\u003eisOpenOn('sunday'); // false\n```\n\nIt can also be queried for a specific date and time:\n\n```php\n// Closed because it's after hours:\n$openingHours-\u003eisOpenAt(new DateTime('2016-09-26 19:00:00')); // false\n\n// Closed because Christmas was set as an exception\n$openingHours-\u003eisOpenOn('2016-12-25'); // false\n```\n\nIt can also return arrays of opening hours for a week or a day:\n\n```php\n// OpeningHoursForDay object for the regular schedule\n$openingHours-\u003eforDay('monday');\n\n// OpeningHoursForDay[] for the regular schedule, keyed by day name\n$openingHours-\u003eforWeek();\n\n// Array of day with same schedule for the regular schedule, keyed by day name, days combined by working hours\n$openingHours-\u003eforWeekCombined();\n\n// OpeningHoursForDay object for a specific day\n$openingHours-\u003eforDate(new DateTime('2016-12-25'));\n\n// OpeningHoursForDay[] of all exceptions, keyed by date\n$openingHours-\u003eexceptions();\n```\n\nOn construction, you can set a flag for overflowing times across days. For example, for a nightclub opens until 3am on Friday and Saturday:\n\n```php\n$openingHours = \\Spatie\\OpeningHours\\OpeningHours::create([\n    'overflow' =\u003e true,\n    'friday'   =\u003e ['20:00-03:00'],\n    'saturday' =\u003e ['20:00-03:00'],\n], null);\n```\n\nThis allows the API to further at previous day's data to check if the opening hours are open from its time range. \n\nYou can add data in definitions then retrieve them:\n\n```php\n$openingHours = OpeningHours::create([\n    'monday' =\u003e [\n        'data' =\u003e 'Typical Monday',\n        '09:00-12:00',\n        '13:00-18:00',\n    ],\n    'tuesday' =\u003e [\n        '09:00-12:00',\n        '13:00-18:00',\n        [\n            '19:00-21:00',\n            'data' =\u003e 'Extra on Tuesday evening',\n        ],\n    ],\n    'exceptions' =\u003e [\n        '2016-12-25' =\u003e [\n            'data' =\u003e 'Closed for Christmas',\n        ],\n    ],\n]);\n\necho $openingHours-\u003eforDay('monday')-\u003edata; // Typical Monday\necho $openingHours-\u003eforDate(new DateTime('2016-12-25'))-\u003edata; // Closed for Christmas\necho $openingHours-\u003eforDay('tuesday')[2]-\u003edata; // Extra on Tuesday evening\n```\n\nIn the example above, data are strings but it can be any kind of value. So you can embed multiple properties in an array.\n\nFor structure convenience, the data-hours couple can be a fully-associative array, so the example above is strictly equivalent to the following:\n\n```php\n$openingHours = OpeningHours::create([\n    'monday' =\u003e [\n        'hours' =\u003e [\n            '09:00-12:00',\n            '13:00-18:00',\n        ],\n        'data' =\u003e 'Typical Monday',\n    ],\n    'tuesday' =\u003e [\n        ['hours' =\u003e '09:00-12:00'],\n        ['hours' =\u003e '13:00-18:00'],\n        ['hours' =\u003e '19:00-21:00', 'data' =\u003e 'Extra on Tuesday evening'],\n    ],\n    // Open by night from Wednesday 22h to Thursday 7h:\n    'wednesday' =\u003e ['22:00-24:00'], // use the special \"24:00\" to reach midnight included\n    'thursday' =\u003e ['00:00-07:00'],\n    'exceptions' =\u003e [\n        '2016-12-25' =\u003e [\n            'hours' =\u003e [],\n            'data'  =\u003e 'Closed for Christmas',\n        ],\n    ],\n]);\n```\n\nYou can use the separator `to` to specify multiple days at once, for the week or for exceptions:\n\n```php\n$openingHours = OpeningHours::create([\n    'monday to friday' =\u003e ['09:00-19:00'],\n    'saturday to sunday' =\u003e [],\n    'exceptions' =\u003e [\n        // Every year\n        '12-24 to 12-26' =\u003e [\n            'hours' =\u003e [],\n            'data'  =\u003e 'Holidays',\n        ],\n        // Only happening in 2024\n        '2024-06-25 to 2024-07-01' =\u003e [\n            'hours' =\u003e [],\n            'data'  =\u003e 'Closed for works',\n        ],\n    ],\n]);\n```\n\nThe last structure tool is the filter, it allows you to pass closures (or callable function/method reference) that take a date as a parameter and returns the settings for the given date.\n\n```php\n$openingHours = OpeningHours::create([\n    'monday' =\u003e [\n       '09:00-12:00',\n    ],\n    'filters' =\u003e [\n        function ($date) {\n            $year         = intval($date-\u003eformat('Y'));\n            $easterMonday = new DateTimeImmutable('2018-03-21 +'.(easter_days($year) + 1).'days');\n            if ($date-\u003eformat('m-d') === $easterMonday-\u003eformat('m-d')) {\n                return []; // Closed on Easter Monday\n                // Any valid exception-array can be returned here (range of hours, with or without data)\n            }\n            // Else the filter does not apply to the given date\n        },\n    ],\n]);\n```\n\nIf a callable is found in the `\"exceptions\"` property, it will be added automatically to filters so you can mix filters and exceptions both in the **exceptions** array. The first filter that returns a non-null value will have precedence over the next filters and the **filters** array has precedence over the filters inside the **exceptions** array.\n\nWarning: We will loop on all filters for each date from which we need to retrieve opening hours and can neither predicate nor cache the result (can be a random function) so you must be careful with filters, too many filters or long process inside filters can have a significant impact on the performance.\n\nIt can also return the next open or close `DateTime` from a given `DateTime`.\n\n```php\n// The next open datetime is tomorrow morning, because we’re closed on 25th of December.\n$nextOpen = $openingHours-\u003enextOpen(new DateTime('2016-12-25 10:00:00')); // 2016-12-26 09:00:00\n\n// The next open datetime is this afternoon, after the lunch break.\n$nextOpen = $openingHours-\u003enextOpen(new DateTime('2016-12-24 11:00:00')); // 2016-12-24 13:00:00\n\n\n// The next close datetime is at noon.\n$nextClose = $openingHours-\u003enextClose(new DateTime('2016-12-24 10:00:00')); // 2016-12-24 12:00:00\n\n// The next close datetime is tomorrow at noon, because we’re closed on 25th of December.\n$nextClose = $openingHours-\u003enextClose(new DateTime('2016-12-25 15:00:00')); // 2016-12-26 12:00:00\n```\n\nRead the usage section for the full api.\n\nSpatie is a webdesign agency based in Antwerp, Belgium. You'll find an overview of all our open source projects [on our website](https://spatie.be/opensource).\n\n## Support us\n\n[\u003cimg src=\"https://github-ads.s3.eu-central-1.amazonaws.com/opening-hours.jpg?t=1\" width=\"419px\" /\u003e](https://spatie.be/github-ad-click/opening-hours)\n\nWe invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).\n\nWe highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).\n\n## Installation\n\nYou can install the package via composer:\n\n``` bash\ncomposer require spatie/opening-hours\n```\n\n## Usage\n\nThe package should only be used through the `OpeningHours` class. There are also three value object classes used throughout, `Time`, which represents a single time, `TimeRange`, which represents a period with a start and an end, and `openingHoursForDay`, which represents a set of `TimeRange`s which can't overlap.\n\n### `Spatie\\OpeningHours\\OpeningHours`\n\n#### `OpeningHours::create(array $data, $timezone = null, $toutputTimezone = null): Spatie\\OpeningHours\\OpeningHours`\n\nStatic factory method to fill the set of opening hours.\n\n```php\n$openingHours = OpeningHours::create([\n    'monday' =\u003e ['09:00-12:00', '13:00-18:00'],\n    // ...\n]);\n```\n\nIf no timezone is specified, `OpeningHours` will just assume you always\npass `DateTime` objects that have already the timezone matching your schedule.\n\nIf you pass a `$timezone` as a second argument or via the array-key `'timezone'`\n(it can be either a `DateTimeZone` object or a `string`), then passed dates will\nbe converted to this timezone at the beginning of each method, then if the method\nreturn a date object (such as `nextOpen`, `nextClose`, `previousOpen`,\n`previousClose`, `currentOpenRangeStart` or `currentOpenRangeEnd`), then it's\nconverted back to original timezone before output so the object can reflect\na moment in user local time while `OpeningHours` can stick in its own business\ntimezone.\n\nAlternatively you can also specify both input and output timezone (using second\nand third argument) or using an array:\n```php\n$openingHours = OpeningHours::create([\n    'monday' =\u003e ['09:00-12:00', '13:00-18:00'],\n    'timezone' =\u003e [\n        'input' =\u003e 'America/New_York',\n        'output' =\u003e 'Europe/Oslo',\n    ],\n]);\n```\n\n#### `OpeningHours::mergeOverlappingRanges(array $schedule) : array`\n\nFor safety sake, creating `OpeningHours` object with overlapping ranges will throw an exception unless you pass explicitly `'overflow' =\u003e true,` in the opening hours array definition. You can also explicitly merge them.\n\n```php\n$ranges = [\n  'monday' =\u003e ['08:00-11:00', '10:00-12:00'],\n];\n$mergedRanges = OpeningHours::mergeOverlappingRanges($ranges); // Monday becomes ['08:00-12:00']\n\nOpeningHours::create($mergedRanges);\n// Or use the following shortcut to create from ranges that possibly overlap:\nOpeningHours::createAndMergeOverlappingRanges($ranges);\n```\n\nNot all days are mandatory, if a day is missing, it will be set as closed.\n\n#### `OpeningHours::fill(array $data): Spatie\\OpeningHours\\OpeningHours`\n\nThe same as `create`, but non-static.\n\n```php\n$openingHours = (new OpeningHours)-\u003efill([\n    'monday' =\u003e ['09:00-12:00', '13:00-18:00'],\n    // ...\n]);\n```\n\n#### `OpeningHours::forWeek(): Spatie\\OpeningHours\\OpeningHoursForDay[]`\n\nReturns an array of `OpeningHoursForDay` objects for a regular week.\n\n```php\n$openingHours-\u003eforWeek();\n```\n\n#### `OpeningHours::forWeekCombined(): array`\n\nReturns an array of days. Array key is first day with same hours, array values are days that have the same working hours and `OpeningHoursForDay` object.\n\n```php\n$openingHours-\u003eforWeekCombined();\n```\n\n#### `OpeningHours::forWeekConsecutiveDays(): array`\n\nReturns an array of concatenated days, adjacent days with the same hours. Array key is first day with same hours, array values are days that have the same working hours and `OpeningHoursForDay` object.\n\n*Warning*: consecutive days are considered from Monday to Sunday without looping (Monday is not consecutive to Sunday) no matter the days order in initial data.\n\n```php\n$openingHours-\u003eforWeekConsecutiveDays();\n```\n\n#### `OpeningHours::forDay(string $day): Spatie\\OpeningHours\\OpeningHoursForDay`\n\nReturns an `OpeningHoursForDay` object for a regular day. A day is lowercase string of the english day name.\n\n```php\n$openingHours-\u003eforDay('monday');\n```\n\n#### `OpeningHours::forDate(DateTimeInterface $dateTime): Spatie\\OpeningHours\\OpeningHoursForDay`\n\nReturns an `OpeningHoursForDay` object for a specific date. It looks for an exception on that day, and otherwise it returns the opening hours based on the regular schedule.\n\n```php\n$openingHours-\u003eforDate(new DateTime('2016-12-25'));\n```\n\n#### `OpeningHours::exceptions(): Spatie\\OpeningHours\\OpeningHoursForDay[]`\n\nReturns an array of all `OpeningHoursForDay` objects for exceptions, keyed by a `Y-m-d` date string.\n\n```php\n$openingHours-\u003eexceptions();\n```\n\n#### `OpeningHours::isOpenOn(string $day): bool`\n\nChecks if the business is open (contains at least 1 range of open hours) on a day in the regular schedule.\n\n```php\n$openingHours-\u003eisOpenOn('saturday');\n```\n\nIf the given string is a date, it will check if it's open (contains at least 1 range of open hours) considering\nboth regular day schedule and possible exceptions.\n\n```php\n$openingHours-\u003eisOpenOn('2020-09-03');\n$openingHours-\u003eisOpenOn('09-03'); // If year is omitted, current year is used instead\n```\n\n#### `OpeningHours::isClosedOn(string $day): bool`\n\nChecks if the business is closed on a day in the regular schedule.\n\n```php\n$openingHours-\u003eisClosedOn('sunday');\n```\n\n#### `OpeningHours::isOpenAt(DateTimeInterface $dateTime): bool`\n\nChecks if the business is open on a specific day, at a specific time.\n\n```php\n$openingHours-\u003eisOpenAt(new DateTime('2016-26-09 20:00'));\n```\n\n#### `OpeningHours::isClosedAt(DateTimeInterface $dateTime): bool`\n\nChecks if the business is closed on a specific day, at a specific time.\n\n```php\n$openingHours-\u003eisClosedAt(new DateTime('2016-26-09 20:00'));\n```\n\n#### `OpeningHours::isOpen(): bool`\n\nChecks if the business is open right now.\n\n```php\n$openingHours-\u003eisOpen();\n```\n\n#### `OpeningHours::isClosed(): bool`\n\nChecks if the business is closed right now.\n\n```php\n$openingHours-\u003eisClosed();\n```\n\n#### `OpeningHours::isAlwaysOpen(): bool`\n\nChecks if the business is open 24/7, has no exceptions and no filters.\n\n```php\nif ($openingHours-\u003eisAlwaysOpen()) {\n    echo 'This business is open all day long every day.';\n}\n```\n\n#### `OpeningHours::isAlwaysClosed(): bool`\n\nChecks if the business is never open, has no exceptions and no filters.\n\n`OpeningHours` accept empty array or list with every week day empty with no prejudices.\n\nIf it's not a valid state in your domain, you should use this method to throw an exception\nor show an error.\n\n```php\nif ($openingHours-\u003eisAlwaysClosed()) {\n    throw new RuntimeException('Opening hours missing');\n}\n```\n\n#### `OpeningHours::nextOpen`\n\n```php\nOpeningHours::nextOpen(\n    ?DateTimeInterface $dateTime = null,\n    ?DateTimeInterface $searchUntil = null,\n    ?DateTimeInterface $cap = null,\n) : DateTimeInterface`\n```\n\nReturns next open `DateTime` from the given `DateTime` (`$dateTime` or from now if this parameter is null or omitted).\n\nIf a `DateTimeImmutable` object is passed, a `DateTimeImmutable` object is returned.\n\nSet `$searchUntil` to a date to throw an exception if no open time can be found before this moment.\n\nSet `$cap` to a date so if no open time can be found before this moment, `$cap` is returned.\n\n```php\n$openingHours-\u003enextOpen(new DateTime('2016-12-24 11:00:00'));\n```\n\n#### `OpeningHours::nextClose`\n\n```php\nOpeningHours::nextClose(\n    ?DateTimeInterface $dateTime = null,\n    ?DateTimeInterface $searchUntil = null,\n    ?DateTimeInterface $cap = null,\n) : DateTimeInterface`\n```\n\nReturns next close `DateTime` from the given `DateTime` (`$dateTime` or from now if this parameter is null or omitted).\n\nIf a `DateTimeImmutable` object is passed, a `DateTimeImmutable` object is returned.\n\nSet `$searchUntil` to a date to throw an exception if no closed time can be found before this moment.\n\nSet `$cap` to a date so if no closed time can be found before this moment, `$cap` is returned.\n\nIf the schedule is always open or always closed, there is no state change to found and therefore\n`nextOpen` (but also `previousOpen`, `nextClose` and `previousClose`) will throw a `MaximumLimitExceeded`\nYou can catch it and react accordingly or you can use `isAlwaysOpen` / `isAlwaysClosed` methods\nto anticipate such case.\n\n```php\n$openingHours-\u003enextClose(new DateTime('2016-12-24 11:00:00'));\n```\n\n#### `OpeningHours::previousOpen`\n\n```php\nOpeningHours::previousOpen(\n    ?DateTimeInterface $dateTime = null,\n    ?DateTimeInterface $searchUntil = null,\n    ?DateTimeInterface $cap = null,\n) : DateTimeInterface`\n```\n\nReturns previous open `DateTime` from the given `DateTime` (`$dateTime` or from now if this parameter is null or omitted).\n\nIf a `DateTimeImmutable` object is passed, a `DateTimeImmutable` object is returned.\n\nSet `$searchUntil` to a date to throw an exception if no open time can be found after this moment.\n\nSet `$cap` to a date so if no open time can be found after this moment, `$cap` is returned.\n\n```php\n$openingHours-\u003epreviousOpen(new DateTime('2016-12-24 11:00:00'));\n```\n\n#### `OpeningHours::previousClose`\n\n```php\nOpeningHours::previousClose(\n    ?DateTimeInterface $dateTime = null,\n    ?DateTimeInterface $searchUntil = null,\n    ?DateTimeInterface $cap = null,\n) : DateTimeInterface`\n```\n\nReturns previous close `DateTime` from the given `DateTime` (`$dateTime` or from now if this parameter is null or omitted).\n\nIf a `DateTimeImmutable` object is passed, a `DateTimeImmutable` object is returned.\n\nSet `$searchUntil` to a date to throw an exception if no closed time can be found after this moment.\n\nSet `$cap` to a date so if no closed time can be found after this moment, `$cap` is returned.\n\n```php\n$openingHours-\u003enextClose(new DateTime('2016-12-24 11:00:00'));\n```\n\n#### `OpeningHours::diffInOpenHours(DateTimeInterface $startDate, DateTimeInterface $endDate) : float`\n\nReturn the amount of open time (number of hours as a floating number) between 2 dates/times.\n\n```php\n$openingHours-\u003ediffInOpenHours(new DateTime('2016-12-24 11:00:00'), new DateTime('2016-12-24 16:34:25'));\n```\n\n#### `OpeningHours::diffInOpenMinutes(DateTimeInterface $startDate, DateTimeInterface $endDate) : float`\n\nReturn the amount of open time (number of minutes as a floating number) between 2 dates/times.\n\n#### `OpeningHours::diffInOpenSeconds(DateTimeInterface $startDate, DateTimeInterface $endDate) : float`\n\nReturn the amount of open time (number of seconds as a floating number) between 2 dates/times.\n\n#### `OpeningHours::diffInClosedHours(DateTimeInterface $startDate, DateTimeInterface $endDate) : float`\n\nReturn the amount of closed time (number of hours as a floating number) between 2 dates/times.\n\n```php\n$openingHours-\u003ediffInClosedHours(new DateTime('2016-12-24 11:00:00'), new DateTime('2016-12-24 16:34:25'));\n```\n\n#### `OpeningHours::diffInClosedMinutes(DateTimeInterface $startDate, DateTimeInterface $endDate) : float`\n\nReturn the amount of closed time (number of minutes as a floating number) between 2 dates/times.\n\n#### `OpeningHours::diffInClosedSeconds(DateTimeInterface $startDate, DateTimeInterface $endDate) : float`\n\nReturn the amount of closed time (number of seconds as a floating number) between 2 dates/times.\n\n#### `OpeningHours::currentOpenRange(DateTimeInterface $dateTime) : false | TimeRange`\n\nReturns a `Spatie\\OpeningHours\\TimeRange` instance of the current open range if the\nbusiness is open, false if the business is closed.\n\n```php\n$range = $openingHours-\u003ecurrentOpenRange(new DateTime('2016-12-24 11:00:00'));\n\nif ($range) {\n    echo \"It's open since \".$range-\u003estart().\"\\n\";\n    echo \"It will close at \".$range-\u003eend().\"\\n\";\n} else {\n    echo \"It's closed\";\n}\n```\n\n`start()` and `end()` methods return `Spatie\\OpeningHours\\Time` instances. `Time`\ninstances created from a date can be formatted with date information. This is useful\nfor ranges overflowing midnight:\n\n```php\n$period = $openingHours-\u003ecurrentOpenRange(new DateTime('2016-12-24 11:00:00'));\n\nif ($period) {\n    echo \"It's open since \".$period-\u003estart()-\u003eformat('D G\\h').\"\\n\";\n    echo \"It will close at \".$period-\u003eend()-\u003eformat('D G\\h').\"\\n\";\n} else {\n    echo \"It's closed\";\n}\n```\n\n#### `OpeningHours::currentOpenRangeStart(DateTimeInterface $dateTime) : false | DateTime`\n\nReturns a `DateTime` instance of the date and time since when the business is open if\nthe business is open, false if the business is closed.\n\nNote: date can be the previous day if you use night ranges.\n\n```php\n$date = $openingHours-\u003ecurrentOpenRangeStart(new DateTime('2016-12-24 11:00:00'));\n\nif ($date) {\n    echo \"It's open since \".$date-\u003eformat('H:i');\n} else {\n    echo \"It's closed\";\n}\n```\n\n#### `OpeningHours::currentOpenRangeEnd(DateTimeInterface $dateTime) : false | DateTime`\n\nReturns a `DateTime` instance of the date and time until when the business will be open\nif the business is open, false if the business is closed.\n\nNote: date can be the next day if you use night ranges.\n\n```php\n$date = $openingHours-\u003ecurrentOpenRangeEnd(new DateTime('2016-12-24 11:00:00'));\n\nif ($date) {\n    echo \"It will close at \".$date-\u003eformat('H:i');\n} else {\n    echo \"It's closed\";\n}\n```\n\n#### `OpeningHours::createFromStructuredData(array|string $data, $timezone = null, $outputTimezone = null): Spatie\\OpeningHours\\OpeningHours`\n\nStatic factory method to fill the set with a https://schema.org/OpeningHoursSpecification array or JSON string.\n\n`dayOfWeek` supports array of day names (Google-flavored) or array of day URLs (official schema.org specification).\n\n```php\n$openingHours = OpeningHours::createFromStructuredData('[\n    {\n        \"@type\": \"OpeningHoursSpecification\",\n        \"opens\": \"08:00\",\n        \"closes\": \"12:00\",\n        \"dayOfWeek\": [\n            \"https://schema.org/Monday\",\n            \"https://schema.org/Tuesday\",\n            \"https://schema.org/Wednesday\",\n            \"https://schema.org/Thursday\",\n            \"https://schema.org/Friday\"\n        ]\n    },\n    {\n        \"@type\": \"OpeningHoursSpecification\",\n        \"opens\": \"14:00\",\n        \"closes\": \"18:00\",\n        \"dayOfWeek\": [\n            \"Monday\",\n            \"Tuesday\",\n            \"Wednesday\",\n            \"Thursday\",\n            \"Friday\"\n        ]\n    },\n    {\n        \"@type\": \"OpeningHoursSpecification\",\n        \"opens\": \"00:00\",\n        \"closes\": \"00:00\",\n        \"validFrom\": \"2023-12-25\",\n        \"validThrough\": \"2023-12-25\"\n    }\n]');\n```\n\n#### `OpeningHours::asStructuredData(strinf $format = 'H:i', string|DateTimeZone $timezone) : array`\n\nReturns a [OpeningHoursSpecification](https://schema.org/openingHoursSpecification) as an array.\n\n```php\n$openingHours-\u003easStructuredData();\n$openingHours-\u003easStructuredData('H:i:s'); // Customize time format, could be 'h:i a', 'G:i', etc.\n$openingHours-\u003easStructuredData('H:iP', '-05:00'); // Add a timezone\n// Timezone can be numeric or string like \"America/Toronto\" or a DateTimeZone instance\n// But be careful, the time is arbitrary applied on 1970-01-01, so it does not handle daylight\n// saving time, meaning Europe/Paris is always +01:00 even in summer time.\n```\n\n### `Spatie\\OpeningHours\\OpeningHoursForDay`\n\nThis class is meant as read-only. It implements `ArrayAccess`, `Countable` and `IteratorAggregate` so you can process the list of `TimeRange`s in an array-like way.\n\n### `Spatie\\OpeningHours\\TimeRange`\n\nValue object describing a period with a start and an end time. Can be cast to a string in a `H:i-H:i` format.\n\n### `Spatie\\OpeningHours\\Time`\n\nValue object describing a single time. Can be cast to a string in a `H:i` format.\n\n## Adapters\n\n### OpenStreetMap\n\nYou can convert OpenStreetMap format to `OpeningHours` object using [osm-opening-hours](https://github.com/ujamii/osm-opening-hours) (thanks to [mgrundkoetter](https://github.com/mgrundkoetter))\n\n## Changelog\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information about what has changed recently.\n\n## Testing\n\n``` bash\ncomposer test\n```\n\n## Contributing\n\nPlease see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.\n\n## Security\n\nIf you've found a bug regarding security please mail [security@spatie.be](mailto:security@spatie.be) instead of using the issue tracker.\n\n## Postcardware\n\nYou're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.\n\nOur address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.\n\nWe publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards).\n\n## Credits\n\n- [Sebastian De Deyne](https://github.com/sebastiandedeyne)\n- [All Contributors](../../contributors)\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspatie%2Fopening-hours","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fspatie%2Fopening-hours","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fspatie%2Fopening-hours/lists"}