PluginProbe ʕ •ᴥ•ʔ
Kirki – Freeform Page Builder, Website Builder & Customizer / 6.2.0
Kirki – Freeform Page Builder, Website Builder & Customizer v6.2.0
6.2.1 6.2.0 6.1.1 6.1.0 6.0.14 6.0.13 6.0.12 6.0.11 6.0.10 6.0.9 6.0.8 6.0.7 6.0.6 6.0.5 6.0.4 6.0.3 6.0.2 6.0.1 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 4.0.19 4.0.20 4.0.21 4.0.22 4.0.23 4.0.24 4.1 4.2.0 5.0.0 5.1.0 5.1.1 5.2.0 5.2.1 5.2.2 5.2.3 6.0.0 trunk 3.0.40 3.0.41 3.0.42 3.0.43 3.0.44 3.0.45 3.1.0 3.1.1 3.1.2
kirki / libraries / framework / Supports / Somoy.php
kirki / libraries / framework / Supports Last commit date
Facades 1 week ago Traits 1 month ago Arr.php 1 week ago DataCaster.php 1 month ago Fluent.php 1 week ago HigherOrderTapProxy.php 1 month ago MediaAttachment.php 1 month ago MessagesBag.php 1 week ago Somoy.php 1 week ago Str.php 1 week ago Url.php 1 month ago Utils.php 1 month ago
Somoy.php
1205 lines
1 <?php
2
3 /**
4 * A mutable date and time value object.
5 *
6 * The default implementation of {@see \Framework\Contracts\SomoyInterface}. Code
7 * that consumes dates should type against that contract rather than this
8 * class, so the implementation can be swapped later.
9 *
10 * Extends the native DateTime so instances keep satisfying the
11 * `instanceof DateTimeInterface` checks the query layer relies on, while
12 * exposing a snake_case API for everything the plugin needs.
13 *
14 * Every method this class defines is snake_case. The camelCase methods
15 * inherited from DateTime (format(), getTimestamp(), setTimezone(), ...) are
16 * still reachable because PHP does not allow hiding them, but each one used by
17 * the plugin has a snake_case counterpart below which should be preferred.
18 *
19 * All mutating methods change the instance in place and return `$this`, so
20 * calls can be chained. Use copy() first when the original must be preserved.
21 *
22 * @package Framework
23 * @subpackage Supports
24 * @since 1.0.0
25 */
26 namespace Kirki\Framework\Supports;
27
28 \defined('ABSPATH') || exit;
29 use BadMethodCallException;
30 use DateTime;
31 use DateTimeInterface;
32 use DateTimeZone;
33 use Exception;
34 use Kirki\Framework\Constants\DateTimeFormats;
35 use Kirki\Framework\Contracts\SomoyInterface;
36 use Kirki\Framework\Exceptions\InvalidDateFormatException;
37 use InvalidArgumentException;
38 class Somoy extends DateTime implements SomoyInterface
39 {
40 /**
41 * The date only format.
42 *
43 * @var string
44 *
45 * @since 1.0.0
46 */
47 public const DATE_FORMAT = DateTimeFormats::DB_DATE;
48 /**
49 * The time only format.
50 *
51 * @var string
52 *
53 * @since 1.0.0
54 */
55 public const TIME_FORMAT = 'H:i:s';
56 /**
57 * The combined date and time format.
58 *
59 * @var string
60 *
61 * @since 1.0.0
62 */
63 public const DATETIME_FORMAT = DateTimeFormats::DB_DATETIME;
64 /**
65 * The ISO-8601 format including the timezone offset.
66 *
67 * @var string
68 *
69 * @since 1.0.0
70 */
71 public const ISO8601_FORMAT = 'Y-m-d\\TH:i:sP';
72 /**
73 * The format used when serializing to JSON. Always emitted in UTC with a
74 * trailing "Z", so the microseconds are part of the format.
75 *
76 * @var string
77 *
78 * @since 1.0.0
79 */
80 public const JSON_FORMAT = 'Y-m-d\\TH:i:s.u';
81 /**
82 * The units that may be read as properties.
83 *
84 * @var array
85 *
86 * @since 1.0.0
87 */
88 protected static $readable_units = ['year' => 'Y', 'month' => 'n', 'day' => 'j', 'hour' => 'G', 'minute' => 'i', 'second' => 's', 'microsecond' => 'u', 'timestamp' => 'U', 'day_of_week' => 'w', 'day_of_year' => 'z', 'days_in_month' => 't', 'week_of_year' => 'W'];
89 /**
90 * Get an instance for the current date and time.
91 *
92 * @param DateTimeZone|string|null $timezone The timezone to use.
93 *
94 * @return static The current date and time.
95 *
96 * @since 1.0.0
97 */
98 public static function now($timezone = null)
99 {
100 return static::parse('now', $timezone);
101 }
102 /**
103 * Get an instance for today at midnight.
104 *
105 * @param DateTimeZone|string|null $timezone The timezone to use.
106 *
107 * @return static Today at midnight.
108 *
109 * @since 1.0.0
110 */
111 public static function today($timezone = null)
112 {
113 return static::now($timezone)->start_of_day();
114 }
115 /**
116 * Get an instance for yesterday at midnight.
117 *
118 * @param DateTimeZone|string|null $timezone The timezone to use.
119 *
120 * @return static Yesterday at midnight.
121 *
122 * @since 1.0.0
123 */
124 public static function yesterday($timezone = null)
125 {
126 return static::today($timezone)->sub_day();
127 }
128 /**
129 * Get an instance for tomorrow at midnight.
130 *
131 * @param DateTimeZone|string|null $timezone The timezone to use.
132 *
133 * @return static Tomorrow at midnight.
134 *
135 * @since 1.0.0
136 */
137 public static function tomorrow($timezone = null)
138 {
139 return static::today($timezone)->add_day();
140 }
141 /**
142 * Parse a value into a date instance.
143 *
144 * Integers and floats are read as unix timestamps. Null and empty strings
145 * resolve to the current date and time.
146 *
147 * @param DateTimeInterface|string|int|float|null $time The value to parse.
148 * @param DateTimeZone|string|null $timezone The timezone to use.
149 *
150 * @return static The parsed date.
151 *
152 * @throws InvalidDateFormatException When the value cannot be parsed.
153 *
154 * @since 1.0.0
155 */
156 public static function parse($time = null, $timezone = null)
157 {
158 if ($time instanceof DateTimeInterface) {
159 $instance = static::instance($time);
160 return $timezone === null ? $instance : $instance->set_timezone($timezone);
161 }
162 if (\is_int($time) || \is_float($time)) {
163 $instance = static::from_timestamp($time);
164 return $timezone === null ? $instance : $instance->set_timezone($timezone);
165 }
166 if (\is_bool($time)) {
167 $time = (string) $time;
168 }
169 if ($time !== null && !\is_string($time)) {
170 throw new InvalidDateFormatException(\sprintf('Could not parse a value of type %s as a date.', \gettype($time)));
171 }
172 try {
173 return new static($time === null || $time === '' ? 'now' : $time, static::resolve_timezone($timezone));
174 } catch (Exception $exception) {
175 throw new InvalidDateFormatException(\sprintf('Could not parse "%s" as a date.', $time), 0, $exception);
176 }
177 }
178 /**
179 * Create an instance from any other date object.
180 *
181 * The microseconds and the timezone of the given date are preserved.
182 *
183 * @param DateTimeInterface $date The date to copy.
184 *
185 * @return static The new instance.
186 *
187 * @since 1.0.0
188 */
189 public static function instance(DateTimeInterface $date)
190 {
191 return new static($date->format('Y-m-d H:i:s.u'), $date->getTimezone() ?: null);
192 }
193 /**
194 * Create an instance from a unix timestamp.
195 *
196 * Unlike parse(), a timestamp without an explicit timezone is expressed in
197 * the default timezone rather than in UTC.
198 *
199 * @param int|float|string $timestamp The unix timestamp.
200 * @param DateTimeZone|string|null $timezone The timezone to use.
201 *
202 * @return static The new instance.
203 *
204 * @throws InvalidDateFormatException When the timestamp is not numeric.
205 *
206 * @since 1.0.0
207 */
208 public static function create_from_timestamp($timestamp, $timezone = null)
209 {
210 return static::from_timestamp($timestamp)->set_timezone($timezone === null ? \date_default_timezone_get() : $timezone);
211 }
212 /**
213 * Create an instance from a value matching the given format.
214 *
215 * @param string $format The format the value is written in.
216 * @param string $time The value to read.
217 * @param DateTimeZone|string|null $timezone The timezone to use.
218 *
219 * @return static The new instance.
220 *
221 * @throws InvalidDateFormatException When the value does not match the format.
222 *
223 * @since 1.0.0
224 */
225 public static function create_from_format($format, $time, $timezone = null)
226 {
227 $timezone = static::resolve_timezone($timezone);
228 $date = $timezone === null ? DateTime::createFromFormat($format, $time) : DateTime::createFromFormat($format, $time, $timezone);
229 if (!$date instanceof DateTimeInterface) {
230 throw new InvalidDateFormatException(\sprintf('Could not parse "%s" using the format "%s".', \is_scalar($time) ? $time : \gettype($time), $format));
231 }
232 return static::instance($date);
233 }
234 /**
235 * Create an instance from the given date and time parts.
236 *
237 * Any part left as null falls back to the matching part of the current
238 * date and time.
239 *
240 * @param int|null $year The year.
241 * @param int|null $month The month.
242 * @param int|null $day The day.
243 * @param int|null $hour The hour.
244 * @param int|null $minute The minute.
245 * @param int|null $second The second.
246 * @param DateTimeZone|string|null $timezone The timezone to use.
247 *
248 * @return static The new instance.
249 *
250 * @since 1.0.0
251 */
252 public static function create($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $timezone = null)
253 {
254 $instance = static::now($timezone);
255 return $instance->set_date($year === null ? (int) $instance->format('Y') : (int) $year, $month === null ? (int) $instance->format('n') : (int) $month, $day === null ? (int) $instance->format('j') : (int) $day)->set_time($hour === null ? (int) $instance->format('G') : (int) $hour, $minute === null ? (int) $instance->format('i') : (int) $minute, $second === null ? (int) $instance->format('s') : (int) $second);
256 }
257 /**
258 * Check if the value is a valid date.
259 *
260 * @param mixed $value The value to check.
261 *
262 * @return bool Whether the value can be read as a date.
263 *
264 * @since 1.0.0
265 */
266 public static function is_valid_date($value)
267 {
268 if ($value instanceof DateTimeInterface) {
269 return \true;
270 }
271 try {
272 static::parse($value);
273 return \true;
274 } catch (InvalidDateFormatException $exception) {
275 return \false;
276 }
277 }
278 /**
279 * Get a copy of the instance.
280 *
281 * @return static The copied instance.
282 *
283 * @since 1.0.0
284 */
285 public function copy()
286 {
287 return clone $this;
288 }
289 /**
290 * Add the given number of seconds.
291 *
292 * @param int $value The number of seconds.
293 *
294 * @return $this The mutated instance.
295 *
296 * @since 1.0.0
297 */
298 public function add_seconds($value = 1)
299 {
300 return $this->shift('second', $value);
301 }
302 /**
303 * Add the given number of minutes.
304 *
305 * @param int $value The number of minutes.
306 *
307 * @return $this The mutated instance.
308 *
309 * @since 1.0.0
310 */
311 public function add_minutes($value = 1)
312 {
313 return $this->shift('minute', $value);
314 }
315 /**
316 * Add the given number of hours.
317 *
318 * @param int $value The number of hours.
319 *
320 * @return $this The mutated instance.
321 *
322 * @since 1.0.0
323 */
324 public function add_hours($value = 1)
325 {
326 return $this->shift('hour', $value);
327 }
328 /**
329 * Add the given number of days.
330 *
331 * @param int $value The number of days.
332 *
333 * @return $this The mutated instance.
334 *
335 * @since 1.0.0
336 */
337 public function add_days($value = 1)
338 {
339 return $this->shift('day', $value);
340 }
341 /**
342 * Add the given number of weeks.
343 *
344 * @param int $value The number of weeks.
345 *
346 * @return $this The mutated instance.
347 *
348 * @since 1.0.0
349 */
350 public function add_weeks($value = 1)
351 {
352 return $this->shift('week', $value);
353 }
354 /**
355 * Add the given number of months.
356 *
357 * Overflows into the next month when the resulting month is shorter, which
358 * is the behaviour of the native date arithmetic.
359 *
360 * @param int $value The number of months.
361 *
362 * @return $this The mutated instance.
363 *
364 * @since 1.0.0
365 */
366 public function add_months($value = 1)
367 {
368 return $this->shift('month', $value);
369 }
370 /**
371 * Add the given number of years.
372 *
373 * @param int $value The number of years.
374 *
375 * @return $this The mutated instance.
376 *
377 * @since 1.0.0
378 */
379 public function add_years($value = 1)
380 {
381 return $this->shift('year', $value);
382 }
383 /**
384 * Subtract the given number of seconds.
385 *
386 * @param int $value The number of seconds.
387 *
388 * @return $this The mutated instance.
389 *
390 * @since 1.0.0
391 */
392 public function sub_seconds($value = 1)
393 {
394 return $this->shift('second', -$value);
395 }
396 /**
397 * Subtract the given number of minutes.
398 *
399 * @param int $value The number of minutes.
400 *
401 * @return $this The mutated instance.
402 *
403 * @since 1.0.0
404 */
405 public function sub_minutes($value = 1)
406 {
407 return $this->shift('minute', -$value);
408 }
409 /**
410 * Subtract the given number of hours.
411 *
412 * @param int $value The number of hours.
413 *
414 * @return $this The mutated instance.
415 *
416 * @since 1.0.0
417 */
418 public function sub_hours($value = 1)
419 {
420 return $this->shift('hour', -$value);
421 }
422 /**
423 * Subtract the given number of days.
424 *
425 * @param int $value The number of days.
426 *
427 * @return $this The mutated instance.
428 *
429 * @since 1.0.0
430 */
431 public function sub_days($value = 1)
432 {
433 return $this->shift('day', -$value);
434 }
435 /**
436 * Subtract the given number of weeks.
437 *
438 * @param int $value The number of weeks.
439 *
440 * @return $this The mutated instance.
441 *
442 * @since 1.0.0
443 */
444 public function sub_weeks($value = 1)
445 {
446 return $this->shift('week', -$value);
447 }
448 /**
449 * Subtract the given number of months.
450 *
451 * @param int $value The number of months.
452 *
453 * @return $this The mutated instance.
454 *
455 * @since 1.0.0
456 */
457 public function sub_months($value = 1)
458 {
459 return $this->shift('month', -$value);
460 }
461 /**
462 * Subtract the given number of years.
463 *
464 * @param int $value The number of years.
465 *
466 * @return $this The mutated instance.
467 *
468 * @since 1.0.0
469 */
470 public function sub_years($value = 1)
471 {
472 return $this->shift('year', -$value);
473 }
474 /**
475 * Add a single second.
476 *
477 * @return $this The mutated instance.
478 *
479 * @since 1.0.0
480 */
481 public function add_second()
482 {
483 return $this->add_seconds(1);
484 }
485 /**
486 * Add a single minute.
487 *
488 * @return $this The mutated instance.
489 *
490 * @since 1.0.0
491 */
492 public function add_minute()
493 {
494 return $this->add_minutes(1);
495 }
496 /**
497 * Add a single hour.
498 *
499 * @return $this The mutated instance.
500 *
501 * @since 1.0.0
502 */
503 public function add_hour()
504 {
505 return $this->add_hours(1);
506 }
507 /**
508 * Add a single day.
509 *
510 * @return $this The mutated instance.
511 *
512 * @since 1.0.0
513 */
514 public function add_day()
515 {
516 return $this->add_days(1);
517 }
518 /**
519 * Add a single week.
520 *
521 * @return $this The mutated instance.
522 *
523 * @since 1.0.0
524 */
525 public function add_week()
526 {
527 return $this->add_weeks(1);
528 }
529 /**
530 * Add a single month.
531 *
532 * @return $this The mutated instance.
533 *
534 * @since 1.0.0
535 */
536 public function add_month()
537 {
538 return $this->add_months(1);
539 }
540 /**
541 * Add a single year.
542 *
543 * @return $this The mutated instance.
544 *
545 * @since 1.0.0
546 */
547 public function add_year()
548 {
549 return $this->add_years(1);
550 }
551 /**
552 * Subtract a single second.
553 *
554 * @return $this The mutated instance.
555 *
556 * @since 1.0.0
557 */
558 public function sub_second()
559 {
560 return $this->sub_seconds(1);
561 }
562 /**
563 * Subtract a single minute.
564 *
565 * @return $this The mutated instance.
566 *
567 * @since 1.0.0
568 */
569 public function sub_minute()
570 {
571 return $this->sub_minutes(1);
572 }
573 /**
574 * Subtract a single hour.
575 *
576 * @return $this The mutated instance.
577 *
578 * @since 1.0.0
579 */
580 public function sub_hour()
581 {
582 return $this->sub_hours(1);
583 }
584 /**
585 * Subtract a single day.
586 *
587 * @return $this The mutated instance.
588 *
589 * @since 1.0.0
590 */
591 public function sub_day()
592 {
593 return $this->sub_days(1);
594 }
595 /**
596 * Subtract a single week.
597 *
598 * @return $this The mutated instance.
599 *
600 * @since 1.0.0
601 */
602 public function sub_week()
603 {
604 return $this->sub_weeks(1);
605 }
606 /**
607 * Subtract a single month.
608 *
609 * @return $this The mutated instance.
610 *
611 * @since 1.0.0
612 */
613 public function sub_month()
614 {
615 return $this->sub_months(1);
616 }
617 /**
618 * Subtract a single year.
619 *
620 * @return $this The mutated instance.
621 *
622 * @since 1.0.0
623 */
624 public function sub_year()
625 {
626 return $this->sub_years(1);
627 }
628 /**
629 * Move the instance to the first moment of its day.
630 *
631 * @return $this The mutated instance.
632 *
633 * @since 1.0.0
634 */
635 public function start_of_day()
636 {
637 return $this->set_time(0, 0, 0, 0);
638 }
639 /**
640 * Move the instance to the last moment of its day.
641 *
642 * @return $this The mutated instance.
643 *
644 * @since 1.0.0
645 */
646 public function end_of_day()
647 {
648 return $this->set_time(23, 59, 59, 999999);
649 }
650 /**
651 * Move the instance to the first moment of its week, weeks starting on Monday.
652 *
653 * @return $this The mutated instance.
654 *
655 * @since 1.0.0
656 */
657 public function start_of_week()
658 {
659 return $this->sub_days((int) $this->format('N') - 1)->start_of_day();
660 }
661 /**
662 * Move the instance to the last moment of its week, weeks ending on Sunday.
663 *
664 * @return $this The mutated instance.
665 *
666 * @since 1.0.0
667 */
668 public function end_of_week()
669 {
670 return $this->start_of_week()->add_days(6)->end_of_day();
671 }
672 /**
673 * Move the instance to the first moment of its month.
674 *
675 * @return $this The mutated instance.
676 *
677 * @since 1.0.0
678 */
679 public function start_of_month()
680 {
681 return $this->set_date((int) $this->format('Y'), (int) $this->format('n'), 1)->start_of_day();
682 }
683 /**
684 * Move the instance to the last moment of its month.
685 *
686 * @return $this The mutated instance.
687 *
688 * @since 1.0.0
689 */
690 public function end_of_month()
691 {
692 return $this->set_date((int) $this->format('Y'), (int) $this->format('n'), (int) $this->format('t'))->end_of_day();
693 }
694 /**
695 * Move the instance to the first moment of its year.
696 *
697 * @return $this The mutated instance.
698 *
699 * @since 1.0.0
700 */
701 public function start_of_year()
702 {
703 return $this->set_date((int) $this->format('Y'), 1, 1)->start_of_day();
704 }
705 /**
706 * Move the instance to the last moment of its year.
707 *
708 * @return $this The mutated instance.
709 *
710 * @since 1.0.0
711 */
712 public function end_of_year()
713 {
714 return $this->set_date((int) $this->format('Y'), 12, 31)->end_of_day();
715 }
716 /**
717 * Move the instance to the first day of its month at midnight.
718 *
719 * @return $this The mutated instance.
720 *
721 * @since 1.0.0
722 */
723 public function first_of_month()
724 {
725 return $this->start_of_month();
726 }
727 /**
728 * Move the instance to the last day of its month at midnight.
729 *
730 * @return $this The mutated instance.
731 *
732 * @since 1.0.0
733 */
734 public function last_of_month()
735 {
736 return $this->set_date((int) $this->format('Y'), (int) $this->format('n'), (int) $this->format('t'))->start_of_day();
737 }
738 /**
739 * Determine whether the instance is equal to the given date.
740 *
741 * @param DateTimeInterface|string|int|float|null $date The date to compare with.
742 *
743 * @return bool True when both represent the same moment.
744 *
745 * @since 1.0.0
746 */
747 public function eq($date)
748 {
749 return $this == $this->resolve($date);
750 }
751 /**
752 * Determine whether the instance is different from the given date.
753 *
754 * @param DateTimeInterface|string|int|float|null $date The date to compare with.
755 *
756 * @return bool True when both represent a different moment.
757 *
758 * @since 1.0.0
759 */
760 public function ne($date)
761 {
762 return !$this->eq($date);
763 }
764 /**
765 * Determine whether the instance is later than the given date.
766 *
767 * @param DateTimeInterface|string|int|float|null $date The date to compare with.
768 *
769 * @return bool True when the instance is later.
770 *
771 * @since 1.0.0
772 */
773 public function gt($date)
774 {
775 return $this > $this->resolve($date);
776 }
777 /**
778 * Determine whether the instance is later than or equal to the given date.
779 *
780 * @param DateTimeInterface|string|int|float|null $date The date to compare with.
781 *
782 * @return bool True when the instance is later or equal.
783 *
784 * @since 1.0.0
785 */
786 public function gte($date)
787 {
788 return $this >= $this->resolve($date);
789 }
790 /**
791 * Determine whether the instance is earlier than the given date.
792 *
793 * @param DateTimeInterface|string|int|float|null $date The date to compare with.
794 *
795 * @return bool True when the instance is earlier.
796 *
797 * @since 1.0.0
798 */
799 public function lt($date)
800 {
801 return $this < $this->resolve($date);
802 }
803 /**
804 * Determine whether the instance is earlier than or equal to the given date.
805 *
806 * @param DateTimeInterface|string|int|float|null $date The date to compare with.
807 *
808 * @return bool True when the instance is earlier or equal.
809 *
810 * @since 1.0.0
811 */
812 public function lte($date)
813 {
814 return $this <= $this->resolve($date);
815 }
816 /**
817 * Determine whether the instance is later than the given date.
818 *
819 * @param DateTimeInterface|string|int|float|null $date The date to compare with.
820 *
821 * @return bool True when the instance is later.
822 *
823 * @since 1.0.0
824 */
825 public function is_after($date)
826 {
827 return $this->gt($date);
828 }
829 /**
830 * Determine whether the instance is earlier than the given date.
831 *
832 * @param DateTimeInterface|string|int|float|null $date The date to compare with.
833 *
834 * @return bool True when the instance is earlier.
835 *
836 * @since 1.0.0
837 */
838 public function is_before($date)
839 {
840 return $this->lt($date);
841 }
842 /**
843 * Determine whether the instance falls on the same day as the given date.
844 *
845 * @param DateTimeInterface|string|int|float|null $date The date to compare with.
846 *
847 * @return bool True when both fall on the same calendar day.
848 *
849 * @since 1.0.0
850 */
851 public function is_same_day($date)
852 {
853 return $this->format(static::DATE_FORMAT) === $this->resolve($date)->format(static::DATE_FORMAT);
854 }
855 /**
856 * Determine whether the instance falls between the given dates, bounds included.
857 *
858 * @param DateTimeInterface|string|int|float|null $start The lower bound.
859 * @param DateTimeInterface|string|int|float|null $end The upper bound.
860 *
861 * @return bool True when the instance falls within the range.
862 *
863 * @since 1.0.0
864 */
865 public function between($start, $end)
866 {
867 return $this->gte($start) && $this->lte($end);
868 }
869 /**
870 * Format the instance as a date.
871 *
872 * @return string The formatted date.
873 *
874 * @since 1.0.0
875 */
876 public function to_date_string()
877 {
878 return $this->format(static::DATE_FORMAT);
879 }
880 /**
881 * Format the instance as a time.
882 *
883 * @return string The formatted time.
884 *
885 * @since 1.0.0
886 */
887 public function to_time_string()
888 {
889 return $this->format(static::TIME_FORMAT);
890 }
891 /**
892 * Format the instance as a date and time.
893 *
894 * @return string The formatted date and time.
895 *
896 * @since 1.0.0
897 */
898 public function to_date_time_string()
899 {
900 return $this->format(static::DATETIME_FORMAT);
901 }
902 /**
903 * Format the instance as an ISO-8601 string keeping the timezone offset.
904 *
905 * @return string The formatted date and time.
906 *
907 * @since 1.0.0
908 */
909 public function to_iso8601_string()
910 {
911 return $this->format(static::ISO8601_FORMAT);
912 }
913 /**
914 * Format the instance the way it is serialized to JSON.
915 *
916 * The value is normalized to UTC and carries a trailing "Z".
917 *
918 * @return string The formatted date and time.
919 *
920 * @since 1.0.0
921 */
922 public function to_json()
923 {
924 return $this->copy()->set_timezone('UTC')->format(static::JSON_FORMAT) . 'Z';
925 }
926 /**
927 * Convert the instance to a SQL safe date string.
928 *
929 * @return string The formatted date string.
930 *
931 * @since 1.0.0
932 */
933 public function to_sql_datetime_string()
934 {
935 return $this->format(static::DATETIME_FORMAT);
936 }
937 /**
938 * Get the unix timestamp of the instance.
939 *
940 * @return int The unix timestamp.
941 *
942 * @since 1.0.0
943 */
944 public function get_timestamp()
945 {
946 return $this->getTimestamp();
947 }
948 /**
949 * Get the timezone of the instance.
950 *
951 * @return DateTimeZone The timezone.
952 *
953 * @since 1.0.0
954 */
955 public function get_timezone()
956 {
957 return $this->getTimezone();
958 }
959 /**
960 * Move the instance to the given timezone, keeping the same moment in time.
961 *
962 * @param DateTimeZone|string $timezone The timezone to move to.
963 *
964 * @return $this The mutated instance.
965 *
966 * @since 1.0.0
967 */
968 public function set_timezone($timezone)
969 {
970 $this->setTimezone(static::resolve_timezone($timezone));
971 return $this;
972 }
973 /**
974 * Set the date part of the instance.
975 *
976 * @param int $year The year.
977 * @param int $month The month.
978 * @param int $day The day.
979 *
980 * @return $this The mutated instance.
981 *
982 * @since 1.0.0
983 */
984 public function set_date($year, $month, $day)
985 {
986 $this->setDate((int) $year, (int) $month, (int) $day);
987 return $this;
988 }
989 /**
990 * Set the time part of the instance.
991 *
992 * @param int $hour The hour.
993 * @param int $minute The minute.
994 * @param int $second The second.
995 * @param int $microsecond The microsecond.
996 *
997 * @return $this The mutated instance.
998 *
999 * @since 1.0.0
1000 */
1001 public function set_time($hour, $minute, $second = 0, $microsecond = 0)
1002 {
1003 $this->setTime((int) $hour, (int) $minute, (int) $second, (int) $microsecond);
1004 return $this;
1005 }
1006 /**
1007 * Set the time part of the instance from a time string.
1008 *
1009 * Accepts the shapes a time column holds, such as "10", "10:30",
1010 * "10:30:45" and "10:30:45.123456". Parts that are not given are reset to
1011 * zero, so "10:30" clears the seconds and the microseconds. The date part
1012 * is left untouched.
1013 *
1014 * @param string $time The time to read.
1015 *
1016 * @return $this The mutated instance.
1017 *
1018 * @throws InvalidDateFormatException When the time cannot be read.
1019 *
1020 * @since 1.0.0
1021 */
1022 public function set_time_from_time_string($time)
1023 {
1024 $given = $time;
1025 $time = (string) $time;
1026 if (\strpos($time, ':') === \false) {
1027 $time .= ':0';
1028 }
1029 try {
1030 $modified = @$this->modify($time);
1031 } catch (Exception $exception) {
1032 $modified = \false;
1033 }
1034 if ($modified === \false) {
1035 throw new InvalidDateFormatException(\sprintf('Could not read "%s" as a time.', \is_scalar($given) ? $given : \gettype($given)));
1036 }
1037 return $this;
1038 }
1039 /**
1040 * Get the value used when the instance is encoded to JSON.
1041 *
1042 * @return string The JSON representation.
1043 *
1044 * @since 1.0.0
1045 */
1046 public function jsonSerialize() : string
1047 {
1048 return $this->to_json();
1049 }
1050 /**
1051 * Get the value used when the instance is cast to a string.
1052 *
1053 * @return string The string representation.
1054 *
1055 * @since 1.0.0
1056 */
1057 public function __toString()
1058 {
1059 return $this->to_date_time_string();
1060 }
1061 /**
1062 * Read a single unit of the instance as a property.
1063 *
1064 * @param string $name The unit name.
1065 *
1066 * @return int The unit value.
1067 *
1068 * @throws InvalidArgumentException When the unit is unknown.
1069 *
1070 * @since 1.0.0
1071 */
1072 public function __get($name)
1073 {
1074 if (!isset(static::$readable_units[$name])) {
1075 throw new InvalidArgumentException(\sprintf('Undefined property %s::$%s.', static::class, $name));
1076 }
1077 return (int) $this->format(static::$readable_units[$name]);
1078 }
1079 /**
1080 * Determine whether a unit can be read as a property.
1081 *
1082 * @param string $name The unit name.
1083 *
1084 * @return bool Whether the unit is readable.
1085 *
1086 * @since 1.0.0
1087 */
1088 public function __isset($name)
1089 {
1090 return isset(static::$readable_units[$name]);
1091 }
1092 /**
1093 * Reject any undefined instance method.
1094 *
1095 * The API of this class is snake_case only, so calls are never translated
1096 * from another naming style.
1097 *
1098 * @param string $method The method name.
1099 * @param array $parameters The method parameters.
1100 *
1101 * @return void No return value.
1102 *
1103 * @throws BadMethodCallException Always.
1104 *
1105 * @since 1.0.0
1106 */
1107 public function __call($method, $parameters)
1108 {
1109 throw new BadMethodCallException(\sprintf('Call to undefined method %s::%s(). Date methods are snake_case.', static::class, $method));
1110 }
1111 /**
1112 * Reject any undefined static method.
1113 *
1114 * @param string $method The method name.
1115 * @param array $parameters The method parameters.
1116 *
1117 * @return void No return value.
1118 *
1119 * @throws BadMethodCallException Always.
1120 *
1121 * @since 1.0.0
1122 */
1123 public static function __callStatic($method, $parameters)
1124 {
1125 throw new BadMethodCallException(\sprintf('Call to undefined method %s::%s(). Date methods are snake_case.', static::class, $method));
1126 }
1127 /**
1128 * Shift the instance by the given amount of a single unit.
1129 *
1130 * @param string $unit The unit to shift by.
1131 * @param int $value The signed amount to shift.
1132 *
1133 * @return $this The mutated instance.
1134 *
1135 * @since 1.0.0
1136 */
1137 protected function shift($unit, $value)
1138 {
1139 $this->modify(\sprintf('%+d %s', (int) $value, $unit));
1140 return $this;
1141 }
1142 /**
1143 * Normalize a value into a date instance to compare against.
1144 *
1145 * @param DateTimeInterface|string|int|float|null $date The value to normalize.
1146 *
1147 * @return static The normalized date.
1148 *
1149 * @throws InvalidDateFormatException When the value cannot be parsed.
1150 *
1151 * @since 1.0.0
1152 */
1153 protected function resolve($date)
1154 {
1155 return $date instanceof static ? $date : static::parse($date);
1156 }
1157 /**
1158 * Normalize a timezone value into a timezone object.
1159 *
1160 * @param DateTimeZone|string|null $timezone The timezone to normalize.
1161 *
1162 * @return DateTimeZone|null The timezone, or null when none was given.
1163 *
1164 * @throws InvalidDateFormatException When the timezone is unknown.
1165 *
1166 * @since 1.0.0
1167 */
1168 protected static function resolve_timezone($timezone)
1169 {
1170 if ($timezone === null || $timezone instanceof DateTimeZone) {
1171 return $timezone;
1172 }
1173 try {
1174 return new DateTimeZone($timezone);
1175 } catch (Exception $exception) {
1176 throw new InvalidDateFormatException(\sprintf('Unknown timezone "%s".', \is_scalar($timezone) ? $timezone : \gettype($timezone)), 0, $exception);
1177 }
1178 }
1179 /**
1180 * Build an instance from a unix timestamp, in UTC.
1181 *
1182 * The timestamp is always rendered with six decimal places because the
1183 * native parser rejects a shorter microsecond fragment.
1184 *
1185 * @param int|float|string $timestamp The unix timestamp.
1186 *
1187 * @return static The new instance, expressed as a UTC offset.
1188 *
1189 * @throws InvalidDateFormatException When the timestamp is not numeric.
1190 *
1191 * @since 1.0.0
1192 */
1193 protected static function from_timestamp($timestamp)
1194 {
1195 if (!\is_numeric($timestamp)) {
1196 throw new InvalidDateFormatException(\sprintf('Could not create a date from a non numeric timestamp of type %s.', \gettype($timestamp)));
1197 }
1198 try {
1199 return new static('@' . \sprintf('%.6F', (float) $timestamp));
1200 } catch (Exception $exception) {
1201 throw new InvalidDateFormatException(\sprintf('Could not create a date from the timestamp "%s".', $timestamp), 0, $exception);
1202 }
1203 }
1204 }
1205