pwm/datetime-period

用于处理时间间隔的datetime period类型的实现

1.0.2 2018-09-25 12:02 UTC

This package is auto-updated.

Last update: 2024-09-29 02:02:28 UTC


README

Build Status codecov Maintainability Test Coverage License: MIT

用于处理时间间隔的datetime period类型的实现。该库包括由Allen的时间间隔代数定义的间隔关系全集。有关更多信息,请参阅“用法”和“工作原理”段落。

目录

需求

PHP 7.1+

安装

$ composer require pwm/datetime-period

用法

创建
$start = new DateTimeImmutable('2010-10-10T10:10:10+00:00');
$end = new DateTimeImmutable('2011-11-11T11:11:11+00:00');
$period = new DateTimePeriod($start, $end);

// Start and end instants (see the definition of instant under "How it works")
$start = $period->getStart(); // DateTimeImmutable('2010-10-10T10:10:10+00:00')
$end = $period->getEnd(); // DateTimeImmutable('2011-11-11T11:11:11+00:00')
限制
// Throws TimeZoneMismatch exception
new DateTimePeriod(
    new DateTimeImmutable('2017-10-10T10:10:10+02:00'),
    new DateTimeImmutable('2017-10-10T10:10:10-05:00')
);

// Throws NegativeDateTimePeriod exception
new DateTimePeriod(
    new DateTimeImmutable('+1 day'),
    new DateTimeImmutable('-1 day')
);
两个时间段之间的全部关系集
$a = new DateTimePeriod(new DateTimeImmutable('...'), new DateTimeImmutable('...'));
$b = new DateTimePeriod(new DateTimeImmutable('...'), new DateTimeImmutable('...'));

// |--a--|
//          |--b--|
$a->precedes($b);

// |--a--|
//       |--b--|
$a->meets($b);

// |--a--|
//    |--b--|
$a->overlaps($b);

// |----a----|
//     |--b--|
$a->finishedBy($b);

// |----a----|
//   |--b--|
$a->contains($b);

// |--a--|
// |----b----|
$a->starts($b);

// |--a--|
// |--b--|
$a->equals($b);

//  |----a----|
//  |--b--|
$a->startedBy($b);

//   |--a--|
// |----b----|
$a->during($b);

//     |--a--|
// |----b----|
$a->finishes($b);

//    |--a--|
// |--b--|
$a->overlappedBy($b);

//       |--a--|
// |--b--|
$a->metBy($b);

//          |--a--|
// |--b--|
$a->precededBy($b);
可扩展性

DateTimePeriod对象本身是不可变的,这意味着一旦创建,就无法更改对象的状态,即其属性的值。然而,属性已被定义为protected,以便在需要时可以在项目中继承该类型。

处理不同的粒度

以下两个时间段在按小时粒度的时间线上相交,但在更精细的按分钟粒度的时间线上不相交。

$aStart = '2017-01-01T12:12:09.829462+00:00';
$aEnd   = '2017-01-01T14:23:34.534678+00:00';
$bStart = '2017-01-01T14:41:57.657388+00:00';
$bEnd   = '2017-01-01T16:19:03.412832+00:00';

$hourGranule = 'Y-m-d\TH';
$a = new DateTimePeriod(
    DateTimeImmutable::createFromFormat($hourGranule, (new DateTimeImmutable($aStart))->format($hourGranule)),
    DateTimeImmutable::createFromFormat($hourGranule, (new DateTimeImmutable($aEnd))->format($hourGranule))
);
$b = new DateTimePeriod(
    DateTimeImmutable::createFromFormat($hourGranule, (new DateTimeImmutable($bStart))->format($hourGranule)),
    DateTimeImmutable::createFromFormat($hourGranule, (new DateTimeImmutable($bEnd))->format($hourGranule))
);
assert($a->meets($b) === true); // a meets b by the hour granule

