ckuran/period

复杂的周期比较

1.4.4 2019-08-05 07:34 UTC

README

Latest Version on Packagist Build Status Quality Score StyleCI Total Downloads

此包添加了对比较多个日期相互支持。您可以计算n个时间段之间的重叠和差异,以及两个时间段之间的一些更基本的比较。

时间段可以由任何类型的 DateTime 实现构建,这使得此包与自定义的 DateTime 实现(如 Carbon)兼容(请参阅 cmixin/enhanced-period 以直接从CarbonPeriod转换过来)。

时间段始终是不可变的,您永远不用担心输入的日期会被更改。

此包仍在开发中。

安装

您可以通过composer安装此包

composer require spatie/period

用法

快速参考

创建一个时间段

$period = new Period(
     DateTimeImmutable $start
   , DateTimeImmutable $end
  [, ?int $precisionMask = Precision::DAY]
  [, ?int $boundaryExclusionMask = Boundaries::EXCLUDE_NONE]
)

静态构造函数 ::make 也可以接受字符串和其他 DateTimeInterface 实现以及额外的格式字符串,以防传递的文本日期不是 Y-m-dY-m-d H:i:s 格式。

$period = Period::make(
     string|DateTimeInterface $start
   , string|DateTimeInterface $end
  [, ?int Precision::DAY]
  [, ?int Boundaries::EXCLUDE_NONE]
  [, string $format]
)

长度和边界

$period->length(): int
$period->startIncluded(): bool
$period->startExcluded(): bool
$period->endIncluded(): bool
$period->endExcluded(): bool
$period->getStart(): DateTimeImmutable
$period->getStartIncluded(): DateTimeImmutable
$period->getEnd(): DateTimeImmutable
$period->getEndIncluded(): DateTimeImmutable

比较

$period->contains(DateTimeInterface $date): bool
$period->equals(Period $period): bool

$period->overlapsWith(Period $period): bool
$period->touchesWith(Period $period): bool
$period->startsAt(DateTimeInterface $date): bool
$period->startsBefore(DateTimeInterface $date): bool
$period->startsBeforeOrAt(DateTimeInterface $date): bool
$period->startsAfter(DateTimeInterface $date): bool
$period->startsAfterOrAt(DateTimeInterface $date): bool
$period->endsAt(DateTimeInterface $date): bool
$period->endsBefore(DateTimeInterface $date): bool
$period->endsBeforeOrAt(DateTimeInterface $date): bool
$period->endsAfter(DateTimeInterface $date): bool
$period->endsAfterOrAt(DateTimeInterface $date): bool
$period->gap(Period $period): ?Period
$period->overlapSingle(Period $period): ?Period
$period->overlap(Period ...$periods): PeriodCollection
$period->overlapAll(Period ...$periods): Period
$period->diffSingle(Period $period): PeriodCollection
$period->diff(Period ...$periods): PeriodCollection
$periodCollection->overlap(PeriodCollection ...$periodCollections): PeriodCollection
$periodCollection->overlapSingle(PeriodCollection $periodCollection): PeriodCollection
$periodCollection->map(Closure<Period> $closure): PeriodCollection
$periodCollection->reduce(Closure<mixed, Period> $closure): mixed
$periodCollection->boundaries(): ?Period
$periodCollection->gaps(): PeriodCollection

比较时间段

与其他任何时间段的重叠:此方法返回一个包含多个重叠 Period 对象的 PeriodCollection

/*
 * A       [========]
 * B                    [==]
 * C                            [=====]
 * CURRENT        [===============]
 *
 * OVERLAP        [=]   [==]    [=]
 */
 
$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-02-10', '2018-02-20');
$c = Period::make('2018-03-01', '2018-03-31');

$current = Period::make('2018-01-20', '2018-03-10');

$overlaps = $current->overlap($a, $b, $c); 

与所有时间段的重叠:此方法只返回一个所有时间段都重叠的时间段。

/*
 * A              [============]
 * B                   [==]
 * C                  [=======]
 *
 * OVERLAP             [==]
 */

$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-01-10', '2018-01-15');
$c = Period::make('2018-01-10', '2018-01-31');

