flying / 日期
支持调整当前时间的日期生成器
Requires
- php: ^8.1
Requires (Dev)
- phpunit/phpunit: ^10.0
README
DateTime
生成器,支持调整当前时间。
目的
该库的主要目的是在无需调整其代码的情况下(除了切换到使用库本身来创建应用程序中的日期外),帮助测试应用程序在时间敏感场景中。
对于单元测试,测试时间敏感场景相对简单,但对于功能测试来说则要困难得多。当然,有许多解决方案
slope-it/clock-mock
看起来很棒,但它依赖于 PHP 扩展。lcobucci/clock
非常受欢迎,但它需要依赖其自己的Clock
对象,这将需要更新函数和方法的签名以及类属性的类型。- Symfony 的 PHPUnit 桥接器中的时钟模拟 仅模拟与时间相关的函数,而不是基于
\DateTimeInterface
的类。
该库旨在将所需的更改数量保持与其他解决方案相当的水平。下面列出了所需的 代码更新,基本上仅限于将 \DateTime
构造函数方法更改为调用不同类的静态方法。它还简化了测试用例,当代码执行花费了一些时间,并且这种时间变化被以某种方式使用时(例如性能跟踪)。
一般信息
整个 API 都以单个 \Flying\Date\Date
类的形式公开。API 提供了一组静态方法,以确保可以从代码的任何部分访问它们,而无需引入任何类型的附加依赖。
生成的日期对象限于 \DateTimeImmutable
。来自 PSR-20 的即将推出的 ClockInterface
也仅提供不可变日期。
主要 API 方法 - now
和 from
返回 \DateTimeImmutable
实例,可以通过使用 adjust
方法提供时间偏移量来调整其值,以相对于实际当前时间。
在应用程序中使用
为了利用时间偏移功能,需要更新创建新的 \DateTime
或 \DateTimeInterval
对象的代码部分。
要获取 \DateTime
对象,需要将获取到的 \DateTimeInterval
对象转换为 \DateTime
对象
$mutableDateTime = \DateTime::createFromImmutable($immutableDateTime);
创建当前时间和默认时区的对象
- 之前:
new \DateTimeImmutable()
- 之后:
\Flying\Date\Date::now()
创建任意时间和/或时区的对象
从字符串创建
- 之前:
new \DateTimeImmutable('2022-08-01', new \DateTimeZone('UTC'))
- 之后:
\Flying\Date\Date::from('2022-08-01', 'UTC')
从格式创建
- 之前:
\DateTimeImmutable::createFromFormat(\DateTimeInterface::ATOM, 2022-08-01T12:23:34Z')
- 之后:
\Flying\Date\Date::fromFormat(\DateTimeInterface::ATOM, '2022-08-01T12:23:34Z')
在测试中使用
为了测试在不同时间点上的应用程序行为,需要做两件事
- 通过调用
\Flying\Date\Date::adjust()
来提供日期调整值,传入代表新目标日期的\DateTimeInterface
或定义相对于当前时间的时移的\DateInterval
。不带参数调用adjust()
将移除日期调整。 - 通过调用
\Flying\Date\Date::allowAdjustment(true)
来启用日期调整。
重要! 在您的应用程序代码中绝不应该使用日期调整,仅限在测试中使用!在应用程序代码中使用它可能会导致意外的行为!
即使在测试中,也应该将此功能的限制在需要日期调整功能的测试中,并在测试后禁用它。
使用属性简化配置
为了简化库的使用并确保在测试中正确启用/禁用日期调整功能,可以使用 AdjustableDate
PHP 属性。
要启用属性,您需要编辑您的 PHPUnit 配置(phpunit.xml
)并添加测试扩展。
<extensions> <!-- ... other extensions ... --> <bootstrap class="Flying\Date\PHPUnit\Extension\DateExtension"/> </extensions>
然后,对于需要使用可调整日期功能的测试类或(最好是)测试方法,您需要添加 #[AdjustableDate]
属性。它接受可选的配置参数。
bool $enabled
- 用于控制可调整日期功能的默认状态(默认为true
)\DateTimeZone|string|null $timezone
- 用于定义默认时区
关于日期调整中的微秒的说明
对于由 now
和 from
方法生成的时移间隔和调整日期,结果日期的微秒部分将被强制设置为零。未进行日期调整生成的日期对象不受影响。
这应该是安全的,因为日期调整仅用于测试。在小于一秒的间隔上进行时间位移的微秒精度测试,使用 usleep()
更可靠。对于更大的间隔,包括微秒可能会引入整个秒的差异,这可能导致测试偶尔失败。
API 方法
now
签名
\Flying\Date\Date::now(): \DateTimeImmutable
提供当前日期的日期对象。使用通过 setTimezone()
定义或 PHP 默认时区的时区。
如果启用了日期调整,则返回的日期将根据定义的时移间隔进行调整。
from
签名
\Flying\Date\Date::from(\DateTimeInterface|\DateInterval|string $date, \DateTimeZone|string|null $timezone = null): \DateTimeImmutable
根据提供的日期和时区信息创建给定任意时间点的日期对象。使用给定的时区,通过 setTimezone()
定义的时区或 PHP 默认时区。允许将有效时区名称作为时区值传递。
如果启用了日期调整,则返回的日期将根据定义的时移间隔进行调整。
fromFormat
签名
\Flying\Date\Date::fromFormat(string $format, string $datetime, \DateTimeZone|string|null $timezone = null): \DateTimeImmutable|bool
使用给定的格式和时区信息创建给定日期字符串的日期对象。使用给定的时区,通过 setTimezone()
定义的时区或 PHP 默认时区。允许将有效时区名称作为时区值传递。
如果启用了日期调整,则返回的日期将根据定义的时移间隔进行调整。
getTimezone
签名
\Flying\Date\Date::getTimezone(): \DateTimeZone
获取用于创建日期对象的时区。
setTimezone
签名
\Flying\Date\Date::setTimezone(\DateTimeZone|string|null $timezone = null): void
定义(或重置)用于创建日期对象的时区。允许将有效时区名称作为值传递。
adjust
签名
\Flying\Date\Date::adjust(\DateInterval|\DateTimeInterface|string|null $adjustment = null): void
定义(或重置)用于在创建日期对象时调整日期的时移间隔。允许通过传递 \DateTimeInterface
实例定义绝对时间点或通过传递 \DateInterval
对象定义相对于当前时间点的时移。
getAdjustment
签名
\Flying\Date\Date::getAdjustment(): ?\DateInterval
返回当前定义的时间偏移间隔,该间隔用于在创建日期对象时调整日期。
allowAdjustment
签名
\Flying\Date\Date::allowAdjustment(bool $status): void
允许控制日期调整功能的状态。
isAdjustmentAllowed
签名
\Flying\Date\Date::isAdjustmentAllowed(): bool
返回日期调整功能当前的状态。
许可证
该库遵从MIT许可证。