spatie/opening-hours

查询和格式化一组营业时间的辅助工具

4.0.0 2024-06-26 07:52 UTC

README

查询和格式化一组营业时间的辅助工具

Latest Version on Packagist Software License Tests Coverage Quality Score StyleCI Total Downloads

使用 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对象或字符串)传递,那么在每次方法开始时将传递的日期转换为该时区,然后如果方法返回日期对象(如nextOpennextClosepreviousOpenpreviousClosecurrentOpenRangeStartcurrentOpenRangeEnd),则将其转换回原始时区,以便在输出之前,对象可以反映用户的本地时间,而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

这个类旨在作为只读。它实现了 ArrayAccessCountableIteratorAggregate,因此您可以以类似数组的方式处理 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)。有关更多信息,请参阅 许可文件