PluginProbe
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses / 2.7.7
FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses v2.7.7
2.10.0 2.10.01 2.9.1 2.9.0 2.8.1 2.8.0 2.7.7 2.7.5 2.7.0 2.6.01 2.6.0 2.5.0 2.4.01 trunk 1.0.90 1.0.91 1.0.92 1.0.93 1.0.94 1.0.95 1.0.96 1.0.97 1.0.98 1.0.99 1.1.0 All 77 releases
fluent-community / vendor / wpfluent / framework / src / WPFluent / Support / DateTime.php

DateTime.php in FluentCommunity – Ultra-Fast High-Performance Social Network, Community, LMS & Online Courses 2.7.7, at vendor/wpfluent/framework/src/WPFluent/Support/DateTime.php

1,465 lines 37.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCommunity\Framework\Support;
4
5 use DateTimeZone;
6 use DateInterval;
7 use DateTimeInterface;
8 use DateTime as PHPDateTime;
9 use InvalidArgumentException;
10
11 class DateTime extends PHPDateTime
12 {
13 /**
14 * $singularUnits for checking during dynamic calls
15 * @var array
16 */
17 protected static $singularUnits = [
18 'year', 'month', 'week', 'day', 'hour', 'minute', 'second',
19 ];
20
21 /**
22 * $pluralUnits for checking during dynamic calls
23 * @var array
24 */
25 protected static $pluralUnits = [
26 'years','months', 'weeks', 'days', 'hours', 'minutes', 'seconds'
27 ];
28
29 /**
30 * Construct the DateTime Object
31 *
32 * @param string $datetime
33 * @param \DateTimeZone $timezone|null
34 */
35 public function __construct($datetime = 'now', $timezone = null)
36 {
37 if (is_string($timezone)) {
38 $timezone = new DateTimeZone($timezone);
39 }
40
41 $timezone ??= static::getDefaultTimezone();
42
43 if ($datetime instanceof DateTimeInterface) {
44 $datetime = $datetime->format('Y-m-d H:i:s.u');
45 } elseif (
46 is_numeric($datetime)
47 || str_starts_with((string) $datetime, '@')
48 ) {
49 $datetime = '@' . ltrim((string) $datetime, '@');
50 }
51
52 parent::__construct($datetime, $timezone);
53 }
54
55 /**
56 * Create a new DateTime Object with current time
57 *
58 * @param string|null $tz
59 * @return static
60 */
61 public static function now($tz = null)
62 {
63 return static::create('now', $tz);
64 }
65
66 /**
67 * Create a new DateTime Object with today's time
68 *
69 * @param string|null $tz
70 * @return static
71 */
72 public static function today($tz = null)
73 {
74 return static::create('today', $tz)->startOfDay();
75 }
76
77 /**
78 * Create a new DateTime Object with yesterday's time
79 *
80 * @param string|null $tz
81 * @return static
82 */
83 public static function yesterday($tz = null)
84 {
85 return static::create('now', $tz)->modify('-1 day')->startOfDay();
86 }
87
88 /**
89 * Create a new DateTime Object with tomorrow's time
90 *
91 * @param string|null $tz
92 * @return static
93 */
94 public static function tomorrow($tz = null)
95 {
96 return static::create('now', $tz)->modify('+1 day')->startOfDay();
97 }
98
99 /**
100 * Create a new DateTime Object with the current week's starting time.
101 *
102 * @param string|null $tz
103 * @return static
104 */
105 public static function currentWeek($tz = null)
106 {
107 return static::create('now', $tz)->startOfWeek();
108 }
109
110 /**
111 * Create a new DateTime Object with last week's starting time
112 *
113 * @param string|null $tz
114 * @return static
115 */
116 public static function lastWeek($tz = null)
117 {
118 return static::create('now', $tz)->subWeek()->startOfWeek();
119 }
120
121 /**
122 * Create a new DateTime Object with next week's starting time
123 *
124 * @param string|null $tz
125 * @return static
126 */
127 public static function nextWeek($tz = null)
128 {
129 return static::create('now', $tz)->addWeek()->startOfWeek();
130 }
131
132 /**
133 * Create a new DateTime Object with current month's starting time
134 *
135 * @param string|null $tz
136 * @return static
137 */
138 public static function currentMonth($tz = null)
139 {
140 return static::create('now', $tz)->startOfMonth();
141 }
142
143 /**
144 * Create a new DateTime Object with last month's starting time
145 *
146 * @param string|null $tz
147 * @return static
148 */
149 public static function lastMonth($tz = null)
150 {
151 return static::create('now', $tz)->subMonth()->startOfMonth();
152 }
153
154 /**
155 * Create a new DateTime Object with next month's starting time
156 *
157 * @param string|null $tz
158 * @return static
159 */
160 public static function nextMonth($tz = null)
161 {
162 return static::create('now', $tz)->addMonth()->startOfMonth();
163 }
164
165 /**
166 * Create a new DateTime Object with current year's starting time
167 *
168 * @param string|null $tz
169 * @return static
170 */
171 public static function currentYear($tz = null)
172 {
173 return static::create('now', $tz)->startOfYear();
174 }
175
176 /**
177 * Create a new DateTime Object with last year's starting time
178 *
179 * @param string|null $tz
180 * @return static
181 */
182 public static function lastYear($tz = null)
183 {
184 return static::create('now', $tz)->subYear()->startOfYear();
185 }
186
187 /**
188 * Create a new DateTime Object with next year's starting time
189 *
190 * @param string|null $tz
191 * @return static
192 */
193 public static function nextYear($tz = null)
194 {
195 return static::create('now', $tz)->addYear()->startOfYear();
196 }
197
198 /**
199 * Get the default timezone
200 *
201 * @return \DateTimeZone
202 */
203 public function getDefaultTimezone()
204 {
205 return wp_timezone();
206 }
207
208 /**
209 * Set the timezone
210 *
211 * @return $this
212 */
213 public function timezone($tz)
214 {
215 if (is_string($tz)) {
216 $tz = new DateTimeZone($tz);
217 }
218
219 return $this->setTimezone($tz);
220 }
221
222 /**
223 * Get the default date format
224 *
225 * @return string
226 */
227 public function getDateFormat()
228 {
229 return 'Y-m-d H:i:s';
230 }
231
232 /**
233 * Check if the current instance is between two dates
234 *
235 * @param string|DateTimeInterface $date1
236 * @param string|DateTimeInterface $date2,
237 * @return bool
238 *
239 * @phpstan-ignore-next-line
240 */
241 public function between($date1, $date2): bool
242 {
243 if (!$date1 instanceof DateTimeInterface) {
244 $date1 = new DateTime($date1);
245 }
246
247 if (!$date2 instanceof DateTimeInterface) {
248 $date2 = new DateTime($date2);
249 }
250
251 $current = $this->getTimestamp();
252 $start = min($date1->getTimestamp(), $date2->getTimestamp());
253 $end = max($date1->getTimestamp(), $date2->getTimestamp());
254
255 return $current >= $start && $current <= $end;
256 }
257
258 /**
259 * Create a DateTime object from a string, UNIX timestamp,
260 * or other DateTimeInterface object.
261 *
262 * @param string|int|\DateTimeInterface $time
263 * @return static
264 * @throws \Exception
265 */
266 public static function create($time = null, $tz = null)
267 {
268 if (func_num_args() > 2) {
269 return static::createFromDate(...func_get_args());
270 }
271
272 $time = $time ?: static::now();
273
274 if (is_null($tz)) {
275 $timezone = (new static)->getDefaultTimezone();
276 } else {
277 $timezone = is_string($tz) ? new DateTimeZone($tz) : $tz;
278 }
279
280 if (!$timezone instanceof DateTimeZone) {
281 throw new InvalidArgumentException('Invalid timezone.');
282 }
283
284 if ($time instanceof DateTimeInterface) {
285
286 $dateTime = new static(
287 $time->format((new static)->getDateFormat()), $time->getTimezone()
288 );
289
290 // Override the timezone if the timezone is explictly provided
291 // otherwise don't set the default timezone from $timezone.
292 !is_null($tz) && $dateTime->setTimezone($timezone);
293
294 } elseif (is_numeric($time)) {
295 if ($time <= YEAR_IN_SECONDS) {
296 $time += time();
297 }
298
299 $dateTime = new static('@' . $time);
300
301 $dateTime->setTimezone($timezone);
302
303 } else {
304 $dateTime = new static((string) $time);
305
306 // Set the timezone if timezone is explicitly provided
307 // otherwise set the default timezone if there was no
308 // timezne information available with the string.
309 if ($tz || !$dateTime->hasTimezone($time)) {
310 $dateTime->setTimezone($timezone);
311 }
312 }
313
314 return $dateTime;
315 }
316
317 /**
318 * Check if the given datetime string has the timezone
319 * information attached: Z or +/-00:00 or Asia\Dhaka.
320 *
321 * @param string $datetimeString
322 * @return boolean
323 */
324 public function hasTimezone($datetimeString)
325 {
326 // Regular expression to match timezone
327 // identifier, UTC, or timezone offset
328 $pattern = '/(?:[A-Z][a-zA-Z_]+\/[a-zA-Z_]+|Z|[-+]\d{2}:\d{2})/';
329
330 return preg_match($pattern, $datetimeString) === 1;
331 }
332
333 /**
334 * {@inheritdoc}
335 */
336 #[\ReturnTypeWillChange]
337 public static function createFromFormat($format, $datetimeString, $timezone = null)
338 {
339 if (is_null($timezone)) {
340 $timezone = (new static)->getDefaultTimezone();
341 } else {
342 $timezone = is_string($timezone) ? new DateTimeZone($timezone) : $timezone;
343 }
344
345 if (!$timezone instanceof DateTimeZone) {
346 throw new InvalidArgumentException('Invalid timezone.');
347 }
348
349 $dateTime = PHPDateTime::createFromFormat($format, $datetimeString);
350
351 if ($dateTime !== false) {
352
353 if (!$dateTime instanceof static) {
354 return new static(
355 $dateTime->format(ltrim($format, '!')), $timezone
356 );
357 }
358
359 $dateTime->setTimezone($timezone);
360
361 return $dateTime;
362 }
363
364 throw new InvalidArgumentException(
365 "Unable to create datetime from: {$datetimeString}."
366 );
367 }
368
369 /**
370 * Create DateTime object.
371 *
372 * @return static
373 * @throws InvalidArgumentException
374 */
375 public static function createFromDate(
376 $year, $month, $day, $hour = 0, $minute = 0, $second = 0.0, $tz = null
377 ) {
378
379 $s = sprintf(
380 '%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second
381 );
382
383 if (
384 !checkdate($month, $day, $year)
385 || $hour < 0
386 || $hour > 23
387 || $minute < 0
388 || $minute > 59
389 || $second < 0
390 || $second >= 60
391 ) {
392 throw new InvalidArgumentException("Invalid date '$s'");
393 }
394
395 return new static($s, (is_string($tz) ? new DateTimeZone($tz) : $tz));
396 }
397
398 /**
399 * Given a date in UTC or GMT timezone, returns
400 * that date in the timezone of the site.
401 *
402 * Requires a date in the Y-m-d H:i:s format.
403 *
404 * Default return format of 'Y-m-d H:i:s' can be
405 * overridden using the `$format` parameter.
406 *
407 * @param string $dateString The date to be converted, in UTC or GMT timezone.
408 * @param string $format The format string for the returned date. Default 'Y-m-d H:i:s'.
409 * @see https://developer.wordpress.org/reference/functions/get_date_from_gmt/
410 *
411 * @return string Formatted version of the date, in the site's timezone.
412 */
413 public static function createFromUTC($dateString, $format = 'Y-m-d H:i:s')
414 {
415 $localString = get_date_from_gmt($dateString, $format);
416
417 $date = new static($localString);
418
419 $date->timezone($date->getDefaultTimezone());
420
421 return $date;
422 }
423
424 /**
425 * Parse a datetime string
426 * @param string $datetimeString
427 * @param string $timezone
428 * @return static
429 * @throws InvalidArgumentException
430 */
431 public static function parse($datetimeString, $timezone = null)
432 {
433 try {
434 return new static($datetimeString, $timezone);
435 } catch (Exception $e) {
436 throw new InvalidArgumentException(
437 'Unable to handle datetime.', 0, $e
438 );
439 }
440 }
441
442 /**
443 * Add inetrvals, for example:
444 *
445 * add(1, day)
446 * add('2 day 8 hours 22 minutes')
447 *
448 * @param \DateInterval|string $interval
449 * @return $this
450 */
451 #[\ReturnTypeWillChange]
452 public function add($interval)
453 {
454 if ($interval instanceof DateInterval) {
455 return parent::add($interval);
456 } elseif (func_num_args() === 1 && is_string($interval)) {
457 return $this->modify('+'.$interval);
458 }
459
460 return $this->addOrSub('add', func_get_args());
461 }
462
463 /**
464 * Substruct inetrvals, for example:
465 *
466 * sub(1, day)
467 * sub('2 day 8 hours 22 minutes')
468 *
469 * @param \DateInterval $interval (optional)
470 * @return $this
471 */
472 #[\ReturnTypeWillChange]
473 public function sub($interval)
474 {
475 if ($interval instanceof DateInterval) {
476 return parent::sub($interval);
477 } elseif (func_num_args() === 1 && is_string($interval)) {
478 return $this->modify('-'.$interval);
479 }
480
481 return $this->addOrSub('sub', func_get_args());
482 }
483
484 /**
485 * Add or sub intervals
486 * @param string $action add/sub
487 * @param array $args
488 */
489 protected function addOrSub($action, $args)
490 {
491 $value = reset($args);
492
493 $action = $action.end($args);
494
495 return $this->{$action}($value);
496 }
497
498 /**
499 * Adds the given number of seconds to the current date and time.
500 *
501 * @param int $seconds The number of seconds to add.
502 * @return $this The current instance for method chaining.
503 */
504 public function addSeconds(int $seconds)
505 {
506 return $this->add("{$seconds} seconds");
507 }
508
509 /**
510 * Adds exactly one second to the current date and time.
511 *
512 * @return $this
513 */
514 public function addSecond()
515 {
516 return $this->add("1 second");
517 }
518
519 /**
520 * Adds the given number of minutes to the current date and time.
521 *
522 * @param int $minutes The number of minutes to add.
523 * @return $this The current instance for method chaining.
524 */
525 public function addMinutes(int $minutes)
526 {
527 return $this->add("{$minutes} minutes");
528 }
529
530 /**
531 * Adds exactly one minute to the current date and time.
532 *
533 * @return $this
534 */
535 public function addMinute()
536 {
537 return $this->add("1 minute");
538 }
539
540 /**
541 * Adds the given number of hours to the current date and time.
542 *
543 * @param int $hours The number of hours to add.
544 * @return $this The current instance for method chaining.
545 */
546 public function addHours(int $hours)
547 {
548 return $this->add("{$hours} hours");
549 }
550
551 /**
552 * Adds exactly one hour to the current date and time.
553 *
554 * @return $this
555 */
556 public function addHour()
557 {
558 return $this->add("1 hour");
559 }
560
561 /**
562 * Adds the given number of days to the current date and time.
563 *
564 * @param int $days The number of days to add.
565 * @return $this The current instance for method chaining.
566 */
567 public function addDays(int $days)
568 {
569 return $this->add("{$days} days");
570 }
571
572
573 /**
574 * Adds exactly one day to the current date and time.
575 *
576 * @return $this
577 */
578 public function addDay()
579 {
580 return $this->add("1 day");
581 }
582
583 /**
584 * Adds the given number of weeks to the current date and time.
585 *
586 * @param int $weeks The number of weeks to add.
587 * @return $this The current instance for method chaining.
588 */
589 public function addWeeks(int $weeks)
590 {
591 return $this->add("{$weeks} weeks");
592 }
593
594 /**
595 * Adds exactly one week to the current date and time.
596 *
597 * @return $this
598 */
599 public function addWeek()
600 {
601 return $this->add("1 week");
602 }
603
604 /**
605 * Adds the given number of months to the current date and time.
606 *
607 * @param int $months The number of months to add.
608 * @return $this The current instance for method chaining.
609 */
610 public function addMonths(int $months)
611 {
612 return $this->add("{$months} months");
613 }
614
615 /**
616 * Adds exactly one month to the current date and time.
617 *
618 * @return $this
619 */
620 public function addMonth()
621 {
622 return $this->add("1 month");
623 }
624
625 /**
626 * Adds the given number of years to the current date and time.
627 *
628 * @param int $years The number of years to add.
629 * @return $this The current instance for method chaining.
630 */
631 public function addYears(int $years)
632 {
633 return $this->add("{$years} years");
634 }
635
636 /**
637 * Adds exactly one year to the current date and time.
638 *
639 * @return $this
640 */
641 public function addYear()
642 {
643 return $this->add("1 year");
644 }
645
646 /**
647 * Add a quarter (3 months) to the current date.
648 *
649 * @return $this
650 */
651 public function addQuarter()
652 {
653 return $this->add(new DateInterval('P3M'));
654 }
655
656 /**
657 * Add a decade (10 years) to the current date.
658 *
659 * @return $this
660 */
661 public function addDecade()
662 {
663 return $this->add(new DateInterval('P10Y'));
664 }
665
666 /**
667 * Subtracts the given number of seconds from the current date and time.
668 *
669 * @param int $seconds The number of seconds to subtract.
670 * @return $this The current instance for method chaining.
671 */
672 public function subSeconds(int $seconds)
673 {
674 return $this->sub("{$seconds} seconds");
675 }
676
677 /**
678 * Subtracts one second from the current date and time.
679 *
680 * @return $this The current instance for method chaining.
681 */
682 public function subSecond()
683 {
684 return $this->sub("1 second");
685 }
686
687 /**
688 * Subtracts the given number of minutes from the current date and time.
689 *
690 * @param int $minutes The number of minutes to subtract.
691 * @return $this The current instance for method chaining.
692 */
693 public function subMinutes(int $minutes)
694 {
695 return $this->sub("{$minutes} minutes");
696 }
697
698 /**
699 * Subtracts one minute from the current date and time.
700 *
701 * @return $this The current instance for method chaining.
702 */
703 public function subMinute()
704 {
705 return $this->sub("1 minute");
706 }
707
708 /**
709 * Subtracts the given number of hours from the current date and time.
710 *
711 * @param int $hours The number of hours to subtract.
712 * @return $this The current instance for method chaining.
713 */
714 public function subHours(int $hours)
715 {
716 return $this->sub("{$hours} hours");
717 }
718
719 /**
720 * Subtracts one hour from the current date and time.
721 *
722 * @return $this The current instance for method chaining.
723 */
724 public function subHour()
725 {
726 return $this->sub("1 hour");
727 }
728
729 /**
730 * Subtracts the given number of days from the current date and time.
731 *
732 * @param int $days The number of days to subtract.
733 * @return $this The current instance for method chaining.
734 */
735 public function subDays(int $days)
736 {
737 return $this->sub("{$days} days");
738 }
739
740 /**
741 * Subtracts one day from the current date and time.
742 *
743 * @return $this The current instance for method chaining.
744 */
745 public function subDay()
746 {
747 return $this->sub("1 day");
748 }
749
750 /**
751 * Subtracts the given number of weeks from the current date and time.
752 *
753 * @param int $weeks The number of weeks to subtract.
754 * @return $this The current instance for method chaining.
755 */
756 public function subWeeks(int $weeks)
757 {
758 return $this->sub("{$weeks} weeks");
759 }
760
761 /**
762 * Subtracts one week from the current date and time.
763 *
764 * @return $this The current instance for method chaining.
765 */
766 public function subWeek()
767 {
768 return $this->sub("1 week");
769 }
770
771 /**
772 * Subtracts the given number of months from the current date and time.
773 *
774 * @param int $months The number of months to subtract.
775 * @return $this The current instance for method chaining.
776 */
777 public function subMonths(int $months)
778 {
779 return $this->sub("{$months} months");
780 }
781
782 /**
783 * Subtracts one month from the current date and time.
784 *
785 * @return $this The current instance for method chaining.
786 */
787 public function subMonth()
788 {
789 return $this->sub("1 month");
790 }
791
792 /**
793 * Subtracts the given number of years from the current date and time.
794 *
795 * @param int $years The number of years to subtract.
796 * @return $this The current instance for method chaining.
797 */
798 public function subYears(int $years)
799 {
800 return $this->sub("{$years} years");
801 }
802
803 /**
804 * Subtracts one year from the current date and time.
805 *
806 * @return $this The current instance for method chaining.
807 */
808 public function subYear()
809 {
810 return $this->sub("1 year");
811 }
812
813 /**
814 * Subtract a quarter (3 months) from the current date.
815 *
816 * @return $this
817 */
818 public function subQuarter()
819 {
820 return $this->sub(new DateInterval('P3M'));
821 }
822
823 /**
824 * Subtract a decade (10 years) from the current date.
825 *
826 * @return $this
827 */
828 public function subDecade()
829 {
830 return $this->sub(new DateInterval('P10Y'));
831 }
832
833 /**
834 * Set the date to start of the decade.
835 * @return $this
836 */
837 public function startOfDecade()
838 {
839 $year = (int) $this->format('Y');
840 // Find the start of the decade by subtracting the remainder
841 // of the division by 10 from the current year.
842 $startOfDecadeYear = $year - ($year % 10);
843
844 // Set the date to the start of the decade (January 1st)
845 return $this->setDate($startOfDecadeYear, 1, 1)->setTime(0, 0);
846 }
847
848 /**
849 * Set the date to end of the decade.
850 * @return $this
851 */
852 public function endOfDecade()
853 {
854 $year = (int) $this->format('Y');
855 // Find the last year of the decade by adding 9 to the current
856 // year and subtracting the remainder of the division by 10.
857 $endOfDecadeYear = $year + (9 - ($year % 10));
858
859 // Set the date to December 31st of that year at 23:59:59
860 return $this->setDate($endOfDecadeYear, 12, 31)->setTime(23, 59, 59);
861 }
862
863 /**
864 * Sets start of the year in the current dateTime
865 *
866 * @return $this
867 */
868 public function startOfYear()
869 {
870 return $this->modify('first day of January')->startOfDay();
871 }
872
873 /**
874 * Sets end of the year in the current dateTime
875 *
876 * @return $this
877 */
878 public function endOfYear()
879 {
880 return $this->modify('last day of December')->endOfDay();
881 }
882
883 /**
884 * Sets the date to the first day of the current quarter at 00:00:00.
885 *
886 * @return $this
887 */
888 public function startOfQuarter()
889 {
890 $month = (int) $this->format('m');
891 // Determine the start month of the current quarter
892 if ($month <= 3) {
893 $startMonth = 1; // Q1 starts in January
894 } elseif ($month <= 6) {
895 $startMonth = 4; // Q2 starts in April
896 } elseif ($month <= 9) {
897 $startMonth = 7; // Q3 starts in July
898 } else {
899 $startMonth = 10; // Q4 starts in October
900 }
901
902 // Set the date to the first day of the quarter at 00:00:00
903 return $this->setDate((int) $this->format('Y'), $startMonth, 1)->setTime(0, 0, 0);
904 }
905
906 /**
907 * Sets the date to the last day of the current quarter at 23:59:59.
908 *
909 * @return $this
910 */
911 public function endOfQuarter()
912 {
913 $month = (int) $this->format('m');
914 // Determine the end month of the current quarter
915 if ($month <= 3) {
916 $endMonth = 3; // Q1 ends in March
917 } elseif ($month <= 6) {
918 $endMonth = 6; // Q2 ends in June
919 } elseif ($month <= 9) {
920 $endMonth = 9; // Q3 ends in September
921 } else {
922 $endMonth = 12; // Q4 ends in December
923 }
924
925 // Set the date to the last day of the quarter at 23:59:59
926 return $this->setDate((int) $this->format('Y'), $endMonth, cal_days_in_month(CAL_GREGORIAN, $endMonth, (int) $this->format('Y')))
927 ->setTime(23, 59, 59);
928 }
929
930 /**
931 * Sets start of the month in the current dateTime
932 *
933 * @return $this
934 */
935 public function startOfMonth()
936 {
937 return $this->modify('first day of this month')->startOfDay();
938 }
939
940 /**
941 * Sets end of the month in the current dateTime
942 *
943 * @return $this
944 */
945 public function endOfMonth()
946 {
947 return $this->modify('last day of this month')->endOfDay();
948 }
949
950 /**
951 * Sets start of the week in the current dateTime
952 *
953 * @return $this
954 */
955 public function startOfWeek()
956 {
957 $startOfWeek = intval(get_option('start_of_week'));
958
959 $this->modify('this week');
960
961 // If the start of the week is Sunday (0)
962 if ($startOfWeek === 0) {
963 return $this->modify('this Sunday')->startOfDay();
964 } else {
965 // If it's Monday (1), we need to subtract 1 day.
966 return $this->modify(
967 'this Sunday - ' . (7 - $startOfWeek) . ' days'
968 )->startOfDay();
969 }
970 }
971
972 /**
973 * Sets end of the week in the current dateTime
974 *
975 * @return $this
976 */
977 public function endOfWeek()
978 {
979 // 0 = Sunday, 1 = Monday, etc.
980 $startOfWeek = intval(get_option('start_of_week'));
981
982 // If the start of the week is Monday (1), the
983 // end of the week is the upcoming Sunday
984 if ($startOfWeek === 1) {
985 return $this->modify('next Sunday')->endOfDay();
986 }
987
988 // If the start of the week is Sunday (0), the
989 // end of the week is the upcoming Saturday
990 return $this->modify('next Saturday')->endOfDay();
991 }
992
993 /**
994 * Sets start of the day in the current dateTime
995 *
996 * @return $this
997 */
998 public function startOfDay()
999 {
1000 return $this->setTime(0, 0, 0, 0);
1001 }
1002
1003 /**
1004 * Sets end of the day in the current dateTime
1005 *
1006 * @return $this
1007 */
1008 public function endOfDay()
1009 {
1010 return $this->setTime(23, 59, 59);
1011 }
1012
1013 /**
1014 * Sets start of the hour in the current DateTime object
1015 *
1016 * @return $this
1017 */
1018 public function startOfHour()
1019 {
1020 return $this->setTime($this->format('H'), 0, 0, 0);
1021 }
1022
1023 /**
1024 * Sets end of the hour in the current DateTime object
1025 *
1026 * @return $this
1027 */
1028 public function endOfHour()
1029 {
1030 return $this->setTime($this->format('H'), 59, 59, 999999);
1031 }
1032
1033 /**
1034 * Sets start of the minute in the current DateTime object
1035 *
1036 * @return $this
1037 */
1038 public function startOfMinute()
1039 {
1040 $hour = $this->format('H');
1041 $minute = $this->format('i');
1042 return $this->setTime($hour, $minute, 0, 0);
1043 }
1044
1045 /**
1046 * Sets end of the minute in the current DateTime object
1047 *
1048 * @return $this
1049 */
1050 public function endOfMinute()
1051 {
1052 $hour = $this->format('H');
1053 $minute = $this->format('i');
1054 return $this->setTime($hour, $minute, 59, 999999);
1055 }
1056
1057 /**
1058 * Check if the current instance is a weekend.
1059 *
1060 * @return bool
1061 *
1062 * @phpstan-ignore-next-line
1063 */
1064 public function isWeekend($startOfWeek = null): bool
1065 {
1066 if ($startOfWeek === null) {
1067 $startOfWeek = $startOfWeek = intval(get_option('start_of_week'));
1068 }
1069
1070 // Get the numeric representation of the current day of the week (0 - 6)
1071 $dayOfWeek = (int) $this->format('w');
1072
1073 // Adjust the day of the week based on the start of the week
1074 switch ($startOfWeek) {
1075 case 'monday':
1076 // If the week starts on Monday, adjust Sunday to 6
1077 return ($dayOfWeek === 0 || $dayOfWeek === 6);
1078 case 'saturday':
1079 // If the week starts on Saturday, adjust Friday to 6
1080 return ($dayOfWeek === 5 || $dayOfWeek === 6);
1081 case 'sunday':
1082 default:
1083 // Default behavior, week starts on Sunday
1084 return ($dayOfWeek === 0 || $dayOfWeek === 6);
1085 }
1086 }
1087
1088 /**
1089 * Check if the current instance is a weekday.
1090 *
1091 * @return bool
1092 */
1093 public function isWeekday()
1094 {
1095 return !$this->isWeekend();
1096 }
1097
1098 /**
1099 * Check if the current instance is in the past.
1100 *
1101 * @return bool
1102 */
1103 public function isPast()
1104 {
1105 // Compare with current date and time
1106 return $this < new static();
1107 }
1108
1109 /**
1110 * Check if the current instance is in the future.
1111 *
1112 * @return bool
1113 */
1114 public function isFuture()
1115 {
1116 return $this > new static();
1117 }
1118
1119 /**
1120 * Check if the year is a leap year.
1121 *
1122 * @return boolean
1123 *
1124 * @phpstan-ignore-next-line
1125 */
1126 public function isLeapYear(): bool
1127 {
1128 $year = (int) $this->format('Y');
1129 return ($year % 4 === 0 && $year % 100 !== 0) || ($year % 400 === 0);
1130 }
1131
1132 /**
1133 * Checks if the current time is midnight (00:00:00).
1134 *
1135 * @return bool
1136 */
1137 public function isMidnight()
1138 {
1139 return $this->format('H:i:s') === '00:00:00';
1140 }
1141
1142 /**
1143 * Check if the current instance is the same day as another DateTime instance.
1144 *
1145 * @param DateTime $other
1146 * @return bool
1147 */
1148 public function isSameDay(DateTime $other)
1149 {
1150 return $this->format('Y-m-d') === $other->format('Y-m-d');
1151 }
1152
1153 /**
1154 * Clone the current Object
1155 *
1156 * @return \FluentCommunity\Framework\Support\DateTime
1157 */
1158 public function copy()
1159 {
1160 return clone $this;
1161 }
1162
1163 /**
1164 * Get the difference in years
1165 *
1166 * @param \FluentCommunity\Framework\Support\DateTime $date
1167 * @return int
1168 */
1169 public function diffInYears($date)
1170 {
1171 return $this->diff($date)->y;
1172 }
1173
1174 /**
1175 * Get the difference in months
1176 *
1177 * @param \FluentCommunity\Framework\Support\DateTime $date
1178 * @return int
1179 */
1180 public function diffInMonths($date)
1181 {
1182 $diff = $this->diff($date);
1183
1184 return $diff->y * 12 + $diff->m;
1185 }
1186
1187 /**
1188 * Get the difference in days
1189 *
1190 * @param \FluentCommunity\Framework\Support\DateTime $date
1191 * @return int
1192 */
1193 public function diffInDays($date)
1194 {
1195 $diff = $this->diff($date);
1196
1197 return $diff->days;
1198 }
1199
1200 /**
1201 * Get the difference in hours
1202 *
1203 * @param \FluentCommunity\Framework\Support\DateTime $date
1204 * @return int
1205 */
1206 public function diffInHours($date)
1207 {
1208 $diff = $this->diff($date);
1209
1210 $diffInHours = $diff->h;
1211
1212 return $diffInHours + $diff->days * 24;
1213 }
1214
1215 /**
1216 * Get the difference in minutes
1217 *
1218 * @param \FluentCommunity\Framework\Support\DateTime $date
1219 * @return int
1220 */
1221 public function diffInMinutes($date)
1222 {
1223 $diff = $this->diff($date);
1224
1225 $diffInMinutes = $diff->i;
1226
1227 $diffInMinutes += $diff->h * 60;
1228
1229 return $diffInMinutes + $diff->days * 24 * 60;
1230 }
1231
1232 /**
1233 * Get the difference in seconds
1234 *
1235 * @param \FluentCommunity\Framework\Support\DateTime $date
1236 * @return int
1237 */
1238 public function diffInSeconds($date)
1239 {
1240 $diff = $this->diff($date);
1241
1242 $diffInSeconds = $diff->days * 24 * 60 * 60;
1243
1244 $diffInSeconds += $diff->h * 60 * 60;
1245
1246 $diffInSeconds += $diff->i * 60;
1247
1248 return $diffInSeconds + $diff->s;
1249 }
1250
1251 /**
1252 * Get human friendly time difference (2 hours ago/ 2 hours from now)
1253 *
1254 * @param \DateTimeInterface|string|int $from The datetime to compare from
1255 * @param \DateTimeInterface|string|int $to The datetime to compare to
1256
1257 * @return string Human readable string, ie. 5 days ago/from now
1258 */
1259 public function diffForHumans($from = null, $to = null)
1260 {
1261 // Use the current object's timestamp if $from (and $to) is null
1262 // This is because ORM's datetime field can call it without params.
1263 if (is_null($from)) {
1264 $from = $this->getTimestamp();
1265 } elseif ($from instanceof \DateTimeInterface) {
1266 $from = $from->getTimestamp();
1267 } elseif (!is_numeric($from)) {
1268 $from = (new \DateTime($from))->getTimestamp();
1269 }
1270
1271 // Use the current time as $to if not provided
1272 if (is_null($to)) {
1273 $to = time();
1274 } elseif ($to instanceof \DateTimeInterface) {
1275 $to = $to->getTimestamp();
1276 } elseif (!is_numeric($to)) {
1277 $to = (new \DateTime($to))->getTimestamp();
1278 }
1279
1280 // Calculate the difference in seconds
1281 $diffInSeconds = abs($to - $from);
1282 $dateTimeDiff = human_time_diff($from, $to);
1283
1284 // Determine if the difference is in the past or future
1285 if ($from > $to) {
1286 // The "from" time is earlier than "to" (future)
1287 return sprintf(__('%s from now'), $dateTimeDiff);
1288 } else {
1289 // The "from" time is later than "to" (older)
1290 if ($diffInSeconds > 60) {
1291 return sprintf(__('%s ago'), $dateTimeDiff);
1292 }
1293
1294 // If difference is less than 1 minute, return just now
1295 return __('just now');
1296 }
1297 }
1298
1299 /**
1300 * Given a date in the timezone of the site, returns that date in UTC.
1301 *
1302 * Requires and returns a date in the Y-m-d H:i:s format.
1303 *
1304 * Return format can be overridden using the $format parameter.
1305 *
1306 * @param string $dateString The date to be converted, in the timezone of the site.
1307 * @param string $format The format string for the returned date. Default 'Y-m-d H:i:s'.
1308 * @see https://developer.wordpress.org/reference/functions/get_gmt_from_date/
1309 *
1310 * @return string Formatted version of the date, in UTC.
1311 */
1312 public function toUTC($dateString, $format = 'Y-m-d H:i:s')
1313 {
1314 return get_gmt_from_date($dateString, $format);
1315 }
1316
1317 /**
1318 * Return the ISO-8601 string
1319 *
1320 * @see https://stackoverflow.com/a/11173072/741747
1321 *
1322 * @return mixed
1323 */
1324 public function toJSON()
1325 {
1326 return date('c', $this->getTimestamp());
1327 }
1328
1329 /**
1330 * Returns the formatted string
1331 *
1332 * @return string
1333 */
1334 public function toString()
1335 {
1336 return (string) $this;
1337 }
1338
1339 /**
1340 * Return only the date part as string
1341 *
1342 * @return string
1343 */
1344 public function toDateString()
1345 {
1346 return (string) $this->format('Y-m-d');
1347 }
1348
1349 /**
1350 * Return only the time part as string
1351 *
1352 * @return string
1353 */
1354 public function toTimeString()
1355 {
1356 return (string) $this->format('H:i:s');
1357 }
1358
1359 /**
1360 * Returns the formatted string
1361 *
1362 * @return string
1363 */
1364 public function __toString()
1365 {
1366 return $this->format($this->getDateFormat());
1367 }
1368
1369 /**
1370 * Getter to get an unit of DateTime
1371 * @param string $key
1372 * @return string|null
1373 */
1374 public function __get($key)
1375 {
1376 if ($key == 'year') {
1377 return $this->format('Y');
1378 } elseif ($key == 'month') {
1379 return $this->format('m');
1380 } elseif ($key == 'day') {
1381 return $this->format('d');
1382 } elseif ($key == 'hour') {
1383 return $this->format('H');
1384 } elseif ($key == 'minute') {
1385 return $this->format('i');
1386 } elseif ($key == 'second') {
1387 return $this->format('s');
1388 }
1389 }
1390
1391 /**
1392 * Setter to set an unit of DateTime
1393 * @param string $key
1394 * @param string|int $value
1395 * @return $this
1396 */
1397 public function __set($key, $value)
1398 {
1399 if ($key == 'year') {
1400 return $this->setDate($value, $this->format('m'), $this->format('d'));
1401 } elseif ($key == 'month') {
1402 return $this->setDate($this->format('Y'), $value, $this->format('d'));
1403 } elseif ($key == 'day') {
1404 return $this->setDate($this->format('Y'), $this->format('m'), $value);
1405 } elseif ($key == 'hour') {
1406 return $this->setTime($value, $this->format('i'), $this->format('s'));
1407 } elseif ($key == 'minute') {
1408 return $this->setTime($this->format('H'), $value, $this->format('s'));
1409 } elseif ($key == 'second') {
1410 return $this->setTime($this->format('H'), $this->format('i'), $value);
1411 }
1412 }
1413
1414 /**
1415 * Handle Dynamic calls (add/sub)
1416 *
1417 * @param string $method
1418 * @param array $params
1419 * @return $this
1420 */
1421 public function __call($method, $params)
1422 {
1423 // Dynamic Setter/Getter
1424 if (strpos($method, 'set') === 0) {
1425 $unit = strtolower(substr($method, 3));
1426 if ($params && in_array($unit, static::$singularUnits)) {
1427 $this->{$unit} = reset($params);
1428 return $this;
1429 }
1430 } elseif (strpos($method, 'get') === 0) {
1431 $unit = strtolower(substr($method, 3));
1432 if (in_array($unit, static::$singularUnits)) {
1433 return $this->{$unit};
1434 }
1435 }
1436
1437 // Dynamic adder/subtractor
1438 if (strpos($method, 'add') === 0) {
1439 $action = '+';
1440 } elseif (strpos($method, 'sub') === 0) {
1441 $action = '-';
1442 }
1443
1444 if (isset($action) && in_array($action, ['+', '-'])) {
1445
1446 if (!$params) {
1447 $duration = 1;
1448 } else {
1449 $duration = reset($params);
1450 }
1451
1452
1453 $unit = strtolower(substr($method, 3));
1454
1455 $units = array_merge(static::$singularUnits, static::$pluralUnits);
1456
1457 if (in_array($unit, $units)) {
1458 return $this->modify("{$action}{$duration}{$unit}");
1459 }
1460 }
1461
1462 throw new InvalidArgumentException("Call to undefined method {$method}.");
1463 }
1464 }
1465