Upstream cakephp/chronos state of the touched files before cdfce65279
This commit is contained in:
commit
760a3e72b1
2 files changed
+961
No files matched your search
@@ -0,0 +1,582 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||||
|
* @link https://cakephp.org CakePHP(tm) Project
|
||||||
|
* @license https://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cake\Chronos;
|
||||||
|
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use DateTimeInterface;
|
||||||
|
use DateTimeZone;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Stringable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-consistent-constructor
|
||||||
|
*/
|
||||||
|
class ChronosTime implements Stringable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected const TICKS_PER_MICROSECOND = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected const TICKS_PER_SECOND = 1000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected const TICKS_PER_MINUTE = self::TICKS_PER_SECOND * 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected const TICKS_PER_HOUR = self::TICKS_PER_MINUTE * 60;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
protected const TICKS_PER_DAY = self::TICKS_PER_HOUR * 24;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default format to use for __toString method.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public const DEFAULT_TO_STRING_FORMAT = 'H:i:s';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format to use for __toString method.
|
||||||
|
*/
|
||||||
|
protected static string $toStringFormat = self::DEFAULT_TO_STRING_FORMAT;
|
||||||
|
|
||||||
|
protected int $ticks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies time from onther instance or from time string in the format HH[:.]mm or HH[:.]mm[:.]ss.u.
|
||||||
|
*
|
||||||
|
* Defaults to server time.
|
||||||
|
*
|
||||||
|
* @param \Cake\Chronos\ChronosTime|\DateTimeInterface|string|null $time Time
|
||||||
|
* @param \DateTimeZone|string|null $timezone The timezone to use for now
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
ChronosTime|DateTimeInterface|string|null $time = null,
|
||||||
|
DateTimeZone|string|null $timezone = null,
|
||||||
|
) {
|
||||||
|
if ($time === null) {
|
||||||
|
$time = Chronos::getTestNow() ?? Chronos::now();
|
||||||
|
if ($timezone !== null) {
|
||||||
|
$time = $time->setTimezone($timezone);
|
||||||
|
}
|
||||||
|
$this->ticks = static::parseString($time->format('H:i:s.u'));
|
||||||
|
} elseif (is_string($time)) {
|
||||||
|
$this->ticks = static::parseString($time);
|
||||||
|
} elseif ($time instanceof ChronosTime) {
|
||||||
|
$this->ticks = $time->ticks;
|
||||||
|
} else {
|
||||||
|
$this->ticks = static::parseString($time->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies time from onther instance or from string in the format HH[:.]mm or HH[:.]mm[:.]ss.u
|
||||||
|
*
|
||||||
|
* Defaults to server time.
|
||||||
|
*
|
||||||
|
* @param \Cake\Chronos\ChronosTime|\DateTimeInterface|string $time Time
|
||||||
|
* @param \DateTimeZone|string|null $timezone The timezone to use for now
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function parse(
|
||||||
|
ChronosTime|DateTimeInterface|string|null $time = null,
|
||||||
|
DateTimeZone|string|null $timezone = null,
|
||||||
|
): static {
|
||||||
|
return new static($time, $timezone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $time Time string in the format HH[:.]mm or HH[:.]mm[:.]ss.u
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected static function parseString(string $time): int
|
||||||
|
{
|
||||||
|
if (!preg_match('/^\s*(\d{1,2})[:.](\d{1,2})(?|[:.](\d{1,2})[.](\d+)|[:.](\d{1,2}))?\s*$/', $time, $matches)) {
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
sprintf('Time string `%s` is not in expected format `HH[:.]mm` or `HH[:.]mm[:.]ss.u`.', $time),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$hours = (int)$matches[1];
|
||||||
|
$minutes = (int)$matches[2];
|
||||||
|
$seconds = (int)($matches[3] ?? 0);
|
||||||
|
$microseconds = (int)substr($matches[4] ?? '', 0, 6);
|
||||||
|
|
||||||
|
if ($hours > 24 || $minutes > 59 || $seconds > 59 || $microseconds > 999_999) {
|
||||||
|
throw new InvalidArgumentException(sprintf('Time string `%s` contains invalid values.', $time));
|
||||||
|
}
|
||||||
|
|
||||||
|
$ticks = $hours * self::TICKS_PER_HOUR;
|
||||||
|
$ticks += $minutes * self::TICKS_PER_MINUTE;
|
||||||
|
$ticks += $seconds * self::TICKS_PER_SECOND;
|
||||||
|
$ticks += $microseconds * self::TICKS_PER_MICROSECOND;
|
||||||
|
|
||||||
|
return $ticks % self::TICKS_PER_DAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns instance set to server time.
|
||||||
|
*
|
||||||
|
* @param \DateTimeZone|string|null $timezone The timezone to use for now
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function now(DateTimeZone|string|null $timezone = null): static
|
||||||
|
{
|
||||||
|
return new static(null, $timezone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns instance set to midnight.
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function midnight(): static
|
||||||
|
{
|
||||||
|
return new static('00:00:00');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns instance set to noon.
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function noon(): static
|
||||||
|
{
|
||||||
|
return new static('12:00:00');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns instance set to end of day - either
|
||||||
|
* 23:59:59 or 23:59:59.999999 if `$microseconds` is true
|
||||||
|
*
|
||||||
|
* @param bool $microseconds Whether to set microseconds or not
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public static function endOfDay(bool $microseconds = false): static
|
||||||
|
{
|
||||||
|
if ($microseconds) {
|
||||||
|
return new static('23:59:59.999999');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new static('23:59:59');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns clock microseconds.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getMicroseconds(): int
|
||||||
|
{
|
||||||
|
return intdiv($this->ticks % self::TICKS_PER_SECOND, self::TICKS_PER_MICROSECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets clock microseconds.
|
||||||
|
*
|
||||||
|
* @param int $microseconds Clock microseconds
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function setMicroseconds(int $microseconds): static
|
||||||
|
{
|
||||||
|
$baseTicks = $this->ticks - $this->ticks % self::TICKS_PER_SECOND;
|
||||||
|
$newTicks = static::mod($baseTicks + $microseconds * self::TICKS_PER_MICROSECOND, self::TICKS_PER_DAY);
|
||||||
|
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->ticks = $newTicks;
|
||||||
|
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return clock seconds.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getSeconds(): int
|
||||||
|
{
|
||||||
|
$secondsTicks = $this->ticks % self::TICKS_PER_MINUTE - $this->ticks % self::TICKS_PER_SECOND;
|
||||||
|
|
||||||
|
return intdiv($secondsTicks, self::TICKS_PER_SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set clock seconds.
|
||||||
|
*
|
||||||
|
* @param int $seconds Clock seconds
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function setSeconds(int $seconds): static
|
||||||
|
{
|
||||||
|
$baseTicks = $this->ticks - ($this->ticks % self::TICKS_PER_MINUTE - $this->ticks % self::TICKS_PER_SECOND);
|
||||||
|
$newTicks = static::mod($baseTicks + $seconds * self::TICKS_PER_SECOND, self::TICKS_PER_DAY);
|
||||||
|
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->ticks = $newTicks;
|
||||||
|
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns clock minutes.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getMinutes(): int
|
||||||
|
{
|
||||||
|
$minutesTicks = $this->ticks % self::TICKS_PER_HOUR - $this->ticks % self::TICKS_PER_MINUTE;
|
||||||
|
|
||||||
|
return intdiv($minutesTicks, self::TICKS_PER_MINUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set clock minutes.
|
||||||
|
*
|
||||||
|
* @param int $minutes Clock minutes
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function setMinutes(int $minutes): static
|
||||||
|
{
|
||||||
|
$baseTicks = $this->ticks - ($this->ticks % self::TICKS_PER_HOUR - $this->ticks % self::TICKS_PER_MINUTE);
|
||||||
|
$newTicks = static::mod($baseTicks + $minutes * self::TICKS_PER_MINUTE, self::TICKS_PER_DAY);
|
||||||
|
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->ticks = $newTicks;
|
||||||
|
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns clock hours.
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getHours(): int
|
||||||
|
{
|
||||||
|
$hoursInTicks = $this->ticks - $this->ticks % self::TICKS_PER_HOUR;
|
||||||
|
|
||||||
|
return intdiv($hoursInTicks, self::TICKS_PER_HOUR);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set clock hours.
|
||||||
|
*
|
||||||
|
* @param int $hours Clock hours
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function setHours(int $hours): static
|
||||||
|
{
|
||||||
|
$baseTicks = $this->ticks - ($this->ticks - $this->ticks % self::TICKS_PER_HOUR);
|
||||||
|
$newTicks = static::mod($baseTicks + $hours * self::TICKS_PER_HOUR, self::TICKS_PER_DAY);
|
||||||
|
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->ticks = $newTicks;
|
||||||
|
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets clock time.
|
||||||
|
*
|
||||||
|
* @param int $hours Clock hours
|
||||||
|
* @param int $minutes Clock minutes
|
||||||
|
* @param int $seconds Clock seconds
|
||||||
|
* @param int $microseconds Clock microseconds
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function setTime(int $hours = 0, int $minutes = 0, int $seconds = 0, int $microseconds = 0): static
|
||||||
|
{
|
||||||
|
$ticks = $hours * self::TICKS_PER_HOUR +
|
||||||
|
$minutes * self::TICKS_PER_MINUTE +
|
||||||
|
$seconds * self::TICKS_PER_SECOND +
|
||||||
|
$microseconds * self::TICKS_PER_MICROSECOND;
|
||||||
|
$ticks = static::mod($ticks, self::TICKS_PER_DAY);
|
||||||
|
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->ticks = $ticks;
|
||||||
|
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets time to the start of the current hour.
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function startOfHour(): static
|
||||||
|
{
|
||||||
|
$hourTicks = $this->ticks - $this->ticks % self::TICKS_PER_HOUR;
|
||||||
|
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->ticks = $hourTicks;
|
||||||
|
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets time to the end of the current hour.
|
||||||
|
*
|
||||||
|
* @return static
|
||||||
|
*/
|
||||||
|
public function endOfHour(): static
|
||||||
|
{
|
||||||
|
$hourTicks = $this->ticks - $this->ticks % self::TICKS_PER_HOUR;
|
||||||
|
$endTicks = $hourTicks + self::TICKS_PER_HOUR - 1;
|
||||||
|
|
||||||
|
$clone = clone $this;
|
||||||
|
$clone->ticks = $endTicks;
|
||||||
|
|
||||||
|
return $clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $a Left side
|
||||||
|
* @param int $a Right side
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
protected static function mod(int $a, int $b): int
|
||||||
|
{
|
||||||
|
if ($a < 0) {
|
||||||
|
return $a % $b + $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $a % $b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats string using the same syntax as `DateTimeImmutable::format()`.
|
||||||
|
*
|
||||||
|
* As this uses DateTimeImmutable::format() to format the string, non-time formatters
|
||||||
|
* will still be interpreted. Be sure to escape those characters first.
|
||||||
|
*
|
||||||
|
* @param string $format Format string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function format(string $format): string
|
||||||
|
{
|
||||||
|
return $this->toDateTimeImmutable()->format($format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset the format used to the default when converting to a string
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function resetToStringFormat(): void
|
||||||
|
{
|
||||||
|
static::setToStringFormat(static::DEFAULT_TO_STRING_FORMAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default format used when converting to a string
|
||||||
|
*
|
||||||
|
* @param string $format The format to use in future __toString() calls.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function setToStringFormat(string $format): void
|
||||||
|
{
|
||||||
|
static::$toStringFormat = $format;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format the instance as a string using the set format
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return $this->format(static::$toStringFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is equal to target time.
|
||||||
|
*
|
||||||
|
* @param \Cake\Chronos\ChronosTime $target Target time
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function equals(ChronosTime $target): bool
|
||||||
|
{
|
||||||
|
return $this->ticks === $target->ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is greater than target time.
|
||||||
|
*
|
||||||
|
* @param \Cake\Chronos\ChronosTime $target Target time
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function greaterThan(ChronosTime $target): bool
|
||||||
|
{
|
||||||
|
return $this->ticks > $target->ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is greater than or equal to target time.
|
||||||
|
*
|
||||||
|
* @param \Cake\Chronos\ChronosTime $target Target time
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function greaterThanOrEquals(ChronosTime $target): bool
|
||||||
|
{
|
||||||
|
return $this->ticks >= $target->ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is less than target time.
|
||||||
|
*
|
||||||
|
* @param \Cake\Chronos\ChronosTime $target Target time
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function lessThan(ChronosTime $target): bool
|
||||||
|
{
|
||||||
|
return $this->ticks < $target->ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is less than or equal to target time.
|
||||||
|
*
|
||||||
|
* @param \Cake\Chronos\ChronosTime $target Target time
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function lessThanOrEquals(ChronosTime $target): bool
|
||||||
|
{
|
||||||
|
return $this->ticks <= $target->ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is between time range.
|
||||||
|
*
|
||||||
|
* @param \Cake\Chronos\ChronosTime $start Start of target range
|
||||||
|
* @param \Cake\Chronos\ChronosTime $end End of target range
|
||||||
|
* @param bool $equals Whether to include the beginning and end of range
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function between(ChronosTime $start, ChronosTime $end, bool $equals = true): bool
|
||||||
|
{
|
||||||
|
if ($start->greaterThan($end)) {
|
||||||
|
[$start, $end] = [$end, $start];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($equals) {
|
||||||
|
return $this->greaterThanOrEquals($start) && $this->lessThanOrEquals($end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->greaterThan($start) && $this->lessThan($end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is start of day.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isStartOfDay(): bool
|
||||||
|
{
|
||||||
|
return $this->ticks === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is end of day.
|
||||||
|
*
|
||||||
|
* Compares against 23:59:59, ignoring microseconds.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isEndOfDay(): bool
|
||||||
|
{
|
||||||
|
$endOfDayTicks = 23 * self::TICKS_PER_HOUR
|
||||||
|
+ 59 * self::TICKS_PER_MINUTE
|
||||||
|
+ 59 * self::TICKS_PER_SECOND;
|
||||||
|
|
||||||
|
$ticksWithoutMicroseconds = $this->ticks - $this->ticks % self::TICKS_PER_SECOND;
|
||||||
|
|
||||||
|
return $ticksWithoutMicroseconds === $endOfDayTicks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is midnight.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isMidnight(): bool
|
||||||
|
{
|
||||||
|
return $this->isStartOfDay();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether time is midday.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isMidday(): bool
|
||||||
|
{
|
||||||
|
return $this->ticks === 12 * self::TICKS_PER_HOUR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an `DateTimeImmutable` instance set to this clock time.
|
||||||
|
*
|
||||||
|
* @param \DateTimeZone|string|null $timezone Time zone the DateTimeImmutable instance will be in
|
||||||
|
* @return \DateTimeImmutable
|
||||||
|
*/
|
||||||
|
public function toDateTimeImmutable(DateTimeZone|string|null $timezone = null): DateTimeImmutable
|
||||||
|
{
|
||||||
|
$timezone = is_string($timezone) ? new DateTimeZone($timezone) : $timezone;
|
||||||
|
|
||||||
|
return (new DateTimeImmutable(timezone: $timezone))->setTime(
|
||||||
|
$this->getHours(),
|
||||||
|
$this->getMinutes(),
|
||||||
|
$this->getSeconds(),
|
||||||
|
$this->getMicroseconds(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an `DateTimeImmutable` instance set to this clock time.
|
||||||
|
*
|
||||||
|
* Alias of `toDateTimeImmutable()`.
|
||||||
|
*
|
||||||
|
* @param \DateTimeZone|string|null $timezone Time zone the DateTimeImmutable instance will be in
|
||||||
|
* @return \DateTimeImmutable
|
||||||
|
*/
|
||||||
|
public function toNative(DateTimeZone|string|null $timezone = null): DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->toDateTimeImmutable($timezone);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the time as an associative array.
|
||||||
|
*
|
||||||
|
* @return array{hour: int, minute: int, second: int, microsecond: int}
|
||||||
|
*/
|
||||||
|
public function toArray(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'hour' => $this->getHours(),
|
||||||
|
'minute' => $this->getMinutes(),
|
||||||
|
'second' => $this->getSeconds(),
|
||||||
|
'microsecond' => $this->getMicroseconds(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,379 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||||
|
*
|
||||||
|
* Licensed under The MIT License
|
||||||
|
* Redistributions of files must retain the above copyright notice.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||||
|
* @link https://cakephp.org CakePHP(tm) Project
|
||||||
|
* @license https://www.opensource.org/licenses/mit-license.php MIT License
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Cake\Chronos\Test\TestCase;
|
||||||
|
|
||||||
|
use Cake\Chronos\Chronos;
|
||||||
|
use Cake\Chronos\ChronosTime;
|
||||||
|
use DateTimeImmutable;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
|
||||||
|
class ChronosTimeTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testConstructNow(): void
|
||||||
|
{
|
||||||
|
Chronos::setTestNow('2001-01-01 12:13:14.123456');
|
||||||
|
|
||||||
|
$t = new ChronosTime();
|
||||||
|
$this->assertSame('12:13:14.123456', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = new ChronosTime();
|
||||||
|
$this->assertSame('12:13:14.123456', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructFromString(): void
|
||||||
|
{
|
||||||
|
$t = new ChronosTime('12:13');
|
||||||
|
$this->assertSame('12:13:00.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = new ChronosTime('0.0.0.0');
|
||||||
|
$this->assertSame('00:00:00.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = new ChronosTime('1:01:1.000001');
|
||||||
|
$this->assertSame('01:01:01.000001', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = new ChronosTime('23:59.59.999999');
|
||||||
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = new ChronosTime('23:59.59.9999991');
|
||||||
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = new ChronosTime('24:59.59.9999991');
|
||||||
|
$this->assertSame('00:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructFromInstance(): void
|
||||||
|
{
|
||||||
|
$t = new ChronosTime(new DateTimeImmutable('23:59:59.999999'));
|
||||||
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = new ChronosTime(new Chronos('23:59:59.999999'));
|
||||||
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = new ChronosTime(new ChronosTime(new Chronos('23:59:59.999999')));
|
||||||
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructInvalid(): void
|
||||||
|
{
|
||||||
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
new ChronosTime('tomorrow');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructIncomplete(): void
|
||||||
|
{
|
||||||
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
new ChronosTime('23');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructInvalidHours(): void
|
||||||
|
{
|
||||||
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
new ChronosTime('25:00:00');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructInvalidMinutes(): void
|
||||||
|
{
|
||||||
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
new ChronosTime('23:60:00');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testConstructInvalidSeconds(): void
|
||||||
|
{
|
||||||
|
$this->expectException(InvalidArgumentException::class);
|
||||||
|
new ChronosTime('23:59:60');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testParse(): void
|
||||||
|
{
|
||||||
|
$t = ChronosTime::parse('23:59:59.999999');
|
||||||
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
Chronos::setTestNow(new Chronos('2001-01-01 12:13:14.123456', 'America/Chicago'));
|
||||||
|
$t = ChronosTime::parse(null, 'America/New_York');
|
||||||
|
$this->assertSame('13:13:14.123456', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNow(): void
|
||||||
|
{
|
||||||
|
Chronos::setTestNow(new Chronos('2001-01-01 12:13:14.123456', 'America/Chicago'));
|
||||||
|
|
||||||
|
$t = ChronosTime::now();
|
||||||
|
$this->assertSame('12:13:14.123456', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::now('America/New_York');
|
||||||
|
$this->assertSame('13:13:14.123456', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testMidnight(): void
|
||||||
|
{
|
||||||
|
$t = ChronosTime::midnight();
|
||||||
|
$this->assertSame('00:00:00.000000', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNoon(): void
|
||||||
|
{
|
||||||
|
$t = ChronosTime::noon();
|
||||||
|
$this->assertSame('12:00:00.000000', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEndOfDay(): void
|
||||||
|
{
|
||||||
|
$t = ChronosTime::endOfDay();
|
||||||
|
$this->assertSame('23:59:59.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::endOfDay(microseconds: true);
|
||||||
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetters(): void
|
||||||
|
{
|
||||||
|
$t = ChronosTime::midnight()->setHours(24);
|
||||||
|
$this->assertSame('00:00:00.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setHours(-1);
|
||||||
|
$this->assertSame('23:00:00.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setMinutes(60);
|
||||||
|
$this->assertSame('01:00:00.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setMinutes(-1);
|
||||||
|
$this->assertSame('23:59:00.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setSeconds(60);
|
||||||
|
$this->assertSame('00:01:00.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setSeconds(-1);
|
||||||
|
$this->assertSame('23:59:59.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setMicroseconds(1_000_000);
|
||||||
|
$this->assertSame('00:00:01.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setMicroseconds(-1);
|
||||||
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setHours(25)->setMinutes(120)->setSeconds(120)->setMicroseconds(2_000_001);
|
||||||
|
$this->assertSame('03:02:02.000001', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGetters(): void
|
||||||
|
{
|
||||||
|
$t = ChronosTime::midnight()->setTime(-1, -1, -1, -1);
|
||||||
|
$this->assertSame(22, $t->getHours());
|
||||||
|
$this->assertSame(58, $t->getMinutes());
|
||||||
|
$this->assertSame(58, $t->getSeconds());
|
||||||
|
$this->assertSame(999_999, $t->getMicroseconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSetTime(): void
|
||||||
|
{
|
||||||
|
$t = new ChronosTime();
|
||||||
|
$new = $t->setTime();
|
||||||
|
$this->assertNotSame($new, $t);
|
||||||
|
$this->assertSame('00:00:00.000000', $new->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setTime(24, 120, 120, 1_000_000);
|
||||||
|
$this->assertSame('02:02:01.000000', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setTime(-1, -1, -1, -1);
|
||||||
|
$this->assertSame('22:58:58.999999', $t->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::midnight()->setTime(-1, 120, -1, 1_000_001);
|
||||||
|
$this->assertSame('01:00:00.000001', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStartOfHour(): void
|
||||||
|
{
|
||||||
|
$t = ChronosTime::parse('12:30:45.123456');
|
||||||
|
$start = $t->startOfHour();
|
||||||
|
|
||||||
|
$this->assertNotSame($t, $start);
|
||||||
|
$this->assertSame('12:00:00.000000', $start->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::parse('00:59:59.999999');
|
||||||
|
$this->assertSame('00:00:00.000000', $t->startOfHour()->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::parse('23:01:01');
|
||||||
|
$this->assertSame('23:00:00.000000', $t->startOfHour()->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testEndOfHour(): void
|
||||||
|
{
|
||||||
|
$t = ChronosTime::parse('12:30:45.123456');
|
||||||
|
$end = $t->endOfHour();
|
||||||
|
|
||||||
|
$this->assertNotSame($t, $end);
|
||||||
|
$this->assertSame('12:59:59.999999', $end->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::parse('00:00:00');
|
||||||
|
$this->assertSame('00:59:59.999999', $t->endOfHour()->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$t = ChronosTime::parse('23:30:00');
|
||||||
|
$this->assertSame('23:59:59.999999', $t->endOfHour()->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormat(): void
|
||||||
|
{
|
||||||
|
$t = new ChronosTime('23:59:59.999999');
|
||||||
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testComparisons(): void
|
||||||
|
{
|
||||||
|
$t1 = ChronosTime::parse('00:00:00');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00');
|
||||||
|
$this->assertTrue($t1->equals($t2));
|
||||||
|
$this->assertTrue($t1->greaterThanOrEquals($t2));
|
||||||
|
$this->assertTrue($t1->lessThanOrEquals($t2));
|
||||||
|
$this->assertFalse($t1->greaterThan($t2));
|
||||||
|
$this->assertFalse($t1->lessThan($t2));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00')->setHours(24);
|
||||||
|
$this->assertTrue($t1->equals($t2));
|
||||||
|
$this->assertTrue($t1->greaterThanOrEquals($t2));
|
||||||
|
$this->assertTrue($t1->lessThanOrEquals($t2));
|
||||||
|
$this->assertFalse($t1->greaterThan($t2));
|
||||||
|
$this->assertFalse($t1->lessThan($t2));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00.000001');
|
||||||
|
$this->assertTrue($t1->lessThan($t2));
|
||||||
|
$this->assertTrue($t1->lessThanOrEquals($t2));
|
||||||
|
$this->assertFalse($t1->equals($t2));
|
||||||
|
$this->assertFalse($t1->greaterThan($t2));
|
||||||
|
$this->assertFalse($t1->greaterThanOrEquals($t2));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00');
|
||||||
|
$this->assertTrue($t1->greaterThan($t2));
|
||||||
|
$this->assertTrue($t1->greaterThanOrEquals($t2));
|
||||||
|
$this->assertFalse($t1->equals($t2));
|
||||||
|
$this->assertFalse($t1->lessThan($t2));
|
||||||
|
$this->assertFalse($t1->lessThanOrEquals($t2));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
||||||
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
||||||
|
$this->assertTrue($t2->between($t1, $t3));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
||||||
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
||||||
|
$this->assertTrue($t2->between($t1, $t3, false));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
||||||
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
||||||
|
$this->assertTrue($t1->between($t1, $t3, true));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
||||||
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
||||||
|
$this->assertFalse($t1->between($t1, $t3, false));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
||||||
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
||||||
|
$this->assertTrue($t2->between($t3, $t1, false));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
||||||
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
||||||
|
$this->assertFalse($t1->between($t2, $t3));
|
||||||
|
|
||||||
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
||||||
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
||||||
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
||||||
|
$this->assertFalse($t3->between($t1, $t2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsStartOfDay(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(ChronosTime::parse('00:00:00')->isStartOfDay());
|
||||||
|
$this->assertTrue(ChronosTime::midnight()->isStartOfDay());
|
||||||
|
$this->assertFalse(ChronosTime::parse('00:00:00.000001')->isStartOfDay());
|
||||||
|
$this->assertFalse(ChronosTime::parse('00:00:01')->isStartOfDay());
|
||||||
|
$this->assertFalse(ChronosTime::noon()->isStartOfDay());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsEndOfDay(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(ChronosTime::parse('23:59:59')->isEndOfDay());
|
||||||
|
$this->assertTrue(ChronosTime::endOfDay()->isEndOfDay());
|
||||||
|
$this->assertTrue(ChronosTime::parse('23:59:59.999999')->isEndOfDay());
|
||||||
|
$this->assertFalse(ChronosTime::parse('23:59:58')->isEndOfDay());
|
||||||
|
$this->assertFalse(ChronosTime::midnight()->isEndOfDay());
|
||||||
|
$this->assertFalse(ChronosTime::noon()->isEndOfDay());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsMidnight(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(ChronosTime::midnight()->isMidnight());
|
||||||
|
$this->assertTrue(ChronosTime::parse('00:00:00')->isMidnight());
|
||||||
|
$this->assertFalse(ChronosTime::parse('00:00:00.000001')->isMidnight());
|
||||||
|
$this->assertFalse(ChronosTime::noon()->isMidnight());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testIsMidday(): void
|
||||||
|
{
|
||||||
|
$this->assertTrue(ChronosTime::noon()->isMidday());
|
||||||
|
$this->assertTrue(ChronosTime::parse('12:00:00')->isMidday());
|
||||||
|
$this->assertFalse(ChronosTime::parse('12:00:00.000001')->isMidday());
|
||||||
|
$this->assertFalse(ChronosTime::parse('12:00:01')->isMidday());
|
||||||
|
$this->assertFalse(ChronosTime::midnight()->isMidday());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testToDateTimeImmutable(): void
|
||||||
|
{
|
||||||
|
$time = ChronosTime::parse('23:59:59.999999');
|
||||||
|
$native = $time->toDateTimeImmutable();
|
||||||
|
$this->assertSame('23:59:59.999999', $native->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$native = $time->toNative();
|
||||||
|
$this->assertSame('23:59:59.999999', $native->format('H:i:s.u'));
|
||||||
|
|
||||||
|
$native = $time->toDateTimeImmutable('Asia/Tokyo');
|
||||||
|
$this->assertSame('23:59:59.999999', $native->format('H:i:s.u'));
|
||||||
|
$this->assertSame('Asia/Tokyo', $native->getTimezone()->getName());
|
||||||
|
|
||||||
|
$native = $time->toNative('Asia/Tokyo');
|
||||||
|
$this->assertSame('23:59:59.999999', $native->format('H:i:s.u'));
|
||||||
|
$this->assertSame('Asia/Tokyo', $native->getTimezone()->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testToString(): void
|
||||||
|
{
|
||||||
|
$t = new ChronosTime('12:13:14.123456');
|
||||||
|
$this->assertSame('12:13:14', (string)$t);
|
||||||
|
|
||||||
|
ChronosTime::setToStringFormat('H:i:s.u');
|
||||||
|
$this->assertSame('12:13:14.123456', (string)$t);
|
||||||
|
|
||||||
|
ChronosTime::resetToStringFormat();
|
||||||
|
$this->assertSame('12:13:14', (string)$t);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testToArray(): void
|
||||||
|
{
|
||||||
|
$t = new ChronosTime('12:30:45.123456');
|
||||||
|
$array = $t->toArray();
|
||||||
|
|
||||||
|
$this->assertSame(12, $array['hour']);
|
||||||
|
$this->assertSame(30, $array['minute']);
|
||||||
|
$this->assertSame(45, $array['second']);
|
||||||
|
$this->assertSame(123456, $array['microsecond']);
|
||||||
|
$this->assertCount(4, $array);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user