$overlap = $a->overlapAll($b, $c);

多个时间段之间的差异:此方法返回一个包含多个时间段和单个时间段之间的差异的 PeriodCollection

/*
 * A                   [====]
 * B                               [========]
 * C         [=====]
 * CURRENT      [========================]
 *
 * DIFF             [=]      [====]
 */

$a = Period::make('2018-01-05', '2018-01-10');
$b = Period::make('2018-01-15', '2018-03-01');
$c = Period::make('2017-01-01', '2018-01-02');

$current = Period::make('2018-01-01', '2018-01-31');

$diff = $current->diff($a, $b, $c);

与重叠:此方法返回一个布尔值,指示两个时间段是否重叠。

/*
 * A              [============]
 * B                   [===========]
 */

$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-01-10', '2018-02-15');

$overlap = $a->overlapsWith($b); // true

接触:此方法确定两个时间段是否相互接触。

/*
 * A              [========]
 * B                        [===========]
 */

$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-02-01', '2018-02-15');

$overlap = $a->touchesWith($b); // true

间隔:返回两个时间段之间的间隔。如果不存在间隔,则返回 null

/*
 * A              [========]
 * B                           [===========]
 */

$a = Period::make('2018-01-01', '2018-01-31');
$b = Period::make('2018-02-05', '2018-02-15');

$overlap = $a->gap($b); // Period('2018-02-01', '2018-02-04')

集合的边界:获取表示集合边界的单个时间段。

/*
 * A                   [====]
 * B                               [========]
 * C           [=====]
 * D                                             [====]
 *
 * BOUNDARIES  [======================================]
 */
 
$collection = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-05'),
    Period::make('2018-01-10', '2018-01-15'),
    Period::make('2018-01-20', '2018-01-25'),
    Period::make('2018-01-30', '2018-01-31')
);

$boundaries = $collection->boundaries();

集合的间隔:获取集合的所有间隔。

/*
 * A                   [====]
 * B                               [========]
 * C         [=====]
 * D                                             [====]
 *
 * GAPS             [=]      [====]          [==]
 */

$collection = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-05'),
    Period::make('2018-01-10', '2018-01-15'),
    Period::make('2018-01-20', '2018-01-25'),
    Period::make('2018-01-30', '2018-01-31')
);

$gaps = $collection->gaps();

重叠多个集合:返回集合之间的重叠。这意味着在集合之间进行AND操作,并在同一集合内进行OR操作。

/*
 * A            [=====]      [===========]
 * B            [=================]
 * C                [====================]
 *
 * OVERLAP          [=]      [====]
 */

$a = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-07'),
    Period::make('2018-01-15', '2018-01-25')
);

$b = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-20')
);

$c = new PeriodCollection(
    Period::make('2018-01-06', '2018-01-25')
);

$overlap = $a->overlap($b, $c);

使用 PeriodCollection

时间段集合是由多个时间段构建的

$collection = new PeriodCollection(
    Period::make('2018-01-01', '2018-01-02'),
    // …
);

可以直接遍历它们,并且您的IDE将识别其内容

$collection = new PeriodCollection(/* … */);

foreach ($collection as $period) {
    $period->…
}

您可以对它们进行解构

[$firstPeriod, $secondPeriod, $thirdPeriod] = $collection;

最后,从另一个集合中构建一个集合

$newCollection = new PeriodCollection(...$otherCollection);

精度

如果您想可靠地比较两个时间段,日期精度至关重要。以下示例

给定两个时间段:[2018-01-01, 2018-01-15][2018-01-15, 2018-01-31];它们是否重叠?

乍一看答案是“是”:它们在 2018-01-15 上重叠。但是,如果第一个时间段在 2018-01-15 10:00:00 结束,而第二个时间段在 2018-01-15 15:00:00 开始,情况又如何?现在它们不再重叠了!

这就是为什么此包要求您为每个时间段指定一个精度。只有具有相同精度的时期才能进行比较。

有关精度为什么如此重要的更深入的解释,请参阅 此处。在构建时间段时可以指定时间段精度

Period::make('2018-01-01', '2018-02-01', Precision::DAY);