$minuteGranule = 'Y-m-d\TH:i';
$a = new DateTimePeriod(
    DateTimeImmutable::createFromFormat($minuteGranule, (new DateTimeImmutable($aStart))->format($minuteGranule)),
    DateTimeImmutable::createFromFormat($minuteGranule, (new DateTimeImmutable($aEnd))->format($minuteGranule))
);

$b = new DateTimePeriod(
    DateTimeImmutable::createFromFormat($minuteGranule, (new DateTimeImmutable($bStart))->format($minuteGranule)),
    DateTimeImmutable::createFromFormat($minuteGranule, (new DateTimeImmutable($bEnd))->format($minuteGranule))
);
assert($a->meets($b) === false); // a does not meet b by the minute granule
杂项

这是一个可以节省您时间的小助手方法列表,当您需要它们的特定功能时。

  1. 获取时间段内的天数
$start = new DateTimeImmutable('2016-01-01T11:11:11+00:00');
$end = new DateTimeImmutable('2018-01-01T11:11:11+00:00');
$period = new DateTimePeriod($start, $end);
assert(366 + 365 === $period->getNumberOfDays()); // 2016 was a leap year

工作原理

为了能够讨论时间段,首先让我们就以下定义达成一致

定义

1. 瞬时

时间线上的一个锚点,即离散点。最基本的时态类型。一个“真实”的时间瞬时是理论上的,就像几何线上的一个点。然而,瞬时的表示总是具有一个持续时间,称为粒度。因此,我们可以使用不同粒度的各种离散时间线来表示相同的瞬时。例如,“2017-10-10”和“2017-10-10 10:10:10”可以表示相同的瞬时。

2. 间隔

时间线上的一个未锚定、有向的部分。未锚定意味着它没有与时间线的绝对关系。例如,“2周”或“1天,2小时和3分钟”。有向意味着可以说“-3天”是完全有效的。

3. 时间段

时间线上的一个锚定间隔。有几种可能的表示方式,最常见的是具有相同粒度的两个有序瞬时的对。根据表示方式,时间段的时间间隔可以在其开始和结束处都打开或关闭。一种常见的方式是使用闭开区间,即[start, end),这有助于简化计算。例如,时间段["2017-10-10", "2017-11-11")包括瞬时"2017-10-10",但不包括瞬时"2017-11-11"。

我们已经到达了时间段的定义。现在继续...

关系

在时间段上定义关系有些复杂,因为不存在全序。1983年,James F. Allen撰写了一篇论文,在其中他定义了13个联合穷尽且相互排斥的二进制关系,这意味着任何两个间隔都正好有一种关系。您可以在“用法”部分中看到上述13种关系中的每一种。这些关系及其操作形成了所谓的Allen的时间间隔代数

复杂性

处理日历时间很困难,有很多特殊性。这反过来意味着处理派生实体,如DateTimePeriod,也很复杂。

以下是一个涉及时区的示例

如果您尝试从2018-03-26T08:00:00开始,到2019-03-26T08:00:00结束,创建一个为期一年的DateTimePeriod,对于时区Europe/London,则会因为UTCOffsetMismatch异常而失败。这是因为对于开始时刻,时区Europe/London等于BST(等于UTC+01:00),而对于结束时刻,Europe/London等于GMT(等于UTC+00:00)。这是因为夏令时在不同年份的日期不同。

如果您使用UTC偏移量创建上述时间段,即从2018-03-26T08:00:00+01:002019-03-26T08:00:00+01:00,则不会抛出UTCOffsetMismatch异常,但是结束时刻将不是一个有效的Europe/London日期时间,因此您需要从您的UTC+01:00偏移量计算出正确的Europe/London时间。

提供的DateTimePeriod::getUtcOffset()函数可以通过将您的当前时区映射到其UTC偏移量来帮助解决这个问题。

测试

$ vendor/bin/phpunit
$ composer phpcs
$ composer phpstan
$ composer infection

变更日志

点击此处

待办事项

  • 为UTC偏移量/时区问题找到一个更用户友好的解决方案。

许可证

麻省理工学院(MIT)