使用spatie/opening-hours
您可以创建一个描述企业营业时间的对象,您可以查询该对象的open
或特定日期的营业时间或closed
,或用于显示每天的时间。
借助 cmixin/business-time, spatie/opening-hours
可以直接在 Carbon 上使用,因此您可以直接在增强型日期对象上受益于开放时间功能。
通过传入定期时间表和例外列表来创建一组开放时间。
// 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"
属性中找到可调用对象,它将自动添加到过滤器中,以便您可以在例外数组中混合过滤器和异常。返回非空值的第一个过滤器将优先于下一个过滤器,并且过滤器数组优先于异常数组内的过滤器。
警告:我们将循环使用需要检索开放时间的每个日期的所有过滤器,并且既不能谓词也不能缓存结果(可以是随机函数),因此您必须小心过滤器、过滤器太多或过滤器内的处理时间过长可能会对性能产生重大影响。
它还可以返回给定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
)。
SpatieOpeningHoursOpeningHours
OpeningHours::create(array $data, $timezone = null, $toutputTimezone = null): SpatieOpeningHoursOpeningHours
静态工厂方法来填充设置的开放时间。
$ openingHours = OpeningHours:: create ([
' monday ' => [ ' 09:00-12:00 ' , ' 13:00-18:00 ' ],
// ...
]);
如果未指定时区, OpeningHours
将假定您始终传递时区已与您的日程安排匹配的DateTime
对象。
如果您传递$timezone
作为第二个参数或通过数组键'timezone'
(它可以是DateTimeZone
对象或string
),则传递的日期将在每个方法的开头转换为该时区,然后如果该方法返回一个日期对象(例如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): SpatieOpeningHoursOpeningHours
与create
相同,但非静态。
$ openingHours = ( new OpeningHours )-> fill ([
' monday ' => [ ' 09:00-12:00 ' , ' 13:00-18:00 ' ],
// ...
]);
OpeningHours::forWeek(): SpatieOpeningHoursOpeningHoursForDay[]
返回一周的OpeningHoursForDay
对象数组。
$ openingHours -> forWeek ();
OpeningHours::forWeekCombined(): array
返回天数数组。数组键是具有相同工作时间的第一天,数组值是具有相同工作时间和OpeningHoursForDay
对象的日期。
$ openingHours -> forWeekCombined ();
OpeningHours::forWeekConsecutiveDays(): array
返回串联日期的数组,相邻日期具有相同的小时数。数组键是具有相同工作时间的第一天,数组值是具有相同工作时间和OpeningHoursForDay
对象的日期。
警告:无论初始数据中的日期顺序如何,都将考虑从周一到周日的连续天而不循环(周一不连续到周日)。
$ openingHours -> forWeekConsecutiveDays ();
OpeningHours::forDay(string $day): SpatieOpeningHoursOpeningHoursForDay
返回常规日期的OpeningHoursForDay
对象。日期是英文日期名称的小写字符串。
$ openingHours -> forDay ( ' monday ' );
OpeningHours::forDate(DateTimeInterface $dateTime): SpatieOpeningHoursOpeningHoursForDay
返回特定日期的OpeningHoursForDay
对象。它会查找当天的例外情况,否则会根据正常时间表返回开放时间。
$ openingHours -> forDate ( new DateTime ( ' 2016-12-25 ' ));
OpeningHours::exceptions(): SpatieOpeningHoursOpeningHoursForDay[]
返回异常的所有OpeningHoursForDay
对象的数组,以Ymd
日期字符串为键。
$ openingHours -> exceptions ();
OpeningHours::isOpenOn(string $day): bool
检查企业是否按正常时间表营业(至少包含 1 个营业时间范围)。
$ openingHours -> isOpenOn ( ' saturday ' );
如果给定的字符串是日期,它将检查其是否开放(至少包含 1 个开放时间范围),同时考虑常规日程安排和可能的例外情况。
$ 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/7 营业,没有例外,也没有过滤器。
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
或从现在开始,如果此参数为空或省略)的下一个打开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
或从现在开始,如果此参数为空或省略)的下一个关闭DateTime
。
如果传递DateTimeImmutable
对象,则返回DateTimeImmutable
对象。
将$searchUntil
设置为一个日期,如果在此时刻之前找不到关闭时间,则会引发异常。
将$cap
设置为一个日期,这样如果在此之前找不到关闭时间,则返回$cap
。
如果计划始终打开或始终关闭,则不会发现任何状态更改,因此nextOpen
(以及previousOpen
、 nextClose
和previousClose
)将抛出MaximumLimitExceeded
您可以捕获它并做出相应反应,也可以使用isAlwaysOpen
/ isAlwaysClosed
方法来预测这样的情况。
$ 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
或从现在开始,如果此参数为空或省略)的上一个打开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
或从现在开始,如果此参数为空或省略)的上一个关闭DateTime
。
如果传递DateTimeImmutable
对象,则返回DateTimeImmutable
对象。
将$searchUntil
设置为一个日期,如果在此时间之后找不到关闭时间,则会抛出异常。
将$cap
设置为一个日期,这样如果在此时间之后找不到关闭时间,则返回$cap
。
$ openingHours -> nextClose ( new DateTime ( ' 2016-12-24 11:00:00 ' ));
OpeningHours::diffInOpenHours(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回 2 个日期/时间之间的开放时间量(浮点数形式的小时数)。
$ 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
返回 2 个日期/时间之间的开放时间量(分钟数,浮点数)。
OpeningHours::diffInOpenSeconds(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回 2 个日期/时间之间的开放时间量(浮点数形式的秒数)。
OpeningHours::diffInClosedHours(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回 2 个日期/时间之间的关闭时间(小时数,浮点数)。
$ 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
返回 2 个日期/时间之间的关闭时间(分钟数,浮点数)。
OpeningHours::diffInClosedSeconds(DateTimeInterface $startDate, DateTimeInterface $endDate) : float
返回 2 个日期/时间之间的关闭时间(秒数,浮点数)。
OpeningHours::currentOpenRange(DateTimeInterface $dateTime) : false | TimeRange
如果商家正在营业,则返回当前营业范围的SpatieOpeningHoursTimeRange
实例;如果商家已关闭,则返回 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()
方法返回SpatieOpeningHoursTime
实例。从日期创建的Time
实例可以使用日期信息进行格式化。这对于午夜溢出的范围很有用:
$ period = $ openingHours -> currentOpenRange ( new DateTime ( ' 2016-12-24 11:00:00 ' ));
if ( $ period ) {
echo " It's open since " . $ period -> start ()-> format ( ' D Gh ' ). "n" ;
echo " It will close at " . $ period -> end ()-> format ( ' D Gh ' ). "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): SpatieOpeningHoursOpeningHours
使用 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(strinf $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.
SpatieOpeningHoursOpeningHoursForDay
该类是只读的。它实现了ArrayAccess
、 Countable
和IteratorAggregate
因此您可以以类似数组的方式处理TimeRange
列表。
SpatieOpeningHoursTimeRange
描述具有开始时间和结束时间的时间段的值对象。可以转换为H:iH:i
格式的字符串。
SpatieOpeningHoursTime
描述单个时间的值对象。可以转换为H:i
格式的字符串。
您可以使用 osm-opening-hours 将 OpenStreetMap 格式转换为OpeningHours
对象(感谢 mgrundkoetter)
请参阅变更日志以了解有关最近更改内容的更多信息。
composer test
详细信息请参阅贡献。
如果您发现有关安全的错误,请发送邮件至 [email protected],而不是使用问题跟踪器。
您可以自由使用这个软件包,但如果它进入您的生产环境,我们非常感谢您从您的家乡给我们寄一张明信片,注明您正在使用我们的哪个软件包。
我们的地址是:Spatie, Kruikstraat 22, 2018 安特卫普, 比利时。
我们在公司网站上发布所有收到的明信片。
麻省理工学院许可证 (MIT)。请参阅许可证文件以获取更多信息。