From d3c7b8f496430f2afc3492f0c300f1effd47c536 Mon Sep 17 00:00:00 2001 From: flashwave Date: Wed, 31 Jul 2024 20:53:06 +0000 Subject: [PATCH] Removed dodgy DateTimeInterface wrappers, use Carbon or the XDateTime utils instead. --- TODO.md | 2 - VERSION | 2 +- src/Data/Migration/DbMigrationManager.php | 18 +- src/Data/Migration/FsDbMigrationInfo.php | 3 +- src/DateTime.php | 659 ------------------- src/Http/Headers/DateHeader.php | 18 +- src/Http/Headers/IfModifiedSinceHeader.php | 22 +- src/Http/Headers/IfRangeHeader.php | 23 +- src/Http/Headers/IfUnmodifiedSinceHeader.php | 22 +- src/Http/Headers/LastModifiedHeader.php | 18 +- src/Http/Headers/OriginHeader.php | 3 +- src/Http/HttpResponseBuilder.php | 16 +- src/TimeSpan.php | 526 --------------- src/TimeZoneInfo.php | 213 ------ src/XDateTime.php | 103 +++ tests/DateTimeTest.php | 386 ----------- tests/XDateTimeTest.php | 66 ++ tools/update-headers.php | 2 +- tools/update-version.php | 2 +- 19 files changed, 255 insertions(+), 1849 deletions(-) delete mode 100644 src/DateTime.php delete mode 100644 src/TimeSpan.php delete mode 100644 src/TimeZoneInfo.php create mode 100644 src/XDateTime.php delete mode 100644 tests/DateTimeTest.php create mode 100644 tests/XDateTimeTest.php diff --git a/TODO.md b/TODO.md index da7d660..890de2b 100644 --- a/TODO.md +++ b/TODO.md @@ -13,8 +13,6 @@ - Add RSS/Atom feed construction utilities. - - Verify structure of the DateTime types. - ## Low Prio - Get guides working on phpdoc. diff --git a/VERSION b/VERSION index 667dcb6..8f4fcc5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2407.311946 +0.2407.312052 diff --git a/src/Data/Migration/DbMigrationManager.php b/src/Data/Migration/DbMigrationManager.php index a59c464..412bded 100644 --- a/src/Data/Migration/DbMigrationManager.php +++ b/src/Data/Migration/DbMigrationManager.php @@ -1,14 +1,15 @@ format(DateTimeInterface::ATOM); + $dateTime = XDateTime::toISO8601String($dateTime); + $this->insertStmt->reset(); $this->insertStmt->addParameter(1, $name, DbType::STRING); $this->insertStmt->addParameter(2, $dateTime, DbType::STRING); @@ -88,8 +90,6 @@ EOF; } public function createFileName(string $name, ?DateTimeInterface $dateTime = null): string { - $dateTime ??= DateTime::utcNow(); - if(empty($name)) throw new InvalidArgumentException('$name may not be empty.'); @@ -97,12 +97,12 @@ EOF; if(!preg_match('#^([a-z_]+)$#', $name)) throw new InvalidArgumentException('$name may only contain alphabetical, spaces and _ characters.'); + $dateTime ??= new DateTimeImmutable('now'); + return $dateTime->format('Y_m_d_His_') . trim($name, '_'); } public function createClassName(string $name, ?DateTimeInterface $dateTime = null): string { - $dateTime ??= DateTime::utcNow(); - if(empty($name)) throw new InvalidArgumentException('$name may not be empty.'); @@ -110,6 +110,8 @@ EOF; if(!preg_match('#^([a-z_]+)$#', $name)) throw new InvalidArgumentException('$name may only contain alphabetical, spaces and _ characters.'); + $dateTime ??= new DateTimeImmutable('now'); + $parts = explode('_', trim($name, '_')); $name = ''; @@ -120,7 +122,7 @@ EOF; } public function createNames(string $baseName, ?DateTimeInterface $dateTime = null): object { - $dateTime ??= DateTime::utcNow(); + $dateTime ??= new DateTimeImmutable('now'); $names = new stdClass; $names->name = $this->createFileName($baseName, $dateTime); diff --git a/src/Data/Migration/FsDbMigrationInfo.php b/src/Data/Migration/FsDbMigrationInfo.php index 21dd6d3..417c661 100644 --- a/src/Data/Migration/FsDbMigrationInfo.php +++ b/src/Data/Migration/FsDbMigrationInfo.php @@ -1,11 +1,10 @@ getYear(), $this->getMonth(), $this->getDay(), 0, 0, 0, 0, $this->getTimezone()); - } - - /** - * Gets the year component of this instance. - * - * @return int Year component. - */ - public function getYear(): int { - return (int)$this->format('Y'); - } - - /** - * Gets the month component of this instance. - * - * @return int Month component, ranging from 1 to 12. - */ - public function getMonth(): int { - return (int)$this->format('n'); - } - - /** - * Gets the day component of this instance. - * - * @return int Day component, ranging from 1 to 31. - */ - public function getDay(): int { - return (int)$this->format('j'); - } - - /** - * Gets the day of the week represented by this instance. - * - * See the SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY and SATURDAY constants for the possible return values of this function. - * - * @return int Integer between 0 and 6 representing the day of the week. - */ - public function getDayOfWeek(): int { - return (int)$this->format('w'); - } - - /** - * Gets the day of the year represented by this instance. - * - * @return int Day of the year, ranging from 1-366. - */ - public function getDayOfYear(): int { - return ((int)$this->format('z')) + 1; - } - - /** - * Gets the ISO8601 week number of this instance. - * - * @return int Integer representing the current week number. - */ - public function getWeek(): int { - return (int)$this->format('W'); - } - - /** - * Gets the time of day for this instance. - * - * @return TimeSpan A TimeSpan representing the amount of time elapsed since midnight. - */ - public function getTimeOfDay(): TimeSpan { - return TimeSpan::create(0, $this->getHour(), $this->getMinute(), $this->getSecond(), $this->getMicrosecond()); - } - - /** - * Gets the hour component of this instance. - * - * @return int Hour component, ranging from 0 to 23. - */ - public function getHour(): int { - return (int)$this->format('G'); - } - - /** - * Gets the minute component of this instance. - * - * @return int Minute component, ranging from 0 to 59. - */ - public function getMinute(): int { - return (int)$this->format('i'); - } - - /** - * Gets the second component of this instance. - * - * @return int Second component, ranging from 0 to 59. - */ - public function getSecond(): int { - return (int)$this->format('s'); - } - - /** - * Gets the millisecond component of this instance. - * - * @return int Millisecond component, ranging from 0 to 999. - */ - public function getMillisecond(): int { - return (int)$this->format('v'); - } - - /** - * Gets the microsecond component of this instance. - * - * @return int Microsecond component, ranging from 0 to 999999. - */ - public function getMicrosecond(): int { - return (int)$this->format('u'); - } - - /** - * Gets the number of seconds that have elapsed since midnight January 1st, 1970 UTC for this instance. - * - * @return int Number of seconds. - */ - public function getUnixTimeSeconds(): int { - return (int)$this->format('U'); - } - - /** - * Gets the number of milliseconds that have elapsed since midnight January 1st, 1970 UTC for this instance. - * - * @return float Number of milliseconds. - */ - public function getUnixTimeMilliseconds(): float { - return (float)$this->format('Uv'); - } - - /** - * Gets whether Daylight Savings is in effect during the time this instance represents. - * - * @return bool true if DST is in effect, false if not. - */ - public function isDaylightSavingTime(): bool { - return $this->format('I') !== '0'; - } - - /** - * Gets whether the year this instance represents is a leap year. - * - * @return bool true if the year is a leap year, false if not. - */ - public function isLeapYear(): bool { - return $this->format('L') !== '0'; - } - - /** - * Gets whether this DateTime uses UTC date/time. - * - * @return bool true if UTC, false if not. - */ - public function isUTC(): bool { - return $this->getTimezone()->equals(TimeZoneInfo::utc()); - } - - /** - * Gets the time zone for this instance. - * - * @return TimeZoneInfo Time zone for this instance. - */ - public function getTimezone(): TimeZoneInfo { - return TimeZoneInfo::cast(parent::getTimezone()); - } - - /** - * Adds a period of time to this date time object. - * - * @param DateInterval $timeSpan Time period to add to this DateTime. - * @return DateTime A DateTime instance which is the sum of this instance and the provided period. - */ - public function add(DateInterval $timeSpan): DateTime { - return self::cast(parent::add($timeSpan)); - } - - /** - * Adds a number of years to this instance. - * - * @param float $years Number of years to add. - * @return DateTime New DateTime instance with the years added. - */ - public function addYears(float $years): DateTime { - return $this->add(TimeSpan::fromDays(365 * $years)); - } - - /** - * Adds a number of months to this instance. - * - * @param float $months Number of months to add. - * @return DateTime New DateTime instance with the months added. - */ - public function addMonths(float $months): DateTime { - return $this->add(TimeSpan::fromDays(365 * 31 * $months)); - } - - /** - * Adds a number of days to this instance. - * - * @param float $days Number of days to add. - * @return DateTime New DateTime instance with the days added. - */ - public function addDays(float $days): DateTime { - return $this->add(TimeSpan::fromDays($days)); - } - - /** - * Adds a number of hours to this instance. - * - * @param float $hours Number of hours to add. - * @return DateTime New DateTime instance with the hours added. - */ - public function addHours(float $hours): DateTime { - return $this->add(TimeSpan::fromHours($hours)); - } - - /** - * Adds a number of minutes to this instance. - * - * @param float $minutes Number of minutes to add. - * @return DateTime New DateTime instance with the minutes added. - */ - public function addMinutes(float $minutes): DateTime { - return $this->add(TimeSpan::fromMinutes($minutes)); - } - - /** - * Adds a number of seconds to this instance. - * - * @param float $seconds Number of seconds to add. - * @return DateTime New DateTime instance with the seconds added. - */ - public function addSeconds(float $seconds): DateTime { - return $this->add(TimeSpan::fromSeconds($seconds)); - } - - /** - * Adds a number of milliseconds to this instance. - * - * @param float $millis Number of milliseconds to add. - * @return DateTime New DateTime instance with the milliseconds added. - */ - public function addMilliseconds(float $millis): DateTime { - return $this->add(TimeSpan::fromMilliseconds($millis)); - } - - /** - * Adds a number of microseconds to this instance. - * - * @param float $micros Number of microseconds to add. - * @return DateTime New DateTime instance with the microseconds added. - */ - public function addMicroseconds(float $micros): DateTime { - return $this->add(TimeSpan::fromMicroseconds($micros)); - } - - /** - * Subtracts a time period from this DateTime. - * - * @param DateInterval $timeSpan Time period to be subtracted. - * @return DateTime A new DateTime instance which is this instance minus the given time period. - */ - public function subtract(DateInterval $timeSpan): DateTime { - return self::cast(parent::sub($timeSpan)); - } - - /** - * Alias for difference, must exist because of the DateTimeImmutable inheritance but I don't like short names. - * - * @internal - */ - public function diff(DateTimeInterface $dateTime, bool $absolute = false): TimeSpan { - return $this->difference($dateTime, $absolute); - } - - /** - * Subtracts another Date/Time from this DateTime. - * - * @param DateTimeInterface $dateTime Date/time to subtract. - * @return TimeSpan Difference between this DateTime and the provided one. - */ - public function difference(DateTimeInterface $dateTime, bool $absolute = false): TimeSpan { - return TimeSpan::cast(parent::diff($dateTime, $absolute)); - } - - /** - * Applies a modifier on this instance and returns the result as a new instance. - * - * @see https://www.php.net/manual/en/datetime.formats.php - * @param string $modifier A date/time format. - * @return DateTime The modified date/time. - */ - public function modify(string $modifier): DateTime { - return self::cast(parent::modify($modifier)); - } - - /** - * Creates a new DateTime with the given date component. - * - * @param int $year Desired year component. - * @param int $month Desired month component. - * @param int $day Desired day component. - * @return DateTime A new DateTime instance with the given date component. - */ - public function setDate(int $year, int $month, int $day): DateTime { - return self::cast(parent::setDate($year, $month, $day)); - } - - /** - * Creates a new DateTime with the given ISO date for the date component. - * - * @param int $year Year of the date. - * @param int $week Week of the date. - * @param int $dayOfWeek Offset from the first of the week. - * @return DateTime A new DateTime instance with the given date component. - */ - public function setISODate(int $year, int $week, int $dayOfWeek = 1): DateTime { - return self::cast(parent::setISODate($year, $week, $dayOfWeek)); - } - - /** - * Creates a new DateTime with the given time component. - * - * @param int $hour Desired hour component. - * @param int $minute Desired minute component. - * @param int $second Desired second component. - * @param int $microsecond Desired microsecond component. - * @return DateTime A new DateTime instance with the given time component. - */ - public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): DateTime { - return self::cast(parent::setTime($hour, $minute, $second, $microsecond)); - } - - /** - * Creates a new DateTime with the date and time components based on a Unix timestamp. - * - * @param int $timestamp Unix timestamp representing the date. - * @return DateTime A new DateTime instance with the given date and time. - */ - public function setTimestamp(int $timestamp): DateTime { - return self::cast(parent::setTimestamp($timestamp)); - } - - /** - * Creates a new DateTime with the given time zone. - * - * @param DateTimeZone $timeZone An object representing the desired time zone. - * @return DateTime A new DateTime with the given time zone. - */ - public function setTimezone(DateTimeZone $timeZone): DateTime { - return self::cast(parent::setTimezone($timeZone)); - } - - public function compare(mixed $other): int { - if($other instanceof DateTimeInterface) - return strcmp($this->format(self::COMPARE), $other->format(self::COMPARE)); - return -1; - } - - public function equals(mixed $other): bool { - if($other instanceof DateTimeInterface) - return $this->format(self::COMPARE) === $other->format(self::COMPARE); - return false; - } - - public function isLessThan(DateTimeInterface $other): bool { - return $this->compare($other) < 0; - } - - public function isLessThanOrEqual(DateTimeInterface $other): bool { - return $this->compare($other) <= 0; - } - - public function isMoreThan(DateTimeInterface $other): bool { - return $this->compare($other) > 0; - } - - public function isMoreThanOrEqual(DateTimeInterface $other): bool { - return $this->compare($other) >= 0; - } - - /** - * Formats this DateTime as an ISO8601 date/time string. - * - * @return string This object represented as an ISO8601 date/time string. - */ - public function toISO8601String(): string { - return $this->format(DateTimeInterface::ATOM); - } - - /** - * Formats this DateTime as a Cookie date/time string. - * - * @return string This object represented as a Cookie date/time string. - */ - public function toCookieString(): string { - return $this->format(DateTimeInterface::COOKIE); - } - - /** - * Formats this DateTime as an RFC 822 date/time string. - * - * @return string This object represented as an RFC 822 date/time string. - */ - public function toRFC822String(): string { - return $this->format(DateTimeInterface::RFC822); - } - - /** - * Creates a string representation of this object. - * - * @return string Representation of this object. - */ - public function __toString(): string { - return $this->format(DateTimeInterface::ATOM); - } - - /** - * Returns the data which should be serialized as json. - * - * @see https://www.php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed Data to be passed to json_encode. - */ - public function jsonSerialize(): mixed { - return (string)$this; - } - - /** - * Converts this immutable \Index\DateTime type to a mutable \DateTime type. - * - * @return \DateTime A new mutable \DateTime instance. - */ - public function toNative(): \DateTime { - return \DateTime::createFromImmutable($this); - } - - /** - * Creates a DateTime object that is set to the current date and time, expressed in the provided time zone. - * - * @param DateTimeZone $timeZone Desired time zone, null for the current default time zone. - * @return DateTime An instance representing now. - */ - public static function now(?DateTimeZone $timeZone = null): DateTime { - return new DateTime('now', $timeZone); - } - - /** - * Creates a DateTime object that is set to the current date and time, expressed in UTC. - * - * @return DateTime An instance representing now in UTC. - */ - public static function utcNow(): DateTime { - return self::now(TimeZoneInfo::utc()); - } - - /** - * Converts Unix time seconds to a DateTime object. - * - * @param int $seconds Unix time seconds. - * @return DateTime A DateTime instance representing the Unix time. - */ - public static function fromUnixTimeSeconds(int $seconds): DateTime { - return new DateTime('@' . $seconds); - } - - /** - * Converts Unix time milliseconds to a DateTime object. - * - * @param float $millis Unix time milliseconds. - * @return DateTime A DateTime instance representing the Unix time. - */ - public static function fromUnixTimeMilliseconds(float $millis): DateTime { - return new DateTime('@' . ($millis / 1000)); - } - - /** - * Creates a new DateTime instance with the given components. - * - * @param int $year Desired year component. - * @param int $month Desired month component. - * @param int $day Desired day component. - * @param int $hour Desired hour component. - * @param int $minute Desired minute component. - * @param int $second Desired second component. - * @param int $micros Desired microsecond component. - * @param ?DateTimeZone $timeZone Desired time zone. - */ - public static function create(int $year, int $month = 1, int $day = 1, int $hour = 0, int $minute = 0, int $second = 0, int $micros = 0, ?DateTimeZone $timeZone = null): DateTime { - if($year < 1 || $year > 9999) - throw new InvalidArgumentException('$year may not be less than 1 or more than 9999.'); - if($month < 1 || $month > 12) - throw new InvalidArgumentException('$month may not be less than 1 or more than 12.'); - if($day < 1 || $day > 31) - throw new InvalidArgumentException('$day may not be less than 1 or more than 31.'); - if($hour < 0 || $hour > 23) - throw new InvalidArgumentException('$hour may not be less than 0 or more than 23.'); - if($minute < 0 || $minute > 59) - throw new InvalidArgumentException('$minute may not be less than 0 or more than 59.'); - if($second < 0 || $second > 59) - throw new InvalidArgumentException('$second may not be less than 0 or more than 59.'); - if($micros < 0 || $micros > 999999) - throw new InvalidArgumentException('$micros may not be less than 0 or more than 999999.'); - - return new DateTime( - sprintf( - '%04d-%02d-%02dT%02d:%02d:%02d.%06d', - $year, $month, $day, $hour, $minute, $second, $micros - ), - $timeZone - ); - } - - /** - * Parses a time string and creates a DateTime using it. - * - * @see https://www.php.net/manual/en/datetime.createfromformat - * @param string $format Format that the passed string should be in. - * @param string $dateTime String representing the time. - * @param ?DateTimeZone $timeZone Desired time zone. - * @return DateTime|false A DateTime instance, or false on failure. - */ - public static function createFromFormat(string $format, string $dateTime, ?DateTimeZone $timeZone = null): DateTime|false { - $instance = parent::createFromFormat($format, $dateTime, $timeZone); - if($instance === false) - return false; - return self::cast($instance); - } - - /** - * Creates a new DateTime instance from an interface implementation. - * - * @param DateTimeInterface $object Object that should be sourced. - * @return DateTime A new DateTime instance. - */ - public static function createFromInterface(DateTimeInterface $object): DateTime { - return self::cast($object); - } - - /** - * Creates a new immutable DateTime instance from a mutable instance. - * - * @param \DateTime $object Mutable object that should be sourced. - * @return DateTime New immutable DateTime representing the same value. - */ - #[\ReturnTypeWillChange] - public static function createFromMutable(\DateTime $object): DateTime { - return self::cast($object); - } - - /** - * @internal - */ - public static function __set_state(array $array): DateTime { - return self::cast(parent::__set_state($array)); - } - - /** - * Makes sure a DateTimeInterface implementing object is a DateTime instance. - * - * @param DateTimeInterface $dateTime Input object. - * @return DateTime If the input was a DateTime, the same instance will be returned. - * If the input was something else, a new DateTime instance will be returned based on the input. - */ - public static function cast(DateTimeInterface $dateTime): DateTime { - if($dateTime instanceof DateTime) - return $dateTime; - return new DateTime($dateTime->format('@U.u'), $dateTime->getTimezone()); - } -} diff --git a/src/Http/Headers/DateHeader.php b/src/Http/Headers/DateHeader.php index 31f347f..bfe4bc4 100644 --- a/src/Http/Headers/DateHeader.php +++ b/src/Http/Headers/DateHeader.php @@ -1,25 +1,31 @@ dateTime = $dateTime; } - public function getDateTime(): DateTime { + public function getDateTime(): DateTimeImmutable { return $this->dateTime; } public static function parse(HttpHeader $header): DateHeader { - return new DateHeader(DateTime::createFromFormat(\DateTimeInterface::RFC7231, $header->getFirstLine())); + $parsed = DateTimeImmutable::createFromFormat(DateTimeInterface::RFC7231, $header->getFirstLine()); + if($parsed === false) + throw new RuntimeException('Failed to parse Date header.'); + + return new DateHeader($parsed); } } diff --git a/src/Http/Headers/IfModifiedSinceHeader.php b/src/Http/Headers/IfModifiedSinceHeader.php index 94224e3..3e0d0fe 100644 --- a/src/Http/Headers/IfModifiedSinceHeader.php +++ b/src/Http/Headers/IfModifiedSinceHeader.php @@ -1,30 +1,36 @@ dateTime = $dateTime; } - public function getDateTime(): DateTime { + public function getDateTime(): DateTimeImmutable { return $this->dateTime; } - public function isLessThanOrEqual(DateTimeInterface $dateTime): bool { - return $this->dateTime->isLessThanOrEqual($dateTime); + public function isLessThanOrEqual(DateTimeInterface $other): bool { + return XDateTime::compare($this->dateTime, $other) <= 0; } public static function parse(HttpHeader $header): IfModifiedSinceHeader { - return new IfModifiedSinceHeader(DateTime::createFromFormat(\DateTimeInterface::RFC7231, $header->getFirstLine())); + $parsed = DateTimeImmutable::createFromFormat(DateTimeInterface::RFC7231, $header->getFirstLine()); + if($parsed === false) + throw new RuntimeException('Failed to parse If-Modified-Since header.'); + + return new IfModifiedSinceHeader($parsed); } } diff --git a/src/Http/Headers/IfRangeHeader.php b/src/Http/Headers/IfRangeHeader.php index 17051f6..74c3edf 100644 --- a/src/Http/Headers/IfRangeHeader.php +++ b/src/Http/Headers/IfRangeHeader.php @@ -1,19 +1,21 @@ tag = $tag; $this->dateTime = $dateTime; } @@ -30,7 +32,7 @@ class IfRangeHeader { return $this->dateTime !== null; } - public function getDateTime(): DateTime { + public function getDateTime(): DateTimeImmutable { return $this->dateTime; } @@ -42,7 +44,7 @@ class IfRangeHeader { } if($this->hasDateTime() && $other instanceof DateTimeInterface) - return $this->dateTime->isLessThanOrEqual($other); + return XDateTime::compare($this->dateTime, $other) <= 0; return false; } @@ -50,8 +52,13 @@ class IfRangeHeader { public static function parse(HttpHeader $header): IfRangeHeader { $line = $header->getFirstLine(); - if($line[0] !== '"' && !str_starts_with($line, 'W/"')) - return new IfRangeHeader(null, DateTime::createFromFormat(\DateTimeInterface::RFC7231, $line)); + if($line[0] !== '"' && !str_starts_with($line, 'W/"')) { + $parsed = DateTimeImmutable::createFromFormat(DateTimeInterface::RFC7231, $line); + if($parsed === false) + throw new RuntimeException('Failed to parse If-Range header.'); + + return new IfRangeHeader(null, $parsed); + } return new IfRangeHeader($line, null); } diff --git a/src/Http/Headers/IfUnmodifiedSinceHeader.php b/src/Http/Headers/IfUnmodifiedSinceHeader.php index 92245a5..86e4937 100644 --- a/src/Http/Headers/IfUnmodifiedSinceHeader.php +++ b/src/Http/Headers/IfUnmodifiedSinceHeader.php @@ -1,30 +1,36 @@ dateTime = $dateTime; } - public function getDateTime(): DateTime { + public function getDateTime(): DateTimeImmutable { return $this->dateTime; } - public function isMoreThan(DateTimeInterface $dateTime): bool { - return $this->dateTime->isMoreThan($dateTime); + public function isMoreThan(DateTimeInterface $other): bool { + return XDateTime::compare($this->dateTime, $other) > 0; } public static function parse(HttpHeader $header): IfUnmodifiedSinceHeader { - return new IfUnmodifiedSinceHeader(DateTime::createFromFormat(\DateTimeInterface::RFC7231, $header->getFirstLine())); + $parsed = DateTimeImmutable::createFromFormat(DateTimeInterface::RFC7231, $header->getFirstLine()); + if($parsed === false) + throw new RuntimeException('Failed to parse If-Unmodified-Since header.'); + + return new IfUnmodifiedSinceHeader($parsed); } } diff --git a/src/Http/Headers/LastModifiedHeader.php b/src/Http/Headers/LastModifiedHeader.php index 4874fce..b4916c9 100644 --- a/src/Http/Headers/LastModifiedHeader.php +++ b/src/Http/Headers/LastModifiedHeader.php @@ -1,25 +1,31 @@ dateTime = $dateTime; } - public function getDateTime(): DateTime { + public function getDateTime(): DateTimeImmutable { return $this->dateTime; } public static function parse(HttpHeader $header): LastModifiedHeader { - return new LastModifiedHeader(DateTime::createFromFormat(\DateTimeInterface::RFC7231, $header->getFirstLine())); + $parsed = DateTimeImmutable::createFromFormat(DateTimeInterface::RFC7231, $header->getFirstLine()); + if($parsed === false) + throw new RuntimeException('Failed to parse Last-Modified header.'); + + return new LastModifiedHeader($parsed); } } diff --git a/src/Http/Headers/OriginHeader.php b/src/Http/Headers/OriginHeader.php index 601e27f..d925ac4 100644 --- a/src/Http/Headers/OriginHeader.php +++ b/src/Http/Headers/OriginHeader.php @@ -1,11 +1,10 @@ toCookieString(); - } + if($expires !== null) + $cookie .= '; Expires=' . XDateTime::toCookieString($expires); if(!empty($domain)) $cookie .= '; Domain=' . $domain; diff --git a/src/TimeSpan.php b/src/TimeSpan.php deleted file mode 100644 index 83b6648..0000000 --- a/src/TimeSpan.php +++ /dev/null @@ -1,526 +0,0 @@ -realDays = $this->days === false ? $this->calculateRealDays() : $this->days; - } - - private function calculateRealDays(): int { - return ($this->y * 365) - + ($this->m * 31) - + $this->d; - } - - /** - * Gets the amount of days this TimeSpan represents. - * - * @return int Amount of days. - */ - public function getDays(): int { - return $this->realDays; - } - - /** - * Gets the amount of hours this TimeSpan represents. - * - * @retunr int Amount of hours. - */ - public function getHours(): int { - return $this->h; - } - - /** - * Gets the amount of minutes this TimeSpan represents. - * - * @return int Amount of minutes. - */ - public function getMinutes(): int { - return $this->i; - } - - /** - * Gets the amount of seconds this TimeSpan represents. - * - * @return int Amount of seconds. - */ - public function getSeconds(): int { - return $this->s; - } - - /** - * Gets the amount of milliseconds this TimeSpan represents. - * - * @return int Amount of milliseconds. - */ - public function getMilliseconds(): int { - return (int)($this->f * 1000); - } - - /** - * Gets the amount of microseconds this TimeSpan represents. - * - * @return int Amount of microseconds. - */ - public function getMicroseconds(): int { - return (int)($this->f * 1000000); - } - - /** - * Gets whether this TimeSpan represents a negative amount of time. - * - * @return bool true if negative, false if positive. - */ - public function isNegative(): bool { - return $this->invert !== 0; - } - - /** - * Counts the total amount of days this TimeSpan contains. - * - * @return float Total amount of days. - */ - public function totalDays(): float { - $days = $this->getDays(); - $days += $this->getHours() / self::HOURS_IN_DAY; - $days += $this->getMinutes() / self::MINS_IN_DAY; - $days += $this->getSeconds() / self::SECS_IN_DAY; - $days += $this->getMicroseconds() / self::MICROS_IN_DAY; - return $days; - } - - /** - * Counts the total amount of hours this TimeSpan contains. - * - * @return float Total amount of hours. - */ - public function totalHours(): float { - $hours = $this->getDays() * self::HOURS_IN_DAY; - $hours += $this->getHours(); - $hours += $this->getMinutes() / self::MINS_IN_HOUR; - $hours += $this->getSeconds() / self::SECS_IN_HOUR; - $hours += $this->getMicroseconds() / self::MICROS_IN_HOUR; - return $hours; - } - - /** - * Counts the total amount of minutes this TimeSpan contains. - * - * @return float Total amount of minutes. - */ - public function totalMinutes(): float { - $mins = $this->getDays() * self::MINS_IN_DAY; - $mins += $this->getHours() * self::MINS_IN_HOUR; - $mins += $this->getMinutes(); - $mins += $this->getSeconds() / self::SECS_IN_MIN; - $mins += $this->getMicroseconds() / self::MICROS_IN_MIN; - return $mins; - } - - /** - * Counts the total amount of seconds this TimeSpan contains. - * - * @return float Total amount of seconds. - */ - public function totalSeconds(): float { - $secs = $this->getDays() * self::SECS_IN_DAY; - $secs += $this->getHours() * self::SECS_IN_HOUR; - $secs += $this->getMinutes() * self::SECS_IN_MIN; - $secs += $this->getSeconds(); - $secs += $this->getMicroseconds() * self::MICROS_IN_SEC; - return $secs; - } - - /** - * Counts the total amount of milliseconds this TimeSpan contains. - * - * @return float Total amount of milliseconds. - */ - public function totalMilliseconds(): float { - $millis = $this->getDays() * self::MILLIS_IN_DAY; - $millis += $this->getHours() * self::MILLIS_IN_HOUR; - $millis += $this->getMinutes() * self::MILLIS_IN_MIN; - $millis += $this->getSeconds() * self::MILLIS_IN_SEC; - $millis += $this->getMilliseconds(); - return $millis; - } - - /** - * Counts the total amount of microseconds this TimeSpan contains. - * - * @return float Total amount of microseconds. - */ - public function totalMicroseconds(): float { - $micros = $this->getDays() * self::MICROS_IN_DAY; - $micros += $this->getHours() * self::MICROS_IN_HOUR; - $micros += $this->getMinutes() * self::MICROS_IN_MIN; - $micros += $this->getSeconds() * self::MICROS_IN_SEC; - $micros += $this->getMicroseconds(); - return $micros; - } - - /** - * Returns a new TimeSpan whose value is the sum of this instance and the provided instance. - * - * @param DateInterval $timeSpan Interval to add. - * @return TimeSpan Instance that represents this instance plus the value of $timeSpan. - */ - public function add(DateInterval $timeSpan): TimeSpan { - $timeSpan = self::cast($timeSpan); - - $days1 = $this->totalDays(); - if($this->isNegative()) - $days1 *= -1; - - $days2 = $timeSpan->totalDays(); - if($timeSpan->isNegative()) - $days2 *= -1; - - return self::fromDays($days1 + $days2); - } - - /** - * Returns a new TimeSpan whose value is the difference of this instance and the provided instance. - * - * @param DateInterval $timeSpan Interval to subtract. - * @return TimeSpan Instance that represents this instance minus the value of $timeSpan. - */ - public function subtract(DateInterval $timeSpan): TimeSpan { - $timeSpan = self::cast($timeSpan); - - $days1 = $this->totalDays(); - if($this->isNegative()) - $days1 *= -1; - - $days2 = $timeSpan->totalDays(); - if($timeSpan->isNegative()) - $days2 *= -1; - - return self::fromDays($days1 - $days2); - } - - /** - * Returns the result of a division of this instance and the provided instance. - * - * @param DateInterval $timeSpan Interval to be divided by. - * @return float $timeSpan Number that represents this instance divided by the value of $timeSpan. - */ - public function divideTimeSpan(DateInterval $timeSpan): float { - $timeSpan = self::cast($timeSpan); - - $days1 = $this->totalDays(); - if($this->isNegative()) - $days1 *= -1; - - $days2 = $timeSpan->totalDays(); - if($timeSpan->isNegative()) - $days2 *= -1; - - if($days2 === 0.0) - return 0.0; - - return $days1 / $days2; - } - - /** - * Returns the result of a division of this instance and the provided divisor. - * - * @param float $divisor Value to divide by. - * @return TimeSpan Instance that represents this instance divided by the value of $divisor. - */ - public function divideFloat(float $divisor): TimeSpan { - $totalDays = $this->totalDays(); - if($this->isNegative()) - $totalDays *= -1; - $totalDays /= $divisor; - return self::fromDays($totalDays); - } - - /** - * Returns a new TimeSpan whose alue is the result of a multiplication of this oinstance and the provided factor. - * - * @param float $factor Value to multiply with. - * @return TimeSpan Instance that represents this instance multiplied by the value of $factor. - */ - public function multiply(float $factor): TimeSpan { - $totalDays = $this->totalDays(); - if($this->isNegative()) - $totalDays *= -1; - $totalDays *= $factor; - return self::fromDays($totalDays); - } - - /** - * Return a new TimeSpan whose value is the negated value of this instance. - * - * @return TimeSpan Negated copy of this instance. - */ - public function negate(): TimeSpan { - return self::create( - $this->getDays(), - $this->getHours(), - $this->getMinutes(), - $this->getSeconds(), - $this->getMicroseconds(), - !$this->isNegative() - ); - } - - public function compare(mixed $other): int { - if(!($other instanceof DateInterval)) - return -1; - $other = self::cast($other); - - $diff = $this->getDays() <=> $other->getDays(); - if($diff) return $diff; - - $diff = $this->getHours() <=> $other->getHours(); - if($diff) return $diff; - - $diff = $this->getMinutes() <=> $other->getMinutes(); - if($diff) return $diff; - - $diff = $this->getSeconds() <=> $other->getSeconds(); - if($diff) return $diff; - - $diff = $this->getMicroseconds() <=> $other->getMicroseconds(); - if($diff) return $diff; - - $diff = $this->isNegative() <=> $other->isNegative(); - if($diff) return $diff; - - return 0; - } - - public function equals(mixed $other): bool { - if(!($other instanceof DateInterval)) - return false; - $other = self::cast($other); - return $this->getDays() === $other->getDays() - && $this->getHours() === $other->getHours() - && $this->getMinutes() === $other->getMinutes() - && $this->getSeconds() === $other->getSeconds() - && $this->getMicroseconds() === $other->getMicroseconds() - && $this->isNegative() === $other->isNegative(); - } - - /** - * Returns the data which should be serialized as json. - * - * @see https://www.php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed Data to be passed to json_encode. - */ - public function jsonSerialize(): mixed { - return (string)$this; - } - - /** - * Creates a string representation of this object. - * - * @return string Representation of this object. - */ - public function __toString(): string { - $string = $this->isNegative() ? '-' : ''; - return sprintf( - '%sP%dY%dM%dDT%dH%dM%d.%dS', - $this->isNegative() ? '-' : '', - $this->y, - $this->m, - $this->d, - $this->getHours(), - $this->getMinutes(), - $this->getSeconds(), - $this->getMicroseconds() - ); - } - - /** - * Creates a new TimeSpan instance that represents the specified number of days. - * - * @param float $days Number of days. - * @return TimeSpan Instance representing the provided number of days. - */ - public static function fromDays(float $days): TimeSpan { - $abs = abs($days); - $round = (int)floor($abs); - $hours = ($abs - $round) * self::HOURS_IN_DAY; - $hoursRound = (int)floor($hours); - $minutes = ($hours - $hoursRound) * self::MINS_IN_HOUR; - $minsRound = (int)floor($minutes); - $seconds = ($minutes - $minsRound) * self::SECS_IN_MIN; - $secsRound = (int)floor($seconds); - $micros = (int)floor(($seconds - $secsRound) * self::MICROS_IN_SEC); - return self::create($round, $hoursRound, $minsRound, $secsRound, $micros, $days < 0); - } - - /** - * Creates a new TimeSpan instance that represents the specified number of hours. - * - * @param float $hours Number of hours. - * @return TimeSpan Instance representing the provided number of hours. - */ - public static function fromHours(float $hours): TimeSpan { - $abs = abs($hours); - $round = (int)floor($abs); - $minutes = ($abs - $round) * self::MINS_IN_HOUR; - $minsRound = (int)floor($minutes); - $seconds = ($minutes - $minsRound) * self::SECS_IN_MIN; - $secsRound = (int)floor($seconds); - $micros = (int)floor(($seconds - $secsRound) * self::MICROS_IN_SEC); - return self::create(0, $round, $minsRound, $secsRound, $micros, $hours < 0); - } - - /** - * Creates a new TimeSpan instance that represents the specified number of minutes. - * - * @param float $minutes Number of minutes. - * @return TimeSpan Instance representing the provided number of minutes. - */ - public static function fromMinutes(float $minutes): TimeSpan { - $abs = abs($minutes); - $round = (int)floor($abs); - $seconds = ($abs - $round) * self::SECS_IN_MIN; - $secsRound = (int)floor($seconds); - $micros = (int)floor(($seconds - $secsRound) * self::MICROS_IN_SEC); - return self::create(0, 0, $round, $secsRound, $micros, $minutes < 0); - } - - /** - * Creates a new TimeSpan instance that represents the specified number of seconds. - * - * @param float $seconds Number of seconds. - * @return TimeSpan Instance representing the provided number of seconds. - */ - public static function fromSeconds(float $seconds): TimeSpan { - $abs = abs($seconds); - $round = (int)floor($abs); - $micros = (int)floor(($abs - $round) * self::MICROS_IN_SEC); - return self::create(0, 0, 0, $round, $micros, $seconds < 0); - } - - /** - * Creates a new TimeSpan instance that represents the specified number of milliseconds. - * - * @param float $millis Number of milliseconds. - * @return TimeSpan Instance representing the provided number of milliseconds. - */ - public static function fromMilliseconds(float $millis): TimeSpan { - return self::fromMicroseconds($millis * 1000); - } - - /** - * Creates a new TimeSpan instance that represents the specified number of microseconds. - * - * @param float $micros Number of microseconds. - * @return TimeSpan Instance representing the provided number of microseconds. - */ - public static function fromMicroseconds(float $micros): TimeSpan { - return self::create(0, 0, 0, 0, (int)floor(abs($micros)), $micros < 0); - } - - /** - * Creates a new TimeSpan using the given parameters. - * - * @param int $days Number of days. - * @param int $hours Number of hours. - * @param int $minutes Number of minutes. - * @param int $seconds Number of seconds. - * @param int $micros Number of microseconds. - * @param bool $negative true for a negative TimeSpan, false for positive. - * @return TimeSpan A new TimeSpan from the provided parameters. - */ - public static function create(int $days, int $hours, int $minutes, int $seconds, int $micros = 0, bool $negative = false): TimeSpan { - $ts = new TimeSpan; - $ts->realDays = $days = max(0, $days); - $ts->h = max(0, min(23, $hours)); - $ts->i = max(0, min(59, $minutes)); - $ts->s = max(0, min(59, $seconds)); - $ts->f = $micros / self::MICROS_IN_SEC; - $ts->invert = $negative ? 1 : 0; - - // this may still be stupid - if($days > 0) { - $days /= 365; - $ts->y = (int)$days; - $days -= $ts->y; - $days *= 12; - $ts->m = (int)$days; - $days -= $ts->m; - $days *= 31; - $ts->d = (int)$days; - } - - return $ts; - } - - /** - * Creates a TimeSpan from the relative parts of a string. - * - * Uses the same parser as strtotime and the DateTime constructor internally. - * - * @param string $dateTime A date with relative parts. - * @return TimeSpan A TimeSpan representing the given - */ - public static function createFromDateString(string $dateTime): TimeSpan { - return self::cast(parent::createFromDateString($dateTime)); - } - - /** - * Makes sure a DateInterval inheriting object is a TimeSpan instance. - * - * @param DateInterval $dateInterval Input object. - * @return TimeSpan If the input was a TimeSpan, the same instance will be returned. - * If the input was something else, a new TimeSpan instance will be returned based on the input. - */ - public static function cast(DateInterval $dateInterval): TimeSpan { - if($dateInterval instanceof TimeSpan) - return $dateInterval; - - $timeSpan = new TimeSpan; - $timeSpan->y = $dateInterval->y; - $timeSpan->m = $dateInterval->m; - $timeSpan->d = $dateInterval->d; - $timeSpan->h = $dateInterval->h; - $timeSpan->i = $dateInterval->i; - $timeSpan->s = $dateInterval->s; - $timeSpan->f = $dateInterval->f; - $timeSpan->invert = $dateInterval->invert; - $timeSpan->realDays = $timeSpan->calculateRealDays(); - return $timeSpan; - } -} diff --git a/src/TimeZoneInfo.php b/src/TimeZoneInfo.php deleted file mode 100644 index 5467cf0..0000000 --- a/src/TimeZoneInfo.php +++ /dev/null @@ -1,213 +0,0 @@ -getName(); - - if(!date_default_timezone_set($timeZone)) - throw new RuntimeException('Invalid default time zone specified.'); - } - - /** - * Gets location info for a time zone. - * - * It makes more sense to use getCountryCode, getLatitude, getLongitude and getComments, if you can. - * - * @return array Location information. - */ - public function getLocation(): array { - if($this->location === null) - $this->location = parent::getLocation(); - return $this->location; - } - - /** - * Gets the country code of the country which this time zone falls in. - * - * @return string Country code for this time zone. - */ - public function getCountryCode(): string { - return $this->getLocation()['country_code']; - } - - /** - * Gets the latitude of this time zone. - * - * @return float Latitude of this time zone. - */ - public function getLatitude(): float { - return $this->getLocation()['latitude']; - } - - /** - * Gets the longitude of this time zone. - * - * @return float Longitude of this time zone. - */ - public function getLongitude(): float { - return $this->getLocation()['longitude']; - } - - /** - * Gets location comments for this time zone. - * - * @return string Location comments. - */ - public function getComments(): string { - return $this->getLocation()['comments']; - } - - /** - * Gets the offset of this timezone relative to a given date/time instance. - * - * @param DateTimeInterface|null $dateTime Date/time to use as a base, null for UTC Now. - * @return int Offset relative to given date/time. - */ - public function getOffset(DateTimeInterface|null $dateTime = null): int { - return parent::getOffset($dateTime ?? new DateTime('now', self::$utc)); - } - - /** - * Creates a string representation of this object. - * - * @return string Representation of this object. - */ - public function __toString(): string { - $offset = $this->getOffset(); - return sprintf( - '(UTC%s%s) %s', - $offset < 0 ? '-' : '+', - date('H:i', abs($offset)), - $this->getName() - ); - } - - /** - * Returns the data which should be serialized as json. - * - * @see https://www.php.net/manual/en/jsonserializable.jsonserialize.php - * @return mixed Data to be passed to json_encode. - */ - public function jsonSerialize(): mixed { - return $this->getName(); - } - - public function compare(mixed $other): int { - if(!($other instanceof DateTimeZone)) - return -1; - $other = self::cast($other); - - $diff = $this->getOffset() <=> $other->getOffset(); - if($diff) return $diff; - - return strcmp($this->getName(), $other->getName()); - } - - public function equals(mixed $other): bool { - if(!($other instanceof DateTimeZone)) - return false; - return $this->getName() === $other->getName() - || $this->getOffset() === self::cast($other)->getOffset(); - } - - /** - * Gets a list of all time zones. - * - * @param bool $ordered true if the list should be ordered, false if not. - * @param int $timeZoneGroup ID of a group to filter the generated list. - * @param string|null $countryCode Country to filter the generated list by. - * @return array Array containing the time zones. - */ - public static function all(bool $ordered = false, int $timeZoneGroup = DateTimeZone::ALL, string|null $countryCode = null): array { - $timeZones = self::listIdentifiers($timeZoneGroup, $countryCode === null ? null : $countryCode); - $timeZones = XArray::select($timeZones, fn($id) => new TimeZoneInfo($id)); - - if($ordered) - $timeZones = XArray::sort($timeZones, fn($a, $b) => $a->compare($b)); - - return $timeZones; - } - - /** - * Makes sure a DateTimeZone inheriting object is a TimeZoneInfo instance. - * - * @param DateTimeZone $timeZone Input object. - * @return TimeZoneInfo If the input was a TimeZoneInfo, the same instance will be returned. - * If the input was something else, a new TimeZoneInfo instance will be returned based on the input. - */ - public static function cast(DateTimeZone $timeZone): TimeZoneInfo { - if($timeZone instanceof TimeZoneInfo) - return $timeZone; - return new TimeZoneInfo($timeZone->getName()); - } -} - -TimeZoneInfo::construct(); diff --git a/src/XDateTime.php b/src/XDateTime.php new file mode 100644 index 0000000..1e72794 --- /dev/null +++ b/src/XDateTime.php @@ -0,0 +1,103 @@ +format(self::COMPARE_FORMAT); + + if(is_string($dt)) + $dt = strtotime($dt); + + return gmdate(self::COMPARE_FORMAT, $dt); + } + + public static function compare( + DateTimeInterface|string|int $dt1, + DateTimeInterface|string|int $dt2 + ): int { + return strcmp( + self::toCompareFormat($dt1), + self::toCompareFormat($dt2) + ); + } + + public static function compareTimeZone( + DateTimeZone|string $tz1, + DateTimeZone|string $tz2 + ): int { + if(is_string($tz1)) + $tz1 = new DateTimeZone($tz1); + if(is_string($tz2)) + $tz2 = new DateTimeZone($tz2); + + $now = self::now(); + $diff = $tz1->getOffset($now) <=> $tz2->getOffset($now); + if($diff) return $diff; + + return strcmp( + $tz1->getName(), + $tz2->getName() + ); + } + + public static function timeZoneListName(DateTimeZone $dtz): string { + $offset = $dtz->getOffset(self::now()); + return sprintf( + '(UTC%s%s) %s', + $offset < 0 ? '-' : '+', + date('H:i', abs($offset)), + $dtz->getName() + ); + } + + public static function listTimeZones(bool $sort = false, int $group = DateTimeZone::ALL, string|null $countryCode = null): array { + $list = DateTimeZone::listIdentifiers($group, $countryCode); + $list = XArray::select($list, fn($id) => new DateTimeZone($id)); + + if($sort) + $list = XArray::sort($list, self::compareTimeZone(...)); + + return $list; + } + + public static function format(DateTimeInterface|string|int|null $dt, string $format): string { + if($dt === null) { + $dt = time(); + } else { + if($dt instanceof DateTimeInterface) + return $dt->format($format); + + if(is_string($dt)) + $dt = strtotime($dt); + } + + return gmdate($format, $dt); + } + + public static function toISO8601String(DateTimeInterface|string|int|null $dt): string { + return self::format($dt, DateTimeInterface::ATOM); + } + + public static function toCookieString(DateTimeInterface|string|int|null $dt): string { + return self::format($dt, DateTimeInterface::COOKIE); + } + + public static function toRFC822String(DateTimeInterface|string|int|null $dt): string { + return self::format($dt, DateTimeInterface::RFC822); + } +} diff --git a/tests/DateTimeTest.php b/tests/DateTimeTest.php deleted file mode 100644 index a2a6549..0000000 --- a/tests/DateTimeTest.php +++ /dev/null @@ -1,386 +0,0 @@ -assertEquals($index->getYear(), 2021); - $this->assertEquals($index->getMonth(), 6); - $this->assertEquals($index->getDay(), 14); - $this->assertEquals($index->getHour(), 21); - $this->assertEquals($index->getMinute(), 7); - $this->assertEquals($index->getSecond(), 14); - $this->assertEquals($index->getMillisecond(), 359); - $this->assertEquals($index->getMicrosecond(), 359324); - $this->assertEquals($index->getDayOfWeek(), DateTime::MONDAY); - $this->assertEquals($index->getDayOfYear(), 165); - $this->assertEquals($index->getWeek(), 24); - $this->assertTrue($index->isDaylightSavingTime()); - $this->assertFalse($index->isLeapYear()); - $this->assertFalse($index->isUTC()); - - $indexDate = $index->getDate(); - $this->assertEquals($indexDate->getYear(), 2021); - $this->assertEquals($indexDate->getMonth(), 6); - $this->assertEquals($indexDate->getDay(), 14); - $this->assertEquals($indexDate->getHour(), 0); - $this->assertEquals($indexDate->getMinute(), 0); - $this->assertEquals($indexDate->getSecond(), 0); - $this->assertEquals($indexDate->getMillisecond(), 0); - $this->assertEquals($indexDate->getMicrosecond(), 0); - $this->assertEquals($indexDate->getDayOfWeek(), DateTime::MONDAY); - $this->assertEquals($indexDate->getDayOfYear(), 165); - $this->assertEquals($indexDate->getWeek(), 24); - - $indexTime = $index->getTimeOfDay(); - $this->assertEquals($indexTime->getHours(), 21); - $this->assertEquals($indexTime->getMinutes(), 7); - $this->assertEquals($indexTime->getSeconds(), 14); - $this->assertEquals($indexTime->getMilliseconds(), 359); - $this->assertEquals($indexTime->getMicroseconds(), 359324); - } - - public function testTimeZone(): void { - $index1 = DateTime::utcNow(); - $index2 = DateTime::now(new TimeZoneInfo('Australia/Melbourne')); - $this->assertEquals($index1->getUnixTimeSeconds(), $index2->getUnixTimeSeconds()); - } - - public function testSetters(): void { - $index = new DateTime('2021-06-14T21:07:14.359324 CEST'); - $this->assertEquals($index->getYear(), 2021); - $this->assertEquals($index->getMonth(), 6); - $this->assertEquals($index->getDay(), 14); - $this->assertEquals($index->getHour(), 21); - $this->assertEquals($index->getMinute(), 7); - $this->assertEquals($index->getSecond(), 14); - $this->assertEquals($index->getMillisecond(), 359); - $this->assertEquals($index->getMicrosecond(), 359324); - - $index = $index->setDate(2005, 3, 9); - $this->assertEquals($index->getYear(), 2005); - $this->assertEquals($index->getMonth(), 3); - $this->assertEquals($index->getDay(), 9); - $this->assertEquals($index->getHour(), 21); - $this->assertEquals($index->getMinute(), 7); - $this->assertEquals($index->getSecond(), 14); - $this->assertEquals($index->getMillisecond(), 359); - $this->assertEquals($index->getMicrosecond(), 359324); - - $index = $index->setISODate(2011, 20, 5); - $this->assertEquals($index->getYear(), 2011); - $this->assertEquals($index->getMonth(), 5); - $this->assertEquals($index->getDay(), 20); - $this->assertEquals($index->getHour(), 21); - $this->assertEquals($index->getMinute(), 7); - $this->assertEquals($index->getSecond(), 14); - $this->assertEquals($index->getMillisecond(), 359); - $this->assertEquals($index->getMicrosecond(), 359324); - - $index = $index->setTime(10, 46, 50, 987654); - $this->assertEquals($index->getYear(), 2011); - $this->assertEquals($index->getMonth(), 5); - $this->assertEquals($index->getDay(), 20); - $this->assertEquals($index->getHour(), 10); - $this->assertEquals($index->getMinute(), 46); - $this->assertEquals($index->getSecond(), 50); - $this->assertEquals($index->getMillisecond(), 987); - $this->assertEquals($index->getMicrosecond(), 987654); - - $index = $index->setTimestamp(1623702634); - $this->assertEquals($index->getYear(), 2021); - $this->assertEquals($index->getMonth(), 6); - $this->assertEquals($index->getDay(), 14); - $this->assertEquals($index->getHour(), 22); - $this->assertEquals($index->getMinute(), 30); - $this->assertEquals($index->getSecond(), 34); - $this->assertEquals($index->getMillisecond(), 0); - $this->assertEquals($index->getMicrosecond(), 0); - $this->assertFalse($index->isUTC()); - } - - public function testUnixTimestamp(): void { - $index = DateTime::fromUnixTimeSeconds(1623696039); - $this->assertEquals($index->getYear(), 2021); - $this->assertEquals($index->getMonth(), 6); - $this->assertEquals($index->getDay(), 14); - $this->assertEquals($index->getHour(), 18); - $this->assertEquals($index->getMinute(), 40); - $this->assertEquals($index->getSecond(), 39); - $this->assertEquals($index->getMillisecond(), 0); - $this->assertEquals($index->getMicrosecond(), 0); - $this->assertEquals($index->getUnixTimeSeconds(), 1623696039); - $this->assertEquals($index->getUnixTimeMilliseconds(), 1623696039000); - $this->assertTrue($index->isUTC()); - - $index = DateTime::fromUnixTimeMilliseconds(1623696422656); - $this->assertEquals($index->getYear(), 2021); - $this->assertEquals($index->getMonth(), 6); - $this->assertEquals($index->getDay(), 14); - $this->assertEquals($index->getHour(), 18); - $this->assertEquals($index->getMinute(), 47); - $this->assertEquals($index->getSecond(), 2); - $this->assertEquals($index->getMillisecond(), 656); - $this->assertEquals($index->getMicrosecond(), 656000); - $this->assertEquals($index->getUnixTimeSeconds(), 1623696422); - $this->assertEquals($index->getUnixTimeMilliseconds(), 1623696422656); - $this->assertTrue($index->isUTC()); - } - - // verify the results of this at some point - public function testAdding(): void { - $index = new DateTime('2021-06-14T21:07:14.359324Z'); - - // Confirm initial state - $this->assertEquals($index->getYear(), 2021); - $this->assertEquals($index->getMonth(), 6); - $this->assertEquals($index->getDay(), 14); - $this->assertEquals($index->getHour(), 21); - $this->assertEquals($index->getMinute(), 7); - $this->assertEquals($index->getSecond(), 14); - $this->assertEquals($index->getMillisecond(), 359); - $this->assertEquals($index->getMicrosecond(), 359324); - - // Add - $index = $index->add(TimeSpan::create(1, 0, 10, 0)); - $this->assertEquals($index->getDay(), 15); - $this->assertEquals($index->getMinute(), 17); - - // Add years - $index = $index->addYears(1); - $this->assertEquals($index->getYear(), 2022); - $this->assertEquals($index->getMonth(), 6); - $index = $index->addYears(1.5); - $this->assertEquals($index->getYear(), 2023); - $this->assertEquals($index->getMonth(), 12); - - // Add months - $index = $index->addMonths(1); - $this->assertEquals($index->getMonth(), 12); - $this->assertEquals($index->getDay(), 16); - $index = $index->addMonths(1.5); - $this->assertEquals($index->getMonth(), 6); - $this->assertEquals($index->getDay(), 15); - - // Add days - $index = $index->addDays(1); - $this->assertEquals($index->getDay(), 16); - $this->assertEquals($index->getHour(), 21); - $index = $index->addDays(1.5); - $this->assertEquals($index->getDay(), 18); - $this->assertEquals($index->getHour(), 9); - - // Add hours - $index = $index->addHours(1); - $this->assertEquals($index->getHour(), 10); - $this->assertEquals($index->getMinute(), 17); - $index = $index->addHours(1.5); - $this->assertEquals($index->getHour(), 11); - $this->assertEquals($index->getMinute(), 47); - - // Add minutes - $index = $index->addMinutes(1); - $this->assertEquals($index->getMinute(), 48); - $this->assertEquals($index->getSecond(), 14); - $index = $index->addMinutes(1.5); - $this->assertEquals($index->getMinute(), 49); - $this->assertEquals($index->getSecond(), 44); - - // Add seconds - $index = $index->addSeconds(1); - $this->assertEquals($index->getSecond(), 45); - $this->assertEquals($index->getMillisecond(), 359); - $this->assertEquals($index->getMicrosecond(), 359324); - $index = $index->addSeconds(1.5); - $this->assertEquals($index->getSecond(), 46); - $this->assertEquals($index->getMillisecond(), 859); - $this->assertEquals($index->getMicrosecond(), 859324); - - // Add milliseconds - $index = $index->addMilliseconds(1); - $this->assertEquals($index->getMillisecond(), 860); - $this->assertEquals($index->getMicrosecond(), 860324); - $index = $index->addMilliseconds(1.5); - $this->assertEquals($index->getMillisecond(), 861); - $this->assertEquals($index->getMicrosecond(), 861824); - - // Add microseconds - $index = $index->addMicroseconds(1); - $this->assertEquals($index->getMicrosecond(), 861825); - $index = $index->addMicroseconds(1.5); - $this->assertEquals($index->getMicrosecond(), 861826); - } - - public function testSubtract(): void { - $index = new DateTime('2021-06-14T21:07:14.359324Z'); - - // Confirm initial state - $this->assertEquals($index->getYear(), 2021); - $this->assertEquals($index->getMonth(), 6); - $this->assertEquals($index->getDay(), 14); - $this->assertEquals($index->getHour(), 21); - $this->assertEquals($index->getMinute(), 7); - $this->assertEquals($index->getSecond(), 14); - $this->assertEquals($index->getMillisecond(), 359); - $this->assertEquals($index->getMicrosecond(), 359324); - - // Subtract - $index = $index->subtract(TimeSpan::fromDays(400.12)); - $this->assertEquals($index->getYear(), 2020); - $this->assertEquals($index->getMonth(), 5); - $this->assertEquals($index->getDay(), 10); - $this->assertEquals($index->getHour(), 18); - $this->assertEquals($index->getMinute(), 14); - $this->assertEquals($index->getSecond(), 26); - $this->assertEquals($index->getMillisecond(), 359); - $this->assertEquals($index->getMicrosecond(), 359324); - } - - public function testDifference(): void { - $index1 = new DateTime('2021-06-14T21:07:14Z'); - $index2 = DateTime::create(2013, 1, 27, 23, 14, 44, 0, new TimeZoneInfo('Europe/Amsterdam')); - $diff = $index1->difference($index2); - - $this->assertEquals($diff->getDays(), 3061); - $this->assertEquals($diff->getHours(), 22); - $this->assertEquals($diff->getMinutes(), 52); - $this->assertEquals($diff->getSeconds(), 30); - $this->assertTrue($diff->isNegative()); - - $this->assertEquals($diff->totalDays(), 3061.953125); - $this->assertEquals($diff->totalHours(), 73486.875); - $this->assertEquals($diff->totalMinutes(), 4409212.5); - $this->assertEquals($diff->totalSeconds(), 264552750); - $this->assertEquals($diff->totalMilliseconds(), 264552750000); - $this->assertEquals($diff->totalMicroseconds(), 264552750000000); - - $negated = $diff->negate(); - $this->assertEquals($negated->getDays(), 3061); - $this->assertEquals($negated->getHours(), 22); - $this->assertEquals($negated->getMinutes(), 52); - $this->assertEquals($negated->getSeconds(), 30); - $this->assertFalse($negated->isNegative()); - } - - public function testModify(): void { - $index = new DateTime('2021-06-14T21:07:14Z'); - $this->assertEquals($index->getMonth(), 6); - - $index = $index->modify('+1 month'); - $this->assertEquals($index->getMonth(), 7); - } - - public function testCreate(): void { - $index = DateTime::create(2013, 1, 27, 23, 14, 44, 0, new TimeZoneInfo('Europe/Amsterdam')); - $this->assertEquals($index->getYear(), 2013); - $this->assertEquals($index->getMonth(), 1); - $this->assertEquals($index->getDay(), 27); - $this->assertEquals($index->getHour(), 23); - $this->assertEquals($index->getMinute(), 14); - $this->assertEquals($index->getSecond(), 44); - - $index = $index->setTimezone(new TimeZoneInfo('UTC')); - $this->assertEquals($index->getHour(), 22); - - $index = $index->setTimezone(new TimeZoneInfo('Asia/Tokyo')); - $this->assertEquals($index->getDay(), 28); - $this->assertEquals($index->getHour(), 7); - - $index = $index->setTimezone(new TimeZoneInfo('America/New_York')); - $this->assertEquals($index->getDay(), 27); - $this->assertEquals($index->getHour(), 17); - - $index = DateTime::createFromFormat('Y-m-d H:i:s', '2012-05-20 10:14:28'); - $this->assertEquals($index->format('Y-m-d H:i:s'), '2012-05-20 10:14:28'); - - $this->assertFalse(DateTime::createFromFormat('Y-m-d H:i:s', 'mewow')); - - $mutable = new \DateTime('2021-06-14T20:20:46Z'); - - $index = DateTime::createFromInterface($mutable); - $this->assertEquals($index->getUnixTimeMilliseconds(), (float)$mutable->format('Uv')); - $this->assertEquals($index->getTimezone()->getName(), '+00:00'); - - $index = DateTime::createFromMutable($mutable); - $this->assertEquals($index->getUnixTimeMilliseconds(), (float)$mutable->format('Uv')); - } - - public function testToString(): void { - $dateTime = DateTime::create(2013, 1, 27, 23, 14, 44, 0, new TimeZoneInfo('Europe/Amsterdam')); - $this->assertEquals((string)$dateTime, '2013-01-27T23:14:44+01:00'); - $this->assertEquals(json_encode($dateTime), '"2013-01-27T23:14:44+01:00"'); - $this->assertEquals((string)$dateTime->toISO8601String(), '2013-01-27T23:14:44+01:00'); - $this->assertEquals((string)$dateTime->toCookieString(), 'Sunday, 27-Jan-2013 23:14:44 CET'); - $this->assertEquals((string)$dateTime->toRFC822String(), 'Sun, 27 Jan 13 23:14:44 +0100'); - - $dateTime = $dateTime->setTimezone(TimeZoneInfo::utc()); - $this->assertEquals((string)$dateTime, '2013-01-27T22:14:44+00:00'); - $this->assertEquals(json_encode($dateTime), '"2013-01-27T22:14:44+00:00"'); - $this->assertEquals((string)$dateTime->toISO8601String(), '2013-01-27T22:14:44+00:00'); - $this->assertEquals((string)$dateTime->toCookieString(), 'Sunday, 27-Jan-2013 22:14:44 UTC'); - $this->assertEquals((string)$dateTime->toRFC822String(), 'Sun, 27 Jan 13 22:14:44 +0000'); - } - - public function testCompare(): void { - $index1 = new DateTime('2021-06-14T21:07:14Z'); - $index2 = DateTime::create(2013, 1, 27, 23, 14, 44, 0, new TimeZoneInfo('Europe/Amsterdam')); - - $this->assertGreaterThan(0, $index1->compare($index2)); - $this->assertLessThan(0, $index2->compare($index1)); - $this->assertEquals(0, $index1->compare($index1)); - } - - public function testEquals(): void { - $index1 = new DateTime('2021-06-14T21:07:14Z'); - $index2 = DateTime::create(2013, 1, 27, 23, 14, 44, 0, new TimeZoneInfo('Europe/Amsterdam')); - - $this->assertFalse($index1->equals($index2)); - $this->assertFalse($index2->equals($index1)); - $this->assertTrue($index1->equals($index1)); - } - - public function testCasting(): void { - $nativeDT = new \DateTime; - $this->assertInstanceOf(DateTime::class, $nativeDTCast = DateTime::cast($nativeDT)); - $this->assertNotSame($nativeDT, $nativeDTCast); - - $indexDT = new DateTime; - $this->assertInstanceOf(DateTime::class, $indexDTCast = DateTime::cast($indexDT)); - $this->assertSame($indexDT, $indexDTCast); - } - - public function testTimeZoneOrder(): void { - $all = TimeZoneInfo::all(true); - $lastOffset = null; - $lastString = null; - - foreach($all as $timeZone) { - $offset = $timeZone->getOffset(); - $string = $timeZone->getName(); - - if($lastOffset !== null) { - $diff = $lastOffset <=> $offset; - $this->assertLessThanOrEqual(0, $diff); - - if($diff === 0) - $this->assertLessThanOrEqual(0, $lastString <=> $string); - } - - $lastOffset = $offset; - $lastString = $string; - } - } -} diff --git a/tests/XDateTimeTest.php b/tests/XDateTimeTest.php new file mode 100644 index 0000000..66aef88 --- /dev/null +++ b/tests/XDateTimeTest.php @@ -0,0 +1,66 @@ +assertEquals(XDateTime::toISO8601String($dateTime), '2013-01-27T23:14:44+01:00'); + $this->assertEquals(XDateTime::toCookieString($dateTime), 'Sunday, 27-Jan-2013 23:14:44 CET'); + $this->assertEquals(XDateTime::toRFC822String($dateTime), 'Sun, 27 Jan 13 23:14:44 +0100'); + + $dateTime = $dateTime->setTimezone(new DateTimeZone('UTC')); + $this->assertEquals(XDateTime::toISO8601String($dateTime), '2013-01-27T22:14:44+00:00'); + $this->assertEquals(XDateTime::toCookieString($dateTime), 'Sunday, 27-Jan-2013 22:14:44 UTC'); + $this->assertEquals(XDateTime::toRFC822String($dateTime), 'Sun, 27 Jan 13 22:14:44 +0000'); + + $this->assertEquals(XDateTime::toISO8601String(1359324884), '2013-01-27T22:14:44+00:00'); + $this->assertEquals(XDateTime::toCookieString(1359324884), 'Sunday, 27-Jan-2013 22:14:44 GMT'); + $this->assertEquals(XDateTime::toRFC822String(1359324884), 'Sun, 27 Jan 13 22:14:44 +0000'); + + $this->assertEquals(XDateTime::toISO8601String('January 27th 2013 22:14:44 UTC'), '2013-01-27T22:14:44+00:00'); + $this->assertEquals(XDateTime::toCookieString('January 27th 2013 22:14:44 UTC'), 'Sunday, 27-Jan-2013 22:14:44 GMT'); + $this->assertEquals(XDateTime::toRFC822String('January 27th 2013 22:14:44 UTC'), 'Sun, 27 Jan 13 22:14:44 +0000'); + } + + public function testCompare(): void { + $dt1 = '2021-06-14T21:07:14Z'; + $dt2 = new DateTimeImmutable('2013-01-27T23:14:44', new DateTimeZone('Europe/Amsterdam')); + + $this->assertGreaterThan(0, XDateTime::compare($dt1, $dt2)); + $this->assertLessThan(0, XDateTime::compare($dt2, $dt1)); + $this->assertEquals(0, XDateTime::compare($dt1, $dt1)); + $this->assertEquals(0, XDateTime::compare($dt2, $dt2)); + } + + public function testTimeZoneOrder(): void { + $timeZones = XDateTime::listTimeZones(true); + $lastOffset = null; + $lastString = null; + + $now = XDateTime::now(); + foreach($timeZones as $timeZone) { + $offset = $timeZone->getOffset($now); + $string = $timeZone->getName(); + + if($lastOffset !== null) { + $diff = $lastOffset <=> $offset; + $this->assertLessThanOrEqual(0, $diff); + + if($diff === 0) + $this->assertLessThanOrEqual(0, $lastString <=> $string); + } + + $lastOffset = $offset; + $lastString = $string; + } + } +} diff --git a/tools/update-headers.php b/tools/update-headers.php index cb63a9b..cfceded 100644 --- a/tools/update-headers.php +++ b/tools/update-headers.php @@ -56,7 +56,7 @@ $files = array_merge($sources, $tests); $topDir = dirname(__DIR__) . DIRECTORY_SEPARATOR; -$now = date('Y-m-d'); +$now = gmdate('Y-m-d'); foreach($files as $file) { echo 'Scanning ' . str_replace($topDir, '', $file) . '...' . PHP_EOL; diff --git a/tools/update-version.php b/tools/update-version.php index 0a1acbc..ec5a913 100644 --- a/tools/update-version.php +++ b/tools/update-version.php @@ -3,7 +3,7 @@ date_default_timezone_set('utc'); -$version = date('0.ym.jHi'); +$version = gmdate('0.ym.jHi'); echo 'Updating VERSION file to ' . $version . PHP_EOL;