spatie / opening-hours
查询和格式化一组营业时间的辅助工具
Requires
- php: ^8.2
Requires (Dev)
- kylekatarnls/multi-tester: ^2.5
- phpunit/phpunit: ^11.2
- dev-master / 4.x-dev
- 4.0.0
- 3.x-dev
- 3.0.0
- 2.x-dev
- 2.41.0
- 2.13.0
- 2.12.0
- 2.11.3
- 2.11.2
- 2.11.1
- 2.11.0
- 2.10.1
- 2.10.0
- 2.9.1
- 2.9.0
- 2.8.0
- 2.7.2
- 2.7.1
- 2.7.0
- 2.6.0
- 2.5.0
- 2.4.1
- 2.4.0
- 2.3.3
- 2.3.2
- 2.3.1
- 2.3.0
- 2.2.1
- 2.2.0
- 2.1.2
- 2.1.1
- 2.1.0
- 2.1.0-beta.1
- 2.0.0
- 2.0.0-beta.1
- 1.9.0
- 1.8.1
- 1.8.0
- 1.7.0
- 1.6.0
- 1.5.0
- 1.4.0
- 1.3.1
- 1.3.0
- 1.2.0
- 1.1.0
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- 0.3.1
- 0.3.0
- 0.2.0
- 0.1.0
This package is auto-updated.
Last update: 2024-08-26 08:23:20 UTC
README
查询和格式化一组营业时间的辅助工具
使用 spatie/opening-hours
,你可以创建一个描述商业营业时间的对象,你可以查询该对象在特定日期或星期几的“open”或“closed”状态,或者使用它来展示每天的营业时间。
spatie/opening-hours
可以直接在 Carbon 上使用,这得益于 cmixin/business-time,因此你可以在增强的日期对象上直接受益于营业时间功能。
通过传递一个常规计划和一个异常列表,可以创建一组营业时间。
// Add the use at the top of each file where you want to use the OpeningHours class: use Spatie\OpeningHours\OpeningHours; $openingHours = OpeningHours::create([ 'monday' => ['09:00-12:00', '13:00-18:00'], 'tuesday' => ['09:00-12:00', '13:00-18:00'], 'wednesday' => ['09:00-12:00'], 'thursday' => ['09:00-12:00', '13:00-18:00'], 'friday' => ['09:00-12:00', '13:00-20:00'], 'saturday' => ['09:00-12:00', '13:00-16:00'], 'sunday' => [], 'exceptions' => [ '2016-11-11' => ['09:00-12:00'], '2016-12-25' => [], '01-01' => [], // Recurring on each 1st of January '12-25' => ['09:00-12:00'], // Recurring on each 25th of December ], ]); // This will allow you to display things like: $now = new DateTime('now'); $range = $openingHours->currentOpenRange($now); if ($range) { echo "It's open since ".$range->start()."\n"; echo "It will close at ".$range->end()."\n"; } else { echo "It's closed since ".$openingHours->previousClose($now)->format('l H:i')."\n"; echo "It will re-open at ".$openingHours->nextOpen($now)->format('l H:i')."\n"; }
对象可以被查询星期中的某一天,它将根据常规计划返回结果。
// Open on Mondays: $openingHours->isOpenOn('monday'); // true // Closed on Sundays: $openingHours->isOpenOn('sunday'); // false
它还可以查询特定日期和时间。
// Closed because it's after hours: $openingHours->isOpenAt(new DateTime('2016-09-26 19:00:00')); // false // Closed because Christmas was set as an exception $openingHours->isOpenOn('2016-12-25'); // false
它还可以返回一周或一天的营业时间数组。
// OpeningHoursForDay object for the regular schedule $openingHours->forDay('monday'); // OpeningHoursForDay[] for the regular schedule, keyed by day name $openingHours->forWeek(); // Array of day with same schedule for the regular schedule, keyed by day name, days combined by working hours $openingHours->forWeekCombined(); // OpeningHoursForDay object for a specific day $openingHours->forDate(new DateTime('2016-12-25')); // OpeningHoursForDay[] of all exceptions, keyed by date $openingHours->exceptions();
在构造时,你可以设置一个标志来表示跨天时间的溢出。例如,对于在周五和周六开放到凌晨3点的夜总会。
$openingHours = \Spatie\OpeningHours\OpeningHours::create([ 'overflow' => true, 'friday' => ['20:00-03:00'], 'saturday' => ['20:00-03:00'], ], null);
这允许API进一步检查前一天的日期数据,以检查营业时间是否在其时间范围内开放。
你可以在定义中添加数据,然后检索它们。
$openingHours = OpeningHours::create([ 'monday' => [ 'data' => 'Typical Monday', '09:00-12:00', '13:00-18:00', ], 'tuesday' => [ '09:00-12:00', '13:00-18:00', [ '19:00-21:00', 'data' => 'Extra on Tuesday evening', ], ], 'exceptions' => [ '2016-12-25' => [ 'data' => 'Closed for Christmas', ], ], ]); echo $openingHours->forDay('monday')->data; // Typical Monday echo $openingHours->forDate(new DateTime('2016-12-25'))->data; // Closed for Christmas echo $openingHours->forDay('tuesday')[2]->data; // Extra on Tuesday evening
在上面的示例中,数据是字符串,但它可以是任何类型的值。因此,你可以在数组中嵌入多个属性。
为了方便结构,数据-时间对可以是完全关联的数组,因此上面的示例严格等价于以下示例
$openingHours = OpeningHours::create([ 'monday' => [ 'hours' => [ '09:00-12:00', '13:00-18:00', ], 'data' => 'Typical Monday', ], 'tuesday' => [ ['hours' => '09:00-12:00'], ['hours' => '13:00-18:00'], ['hours' => '19:00-21:00', 'data' => 'Extra on Tuesday evening'], ], // Open by night from Wednesday 22h to Thursday 7h: 'wednesday' => ['22:00-24:00'], // use the special "24:00" to reach midnight included 'thursday' => ['00:00-07:00'], 'exceptions' => [ '2016-12-25' => [ 'hours' => [], 'data' => 'Closed for Christmas', ], ], ]);
你可以使用分隔符 to
来一次性指定多个日期,对于周或异常。
$openingHours = OpeningHours::create([ 'monday to friday' => ['09:00-19:00'], 'saturday to sunday' => [], 'exceptions' => [ // Every year '12-24 to 12-26' => [ 'hours' => [], 'data' => 'Holidays', ], // Only happening in 2024 '2024-06-25 to 2024-07-01' => [ 'hours' => [], 'data' => 'Closed for works', ], ], ]);
最后一个结构工具是过滤器,它允许你传递闭包(或可调用的函数/方法引用),这些闭包以日期作为参数,并返回给定日期的设置。
$openingHours = OpeningHours::create([ 'monday' => [ '09:00-12:00', ], 'filters' => [ function ($date) { $year = intval($date->format('Y')); $easterMonday = new DateTimeImmutable('2018-03-21 +'.(easter_days($year) + 1).'days'); if ($date->format('m-d') === $easterMonday->format('m-d')) { return []; // Closed on Easter Monday // Any valid exception-array can be returned here (range of hours, with or without data) } // Else the filter does not apply to the given date }, ], ]);
如果可调用的函数在“exceptions”属性中找到,它将被自动添加到过滤器中,因此你可以混合过滤器与异常,既在“exceptions”数组中,也在“filters”数组中。第一个返回非空值的过滤器将优先于下一个过滤器,并且“filters”数组优先于“exceptions”数组中的过滤器。
警告:我们将对所有过滤器进行循环,以获取所需的开闭营业时间,并且不能预测或缓存结果(可能是随机函数),因此你必须小心使用过滤器,太多的过滤器或过滤器中的长过程可能会对性能产生重大影响。
它还可以从给定的 DateTime
返回下一个开放或关闭的 DateTime
。
// The next open datetime is tomorrow morning, because we’re closed on 25th of December. $nextOpen = $openingHours->nextOpen(new DateTime('2016-12-25 10:00:00')); // 2016-12-26 09:00:00 // The next open datetime is this afternoon, after the lunch break. $nextOpen = $openingHours->nextOpen(new DateTime('2016-12-24 11:00:00')); // 2016-12-24 13:00:00 // The next close datetime is at noon. $nextClose = $openingHours->nextClose(new DateTime('2016-12-24 10:00:00')); // 2016-12-24 12:00:00 // The next close datetime is tomorrow at noon, because we’re closed on 25th of December. $nextClose = $openingHours->nextClose(new DateTime('2016-12-25 15:00:00')); // 2016-12-26 12:00:00
请参阅使用部分以了解完整的API。
Spatie 是一家总部位于比利时的安特卫普的网页设计公司。你可以在我们的网站上找到我们所有开源项目的概述:在这里。
支持我们
我们投入了大量资源来创建最佳的开源软件包。你可以通过购买我们的付费产品之一来支持我们。
我们非常感谢您从家乡寄给我们一张明信片,并注明您正在使用我们的哪个包裹。您可以在我们的联系页面找到我们的地址。我们将所有收到的明信片发布在我们的虚拟明信片墙上。
安装
您可以通过composer安装此包
composer require spatie/opening-hours
用法
此包应仅通过OpeningHours
类使用。还包括三个值对象类,Time
表示单个时间,TimeRange
表示有开始和结束的时期,以及openingHoursForDay
表示一组不能重叠的TimeRange
。
Spatie\OpeningHours\OpeningHours
OpeningHours::create(array $data, $timezone = null, $toutputTimezone = null): Spatie\OpeningHours\OpeningHours
静态工厂方法,用于填充营业时间集合。
$openingHours = OpeningHours::create([ 'monday' => ['09:00-12:00', '13:00-18:00'], // ... ]);
如果没有指定时区,OpeningHours
将假设您始终传递带有匹配您日程的时区的DateTime
对象。
如果您将$timezone
作为第二个参数或通过数组键'timezone'
(它可以是DateTimeZone
对象或字符串)传递,那么在每次方法开始时将传递的日期转换为该时区,然后如果方法返回日期对象(如nextOpen
、nextClose
、previousOpen
、previousClose
、currentOpenRangeStart
或currentOpenRangeEnd
),则将其转换回原始时区,以便在输出之前,对象可以反映用户的本地时间,而OpeningHours
可以保持在它自己的业务时区。
或者您也可以指定输入和输出时区(使用第二个和第三个参数)或使用数组
$openingHours = OpeningHours::create([ 'monday' => ['09:00-12:00', '13:00-18:00'], 'timezone' => [ 'input' => 'America/New_York', 'output' => 'Europe/Oslo', ], ]);
OpeningHours::mergeOverlappingRanges(array $schedule) : array
出于安全考虑,如果创建具有重叠范围的OpeningHours
对象,则会抛出异常,除非您在营业时间数组定义中显式传递'overflow' => true,
。您还可以显式合并它们。
$ranges = [ 'monday' => ['08:00-11:00', '10:00-12:00'], ]; $mergedRanges = OpeningHours::mergeOverlappingRanges($ranges); // Monday becomes ['08:00-12:00'] OpeningHours::create($mergedRanges); // Or use the following shortcut to create from ranges that possibly overlap: OpeningHours::createAndMergeOverlappingRanges($ranges);
并非所有天都是强制性的,如果某天缺失,则将其设置为关闭。
OpeningHours::fill(array $data): Spatie\OpeningHours\OpeningHours
与create
相同,但非静态。
$openingHours = (new OpeningHours)->fill([ 'monday' => ['09:00-12:00', '13:00-18:00'], // ... ]);
OpeningHours::forWeek(): Spatie\OpeningHours\OpeningHoursForDay[]
返回一个包含OpeningHoursForDay
对象的数组,用于常规周。
$openingHours->forWeek();
OpeningHours::forWeekCombined(): array
返回一个包含天数的数组。数组的键是具有相同小时的第一天,数组值是有相同工作时间的天数和OpeningHoursForDay
对象。
$openingHours->forWeekCombined();
OpeningHours::forWeekConsecutiveDays(): array
返回一个连接天数的数组,相邻天数具有相同的小时。数组的键是具有相同小时的第一天,数组值是有相同工作时间的天数和OpeningHoursForDay
对象。
警告:连续天被视为从星期一到星期日,不考虑初始数据中的天数顺序(星期一不是连续到星期日)。
$openingHours->forWeekConsecutiveDays();
OpeningHours::forDay(string $day): Spatie\OpeningHours\OpeningHoursForDay
返回一个用于常规天的OpeningHoursForDay
对象。一天是英语天名的小写字符串。
$openingHours->forDay('monday');
OpeningHours::forDate(DateTimeInterface $dateTime): Spatie\OpeningHours\OpeningHoursForDay
返回一个用于特定日期的OpeningHoursForDay
对象。它在该天寻找异常,如果没有找到,则根据常规计划返回营业时间。
$openingHours->forDate(new DateTime('2016-12-25'));
OpeningHours::exceptions(): Spatie\OpeningHours\OpeningHoursForDay[]
返回一个由Y-m-d
日期字符串键控的包含所有OpeningHoursForDay
对象的数组。
$openingHours->exceptions();
OpeningHours::isOpenOn(string $day): bool
检查在常规计划中的某一天,业务是否开放(包含至少一个营业时间范围)。
$openingHours->isOpenOn('saturday');
如果给定的字符串是日期,它将检查是否开放(包含至少一个营业时间范围),考虑常规日计划以及可能的异常。
$openingHours->isOpenOn('2020-09-03'); $openingHours->isOpenOn('09-03'); // If year is omitted, current year is used instead
OpeningHours::isClosedOn(string $day): bool
检查在常规计划中的某一天,业务是否关闭。
$openingHours->isClosedOn('sunday');
OpeningHours::isOpenAt(DateTimeInterface $dateTime): bool
检查在特定日期和特定时间,商家是否营业。
$openingHours->isOpenAt(new DateTime('2016-26-09 20:00'));
OpeningHours::isClosedAt(DateTimeInterface $dateTime): bool
检查在特定日期和特定时间,商家是否关门。
$openingHours->isClosedAt(new DateTime('2016-26-09 20:00'));
OpeningHours::isOpen(): bool
检查商家当前是否营业。
$openingHours->isOpen();
OpeningHours::isClosed(): bool
检查商家当前是否关门。
$openingHours->isClosed();
OpeningHours::isAlwaysOpen(): bool
检查商家是否全天24小时营业,没有例外和过滤器。
if ($openingHours->isAlwaysOpen()) { echo 'This business is open all day long every day.'; }
OpeningHours::isAlwaysClosed(): bool
检查商家是否永远不营业,没有例外和过滤器。
OpeningHours
接受空数组或列表,其中每个星期几的列表都为空,没有偏见。
如果这不是您领域中的有效状态,您应该使用此方法来抛出异常或显示错误。
if ($openingHours->isAlwaysClosed()) { throw new RuntimeException('Opening hours missing'); }
OpeningHours::nextOpen
OpeningHours::nextOpen( ?DateTimeInterface $dateTime = null, ?DateTimeInterface $searchUntil = null, ?DateTimeInterface $cap = null, ) : DateTimeInterface`
从给定的DateTime
($dateTime
或如果此参数为null或省略,则为现在)返回下一个打开的DateTime
。
如果传递了一个DateTimeImmutable
对象,则返回一个DateTimeImmutable
对象。
将$searchUntil
设置为日期,如果在此日期之前找不到开放时间,则抛出异常。
将$cap
设置为日期,如果在此日期之前找不到开放时间,则返回$cap
。
$openingHours->nextOpen(new DateTime('2016-12-24 11:00:00'));
`
OpeningHours::nextClose
OpeningHours::nextClose( ?DateTimeInterface $dateTime = null, ?DateTimeInterface $searchUntil = null, ?DateTimeInterface $cap = null, ) : DateTimeInterface`
从给定的DateTime
($dateTime
或如果此参数为null或省略,则为现在)返回下一个关闭的DateTime
。
如果传递了一个DateTimeImmutable
对象,则返回一个DateTimeImmutable
对象。
将$searchUntil
设置为日期,如果在此日期之前找不到关闭时间,则抛出异常。
将$cap
设置为日期,如果在此日期之前找不到关闭时间,则返回$cap
。
$openingHours->nextClose(new DateTime('2016-12-24 11:00:00'));
OpeningHours::previousOpen
OpeningHours::previousOpen( ?DateTimeInterface $dateTime = null, ?DateTimeInterface $searchUntil = null, ?DateTimeInterface $cap = null, ) : DateTimeInterface`
从给定的DateTime
($dateTime
或如果此参数为null或省略,则为现在)返回上一个打开的DateTime
。
如果传递了一个DateTimeImmutable
对象,则返回一个DateTimeImmutable
对象。
将$searchUntil
设置为日期,如果在此日期之后找不到开放时间,则抛出异常。
将$cap
设置为日期,如果在此日期之后找不到开放时间,则返回$cap
。
$openingHours->previousOpen(new DateTime('2016-12-24 11:00:00'));
OpeningHours::previousClose
OpeningHours::previousClose( ?DateTimeInterface $dateTime = null, ?DateTimeInterface $searchUntil = null, ?DateTimeInterface $cap = null, ) : DateTimeInterface`
从给定的DateTime
($dateTime
或如果此参数为null或省略,则为现在)返回上一个关闭的DateTime
。
如果传递了一个DateTimeImmutable
对象,则返回一个DateTimeImmutable
对象。
将$searchUntil
设置为日期,如果在此日期之后找不到关闭时间,则抛出异常。
将$cap
设置为日期,如果在此日期之后找不到关闭时间,则返回$cap
。
$openingHours->nextClose(new DateTime('2016-12-24 11:00:00'));
OpeningHours::diffInOpenHours(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回两个日期/时间之间开放时间(以浮点数表示的小时数)的数量。
$openingHours->diffInOpenHours(new DateTime('2016-12-24 11:00:00'), new DateTime('2016-12-24 16:34:25'));
OpeningHours::diffInOpenMinutes(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回两个日期/时间之间开放时间(以浮点数表示的分钟数)的数量。
OpeningHours::diffInOpenSeconds(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回两个日期/时间之间开放时间(以浮点数表示的秒数)的数量。
OpeningHours::diffInClosedHours(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回两个日期/时间之间关闭时间(以浮点数表示的小时数)的数量。
$openingHours->diffInClosedHours(new DateTime('2016-12-24 11:00:00'), new DateTime('2016-12-24 16:34:25'));
OpeningHours::diffInClosedMinutes(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回两个日期/时间之间关闭时间(以浮点数表示的分钟数)的数量。
OpeningHours::diffInClosedSeconds(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回两个日期/时间之间关闭时间(以浮点数表示的秒数)的数量。
OpeningHours::currentOpenRange(DateTimeInterface $dateTime) : false | TimeRange
如果商家营业,则返回当前开放范围的Spatie\OpeningHours\TimeRange
实例,如果商家关门,则返回false。
$range = $openingHours->currentOpenRange(new DateTime('2016-12-24 11:00:00')); if ($range) { echo "It's open since ".$range->start()."\n"; echo "It will close at ".$range->end()."\n"; } else { echo "It's closed"; }
start()
和 end()
方法返回 Spatie\OpeningHours\Time
实例。从日期创建的 Time
实例可以格式化带日期信息。这对于超出午夜的时间范围很有用。
$period = $openingHours->currentOpenRange(new DateTime('2016-12-24 11:00:00')); if ($period) { echo "It's open since ".$period->start()->format('D G\h')."\n"; echo "It will close at ".$period->end()->format('D G\h')."\n"; } else { echo "It's closed"; }
OpeningHours::currentOpenRangeStart(DateTimeInterface $dateTime) : false | DateTime
如果商家营业,返回自商家开门以来的日期和时间 DateTime
实例,如果商家关闭,返回 false。
注意:如果您使用夜间时间范围,日期可能是前一天。
$date = $openingHours->currentOpenRangeStart(new DateTime('2016-12-24 11:00:00')); if ($date) { echo "It's open since ".$date->format('H:i'); } else { echo "It's closed"; }
OpeningHours::currentOpenRangeEnd(DateTimeInterface $dateTime) : false | DateTime
如果商家营业,返回商家开门将关闭的日期和时间 DateTime
实例,如果商家关闭,返回 false。
注意:如果您使用夜间时间范围,日期可能是后一天。
$date = $openingHours->currentOpenRangeEnd(new DateTime('2016-12-24 11:00:00')); if ($date) { echo "It will close at ".$date->format('H:i'); } else { echo "It's closed"; }
OpeningHours::createFromStructuredData(array|string $data, $timezone = null, $outputTimezone = null): Spatie\OpeningHours\OpeningHours
静态工厂方法,用于使用 https://schema.org/OpeningHoursSpecification 数组或 JSON 字符串填充集合。
dayOfWeek
支持数组形式的星期名称(Google风格)或星期 URL(官方 schema.org 规范)。
$openingHours = OpeningHours::createFromStructuredData('[ { "@type": "OpeningHoursSpecification", "opens": "08:00", "closes": "12:00", "dayOfWeek": [ "https://schema.org/Monday", "https://schema.org/Tuesday", "https://schema.org/Wednesday", "https://schema.org/Thursday", "https://schema.org/Friday" ] }, { "@type": "OpeningHoursSpecification", "opens": "14:00", "closes": "18:00", "dayOfWeek": [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" ] }, { "@type": "OpeningHoursSpecification", "opens": "00:00", "closes": "00:00", "validFrom": "2023-12-25", "validThrough": "2023-12-25" } ]');
OpeningHours::asStructuredData(string $format = 'H:i', string|DateTimeZone $timezone) : array
返回一个 OpeningHoursSpecification 数组。
$openingHours->asStructuredData(); $openingHours->asStructuredData('H:i:s'); // Customize time format, could be 'h:i a', 'G:i', etc. $openingHours->asStructuredData('H:iP', '-05:00'); // Add a timezone // Timezone can be numeric or string like "America/Toronto" or a DateTimeZone instance // But be careful, the time is arbitrary applied on 1970-01-01, so it does not handle daylight // saving time, meaning Europe/Paris is always +01:00 even in summer time.
Spatie\OpeningHours\OpeningHoursForDay
这个类旨在作为只读。它实现了 ArrayAccess
、Countable
和 IteratorAggregate
,因此您可以以类似数组的方式处理 TimeRange
列表。
Spatie\OpeningHours\TimeRange
描述从开始到结束时间的期间的值对象。可以将其转换为 H:i-H:i
格式的字符串。
Spatie\OpeningHours\Time
描述单个时间的值对象。可以将其转换为 H:i
格式的字符串。
适配器
OpenStreetMap
您可以使用 osm-opening-hours(感谢 mgrundkoetter)将 OpenStreetMap 格式转换为 OpeningHours
对象。
变更日志
有关最近更改的更多信息,请参阅 CHANGELOG。
测试
composer test
贡献
有关详细信息,请参阅 CONTRIBUTING。
安全性
如果您发现与安全相关的错误,请通过电子邮件 security@spatie.be 而不是使用问题跟踪器。
明信片软件
您可以使用此软件包,但如果它进入您的生产环境,我们非常欢迎您从您的家乡给我们寄一张明信片,说明您正在使用我们的哪个软件包。
我们的地址是:Spatie,Kruikstraat 22,2018 安特卫普,比利时。
我们将所有收到的明信片 发布在我们的公司网站上。
致谢
许可
MIT 许可证 (MIT)。有关更多信息,请参阅 许可文件。