默认精度设置为天。以下是可用的精度选项

Precision::YEAR
Precision::MONTH
Precision::DAY
Precision::HOUR
Precision::MINUTE
Precision::SECOND

边界

默认情况下,时间段比较使用包含边界。这意味着这两个时间段重叠

$a = Period::make('2018-01-01', '2018-02-01');
$b = Period::make('2018-02-01', '2018-02-28');

$a->overlapsWith($b); // true

时间段的长度也将包括两个边界

$a = Period::make('2018-01-01', '2018-01-31');

$a->length(); // 31

您可以覆盖边界行为

$a = Period::make('2018-01-01', '2018-02-01', null, Boundaries::EXCLUDE_END);
$b = Period::make('2018-02-01', '2018-02-28', null, Boundaries::EXCLUDE_END);

$a->overlapsWith($b); // false

边界排除有四种类型

Boundaries::EXCLUDE_NONE;
Boundaries::EXCLUDE_START;
Boundaries::EXCLUDE_END;
Boundaries::EXCLUDE_ALL;

兼容性

您可以从任何类型的 DateTime 对象(如 Carbon)构建一个 Period

Period::make(Carbon::make('2018-01-01'), Carbon::make('2018-01-02'));

请注意,一旦构建了时间段,对其的所有进一步操作都是不可变的。永远不会有改变输入日期的风险。

您可以使用指定的精度迭代 Period,就像迭代常规的 DatePeriod 一样

$datePeriod = Period::make(Carbon::make('2018-01-01'), Carbon::make('2018-01-31'));

foreach ($datePeriod as $date) {
    /** @var DateTimeImmutable $date */
    // 2018-01-01
    // 2018-01-02
    // ...
    // (31 iterations)
}

$timePeriod = Period::make(Carbon::make('2018-01-01 00:00:00'), Carbon::make('2018-01-01 23:59:59'), Precision::HOUR);

foreach ($timePeriod as $time) {
    /** @var DateTimeImmutable $time */
    // 2018-01-01 00:00:00
    // 2018-01-01 01:00:00
    // ...
    // (24 iterations)
}

可视化时间段

您还可以将一个或多个 Period 对象以及 PeriodCollection 对象可视化,以查看它们之间的关系

$visualizer = new Visualizer(["width" => 27]);

$visualizer->visualize([
    "A" => Period::make('2018-01-01', '2018-01-31'),
    "B" => Period::make('2018-02-10', '2018-02-20'),
    "C" => Period::make('2018-03-01', '2018-03-31'),
    "D" => Period::make('2018-01-20', '2018-03-10'),
    "OVERLAP" => new PeriodCollection(
        Period::make('2018-01-20', '2018-01-31'),
        Period::make('2018-02-10', '2018-02-20'),
        Period::make('2018-03-01', '2018-03-10')
    ),
]);

可视化将返回以下字符串

A          [========]
B                      [==]
C                           [========]
D               [==============]
OVERLAP         [===]  [==] [==]

可视化器在创建时有一个可配置的宽度,这将控制显示的时段时间范围

$visualizer = new Visualizer(["width" => 10]);

测试

composer test

变更日志

有关最近更改的更多信息,请参阅变更日志

贡献

有关详细信息,请参阅贡献指南

安全

如果您发现任何与安全相关的问题,请通过电子邮件freek@spatie.be联系,而不是使用问题跟踪器。

明信片软件

您可以使用这个包,但如果它进入了您的生产环境,我们非常感谢您从您的家乡寄给我们一张明信片,注明您正在使用我们的哪个(些)包。

我们的地址是:Spatie,Samberstraat 69D,2060 安特卫普,比利时。

我们将发布所有收到的明信片在我们的公司网站上

鸣谢

支持我们

Spatie 是一家位于比利时的安特卫普网站设计公司。您可以在我们的网站上找到我们所有开源项目的概述在这里

您的业务是否依赖于我们的贡献?联系并支持我们在 Patreon。所有承诺都将用于分配人力进行维护和新奇事物的开发。

许可证

MIT 许可证(MIT)。有关更多信息,请参阅许可证文件