数字工匠 / 日期时间精度
一个用于通过值对象进行更精确日期和时间处理的Symfony扩展包
Requires
- php: 8.2.*|8.3.*
- doctrine/dbal: ^3.4.0
- symfony/framework-bundle: ^6.4|^7.0
- symfony/serializer: ^6.4|^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.40
- infection/infection: ^0.26.15
- phpunit/phpunit: ^10.5
- vimeo/psalm: ^5.16
This package is auto-updated.
Last update: 2024-09-23 07:59:52 UTC
README
PHP SPL 中用于处理日期和时间的唯一类是 DateTime
(及其不可变的对应物)。它表示特定时区(无论该时区/偏移量是否显式定义)中的特定时刻。遗憾的是,日期很复杂,不仅仅是一个时刻。例如,有
- 一天中的时间,这在每一天都很相关(例如,营业时间)。
- 特定的日期(例如圣诞节)不受时区限制。
- 日期时间的子集,如时间、日期、月份或年份。
基本上,每次你谈论的不是特定时刻时,DateTime
类包含的信息不仅仅是所需的信息,甚至可能是误导性的信息。
这个扩展包/包试图通过引入一个用于时间点的包装值对象 Moment
以及更精确的子集值对象 Time
、Date
、Month
和 Year
来解决这个问题。它只是对 DateTime
的一个薄层包装,并在内部使用它进行所有修改和比较。这样你就不必确保你的 DateTime
是
- 在特定日期来比较时间。
- 在午夜来比较日期。
- 在一个月的第一天来比较月份。
- 在一年中的第一天来比较年份。
此外,该包提供了一个简化的方法,可以在 UTC
下运行系统,但仍在对相关时区进行修改。内部 DateTime
总是在 UTC
,并且只在内部转换为相关时区进行修改。这样你就可以确保在相关时区进行夏令时和冬令时的转换时不会遗漏或收到一个小时。
这个Symfony扩展包包括用于自动规范化和解规范的Symfony规范化器以及用于直接在数据库中存储对象的Doctrine类型。
作为应用的核心部分,它经过了彻底的测试(包括变异测试)。目前,这个存储库中超过80%的代码行是测试代码。
安装和配置
通过composer安装包
composer require digital-craftsman/date-time-precision
⚠️ 这个扩展包可以在生产中使用(并且正在使用),但尚未达到1.0版本。因此,在次要版本之间会有破坏性变更。我建议你只使用当前次要版本要求扩展包,如
composer require digital-craftsman/date-time-precision:0.10.*
。破坏性变更在发行说明和 变更日志 中描述。更新在 升级指南 中描述。
何时需要它?
基本上,每当您使用 DateTime
对象来处理除了单一时刻之外的其他内容时。
在这些情况下存储更多信息只会导致更多问题,例如“存储月份时,我们是存储月份的第一天午夜,如果是这样,在哪个时区?”因此增加了复杂性。此外,您需要变异或减少时间点以便进行比较。使用该包将变得非常简单
if ($now->isBeforeInTimeZone($facility->openFrom, $facilityTimeZone)) { throw new FacilityIsNotOpenYet(); }
$now
是一个 Moment
(在UTC中),$facility->openFrom
是一个 Time
(在设施所在的时区中)。
设计理念是您的系统可以在UTC
时区运行,所有时刻都在UTC
时区。但所有具有隐含时区的值,如日期或一天中的时间,都只存储所需的数据。这样我们就消除了可能导致更多错误的数据。通过精确的值对象和特定的比较函数,代码的可读性比以前更高。
if ($now->isBeforeInTimeZone($facility->earliestDayOfBooking)) { throw new BookingNotPossibleYet(); }
$now
是一个(UTC时区的)Moment
,而$facility->earliestDayOfBooking
是一个(设施时区的)Date
。之前用于时间比较的相同方法isBeforeInTimeZone
在这里也使用。根据第二个参数的类型,比较是在时刻的相关部分上进行的。
修改操作也是相同的。
$bookingsAllowedFrom = $now->modifyInTimeZone('+ 7 days', $facilityTimeZone);
结果$bookingsAllowedFrom
仍然是一个具有UTC
时区的日期时间,但修改是在相关时区进行的。
集成
为了最佳代码可读性,当您在谈论时间点时,最好使用包提供的Moment
作为\DateTime
或\DateTimeImmutable
的完整替代,而其他值对象用于其他部分。该包为Moment
及其所有部分提供了规范化和Doctrine类型。
Doctrine类型使用以下类型名称自动注册到捆绑包中
dtp_moment
dtp_time
dtp_date
dtp_month
dtp_year
该包还包含一个由接口Clock
组成的时钟组件,具有两个实现:SystemClock
(适用于通用用途)和FrozenClock
(适用于测试)。SystemClock
将自动绑定到Clock
,并在测试环境中自动替换为FrozenClock
。
设计
不可变性
Moment
及其部分的全部修改都是不可变的。
贡献
本地设置使用Docker构建,并通过Make命令进行控制。运行make
以查看所有可用命令及其功能。
在创建PR或推送任何代码之前,请运行make verify
以在本地运行所有测试和验证。