index/src/TimeZoneInfo.php

214 lines
6.4 KiB
PHP

<?php
// TimeZoneInfo.php
// Created: 2021-06-12
// Updated: 2022-02-27
namespace Index;
use DateTimeInterface;
use DateTimeZone;
use JsonSerializable;
use RuntimeException;
use Stringable;
/**
* Represents a time zone.
*/
class TimeZoneInfo extends DateTimeZone implements JsonSerializable, Stringable, IComparable, IEquatable {
private array|null $location = null;
private static bool $constructed = false;
private static TimeZoneInfo $utc;
/**
* Constructs a new TimeZoneInfo object.
*
* @param string $timeZone Time zone identifier.,
* @return TimeZoneInfo New instance of TimeZoneInfo.
*/
public function __construct(string $timeZone) {
parent::__construct($timeZone);
}
/**
* @internal
*/
public static function construct(): void {
if(self::$constructed)
throw new RuntimeException('Static constructor was already called.');
self::$constructed = true;
self::$utc = new TimeZoneInfo('UTC');
}
/**
* Gets a TimeZoneInfo instance representing the UTC (+00:00) time zone.
*
* @return TimeZoneInfo Instance representing UTC.
*/
public static function utc(): TimeZoneInfo {
return self::$utc;
}
/**
* Gets the current default time zone.
*
* Uses date_default_timezone_get internally.
*
* @return TimeZoneInfo Instance representing the default time zone.
*/
public static function default(): TimeZoneInfo {
return new TimeZoneInfo(date_default_timezone_get());
}
/**
* Sets the current default time zone.
*
* Uses date_default_timezone_set internally.
*
* @param DateTimeZone|string $timeZone Desired default time zone.
* @throws RuntimeException If an invalid time zone was supplied.
*/
public static function setDefault(DateTimeZone|string $timeZone): void {
if($timeZone instanceof DateTimeZone)
$timeZone = $timeZone->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();