1: <?php declare(strict_types=1);
2:
3: namespace Salient\Utility;
4:
5: use DateInterval;
6: use DateTimeImmutable;
7: use DateTimeInterface;
8: use DateTimeZone;
9: use InvalidArgumentException;
10:
11: /**
12: * Work with date and time values, timezones and intervals
13: *
14: * @api
15: */
16: final class Date extends AbstractUtility
17: {
18: /**
19: * Get a DateTimeImmutable from a DateTimeInterface
20: *
21: * @param DateTimeInterface|null $datetime If `null`, the current time is
22: * used.
23: */
24: public static function immutable(?DateTimeInterface $datetime = null): DateTimeImmutable
25: {
26: return $datetime instanceof DateTimeImmutable
27: ? $datetime
28: : ($datetime
29: ? DateTimeImmutable::createFromMutable($datetime)
30: : new DateTimeImmutable());
31: }
32:
33: /**
34: * Get a DateTimeZone from a string or DateTimeZone
35: *
36: * @param DateTimeZone|string|null $timezone If `null`, the timezone
37: * returned by {@see date_default_timezone_get()} is used.
38: */
39: public static function timezone($timezone = null): DateTimeZone
40: {
41: if ($timezone instanceof DateTimeZone) {
42: return $timezone;
43: }
44: if ($timezone === null) {
45: $timezone = date_default_timezone_get();
46: }
47: return new DateTimeZone($timezone);
48: }
49:
50: /**
51: * Get a DateInterval or ISO-8601 duration in seconds
52: *
53: * @param DateInterval|string $interval
54: */
55: public static function duration($interval): int
56: {
57: if (!$interval instanceof DateInterval) {
58: if (
59: \PHP_VERSION_ID < 80000
60: && Regex::match('/W.+D/', $interval)
61: ) {
62: throw new InvalidArgumentException(sprintf(
63: 'Invalid $interval: %s',
64: $interval,
65: ));
66: }
67: $interval = new DateInterval($interval);
68: }
69:
70: $then = new DateTimeImmutable();
71: $now = $then->add($interval);
72:
73: return $now->getTimestamp() - $then->getTimestamp();
74: }
75:
76: /**
77: * Set the timezone of a date and time if not already set
78: *
79: * @param DateTimeZone|string|null $timezone If `null`, the timezone
80: * returned by {@see date_default_timezone_get()} is used.
81: */
82: public static function maybeSetTimezone(DateTimeInterface $datetime, $timezone = null): DateTimeImmutable
83: {
84: $datetime = self::immutable($datetime);
85: $tz = $datetime->getTimezone()->getName();
86: if (
87: ($tz === 'UTC' || $tz === '+00:00')
88: && ($timezone !== null || date_default_timezone_get() !== 'UTC')
89: ) {
90: $timezone = self::timezone($timezone);
91: if ($tz !== $timezone->getName()) {
92: return $datetime->setTimezone($timezone);
93: }
94: }
95: return $datetime;
96: }
97: }
98: