PluginProbe
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration / 1.41
FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration v1.41
2.1.0 2.0.15 2.0.12 2.0.10 2.0.4 2.0.1 2.0.0 1.95.3 1.95.2 1.95 1.91.6 trunk 1.11 1.12 1.13 1.20 1.21 1.22 1.23 1.30 1.31 1.32 1.35 1.40 1.41 All 42 releases
fluent-boards / vendor / wpfluent / framework / src / WPFluent / Support / DateTime.php

DateTime.php in FluentBoards – Project Management, Task Management, Goal Tracking, Kanban Board, and, Team Collaboration 1.41, at vendor/wpfluent/framework/src/WPFluent/Support/DateTime.php

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