index/src/DateTime.php

668 lines
21 KiB
PHP

<?php
// DateTime.php
// Created: 2021-06-11
// Updated: 2023-01-01
namespace Index;
use DateInterval;
use DateTimeImmutable;
use DateTimeInterface;
use DateTimeZone;
use InvalidArgumentException;
use JsonSerializable;
use RuntimeException;
use Stringable;
/**
* Represents a date and time
*/
class DateTime extends DateTimeImmutable implements JsonSerializable, Stringable, IComparable, IEquatable {
/**
* Represents Sunday for getDayOfWeek.
*
* @var int
*/
public const SUNDAY = 0;
/**
* Represents Monday for getDayOfWeek.
*
* @var int
*/
public const MONDAY = 1;
/**
* Represents Tuesday for getDayOfWeek.
*
* @var int
*/
public const TUESDAY = 2;
/**
* Represents Wednesday for getDayOfWeek.
*
* @var int
*/
public const WEDNESDAY = 3;
/**
* Represents Thursday for getDayOfWeek.
*
* @var int
*/
public const THURSDAY = 4;
/**
* Represents Friday for getDayOfWeek.
*
* @var int
*/
public const FRIDAY = 5;
/**
* Represents Saturday for getDayOfWeek.
*
* @var int
*/
public const SATURDAY = 6;
private const COMPARE = 'YmdHisu';
/**
* Creates a new DateTime object.
*
* @see https://www.php.net/manual/en/datetime.formats.php
* @param string $dateTime A date/time string.
* @param DateTimeZone|null $timeZone An object representing the desired time zone.
* @return DateTime A new DateTime instance.
*/
public function __construct(string $dateTime = 'now', DateTimeZone|null $timeZone = null) {
parent::__construct($dateTime, $timeZone);
}
/**
* Gets the date component of this instance.
*
* @return DateTime New instance with the same date but the time set to 00:00:00.
*/
public function getDate(): DateTime {
return self::create($this->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));
}
/**
* Alias for subtract, must exist because of the DateTimeImmutable inheritance but I don't like short names.
*
* @internal
*/
public function sub(DateInterval $interval): DateTime {
return $this->subtract($interval);
}
/**
* 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());
}
}