blob: cce1d2c3f47f658ff88bf745799c9d47e8f45bde [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3/**
4 * This file is part of the Carbon package.
5 *
6 * (c) Brian Nesbitt <brian@nesbot.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010011
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +020012namespace Carbon\Traits;
13
14use Carbon\Carbon;
15use Carbon\CarbonImmutable;
16use Carbon\CarbonInterface;
17use Carbon\CarbonInterval;
18use Carbon\CarbonPeriod;
19use Carbon\Translator;
20use Closure;
21use DateInterval;
22use DateTimeInterface;
23use ReturnTypeWillChange;
24
25/**
26 * Trait Difference.
27 *
28 * Depends on the following methods:
29 *
30 * @method bool lessThan($date)
31 * @method static copy()
32 * @method static resolveCarbon($date = null)
33 * @method static Translator translator()
34 */
35trait Difference
36{
37 /**
38 * @codeCoverageIgnore
39 *
40 * @param CarbonInterval $diff
41 */
42 protected static function fixNegativeMicroseconds(CarbonInterval $diff)
43 {
44 if ($diff->s !== 0 || $diff->i !== 0 || $diff->h !== 0 || $diff->d !== 0 || $diff->m !== 0 || $diff->y !== 0) {
45 $diff->f = (round($diff->f * 1000000) + 1000000) / 1000000;
46 $diff->s--;
47
48 if ($diff->s < 0) {
49 $diff->s += 60;
50 $diff->i--;
51
52 if ($diff->i < 0) {
53 $diff->i += 60;
54 $diff->h--;
55
56 if ($diff->h < 0) {
57 $diff->h += 24;
58 $diff->d--;
59
60 if ($diff->d < 0) {
61 $diff->d += 30;
62 $diff->m--;
63
64 if ($diff->m < 0) {
65 $diff->m += 12;
66 $diff->y--;
67 }
68 }
69 }
70 }
71 }
72
73 return;
74 }
75
76 $diff->f *= -1;
77 $diff->invert();
78 }
79
80 /**
81 * @param DateInterval $diff
82 * @param bool $absolute
83 *
84 * @return CarbonInterval
85 */
86 protected static function fixDiffInterval(DateInterval $diff, $absolute)
87 {
88 $diff = CarbonInterval::instance($diff);
89
90 // Work-around for https://bugs.php.net/bug.php?id=77145
91 // @codeCoverageIgnoreStart
92 if ($diff->f > 0 && $diff->y === -1 && $diff->m === 11 && $diff->d >= 27 && $diff->h === 23 && $diff->i === 59 && $diff->s === 59) {
93 $diff->y = 0;
94 $diff->m = 0;
95 $diff->d = 0;
96 $diff->h = 0;
97 $diff->i = 0;
98 $diff->s = 0;
99 $diff->f = (1000000 - round($diff->f * 1000000)) / 1000000;
100 $diff->invert();
101 } elseif ($diff->f < 0) {
102 static::fixNegativeMicroseconds($diff);
103 }
104 // @codeCoverageIgnoreEnd
105
106 if ($absolute && $diff->invert) {
107 $diff->invert();
108 }
109
110 return $diff;
111 }
112
113 /**
114 * Get the difference as a DateInterval instance.
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100115 * Return relative interval (negative if $absolute flag is not set to true and the given date is before
116 * current one).
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200117 *
118 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
119 * @param bool $absolute Get the absolute of the difference
120 *
121 * @return DateInterval
122 */
123 #[ReturnTypeWillChange]
124 public function diff($date = null, $absolute = false)
125 {
126 $other = $this->resolveCarbon($date);
127
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100128 // Work-around for https://bugs.php.net/bug.php?id=81458
129 // It was initially introduced for https://bugs.php.net/bug.php?id=80998
130 // The very specific case of 80998 was fixed in PHP 8.1beta3, but it introduced 81458
131 // So we still need to keep this for now
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200132 // @codeCoverageIgnoreStart
133 if (version_compare(PHP_VERSION, '8.1.0-dev', '>=') && $other->tz !== $this->tz) {
134 $other = $other->avoidMutation()->tz($this->tz);
135 }
136 // @codeCoverageIgnoreEnd
137
138 return parent::diff($other, (bool) $absolute);
139 }
140
141 /**
142 * Get the difference as a CarbonInterval instance.
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100143 * Return relative interval (negative if $absolute flag is not set to true and the given date is before
144 * current one).
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200145 *
146 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
147 * @param bool $absolute Get the absolute of the difference
148 *
149 * @return CarbonInterval
150 */
151 public function diffAsCarbonInterval($date = null, $absolute = true)
152 {
153 return static::fixDiffInterval($this->diff($this->resolveCarbon($date), $absolute), $absolute);
154 }
155
156 /**
157 * Get the difference in years
158 *
159 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
160 * @param bool $absolute Get the absolute of the difference
161 *
162 * @return int
163 */
164 public function diffInYears($date = null, $absolute = true)
165 {
166 return (int) $this->diff($this->resolveCarbon($date), $absolute)->format('%r%y');
167 }
168
169 /**
170 * Get the difference in quarters rounded down.
171 *
172 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
173 * @param bool $absolute Get the absolute of the difference
174 *
175 * @return int
176 */
177 public function diffInQuarters($date = null, $absolute = true)
178 {
179 return (int) ($this->diffInMonths($date, $absolute) / static::MONTHS_PER_QUARTER);
180 }
181
182 /**
183 * Get the difference in months rounded down.
184 *
185 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
186 * @param bool $absolute Get the absolute of the difference
187 *
188 * @return int
189 */
190 public function diffInMonths($date = null, $absolute = true)
191 {
192 $date = $this->resolveCarbon($date);
193
194 return $this->diffInYears($date, $absolute) * static::MONTHS_PER_YEAR + (int) $this->diff($date, $absolute)->format('%r%m');
195 }
196
197 /**
198 * Get the difference in weeks rounded down.
199 *
200 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
201 * @param bool $absolute Get the absolute of the difference
202 *
203 * @return int
204 */
205 public function diffInWeeks($date = null, $absolute = true)
206 {
207 return (int) ($this->diffInDays($date, $absolute) / static::DAYS_PER_WEEK);
208 }
209
210 /**
211 * Get the difference in days rounded down.
212 *
213 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
214 * @param bool $absolute Get the absolute of the difference
215 *
216 * @return int
217 */
218 public function diffInDays($date = null, $absolute = true)
219 {
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100220 return $this->getIntervalDayDiff($this->diff($this->resolveCarbon($date), $absolute));
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200221 }
222
223 /**
224 * Get the difference in days using a filter closure rounded down.
225 *
226 * @param Closure $callback
227 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
228 * @param bool $absolute Get the absolute of the difference
229 *
230 * @return int
231 */
232 public function diffInDaysFiltered(Closure $callback, $date = null, $absolute = true)
233 {
234 return $this->diffFiltered(CarbonInterval::day(), $callback, $date, $absolute);
235 }
236
237 /**
238 * Get the difference in hours using a filter closure rounded down.
239 *
240 * @param Closure $callback
241 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
242 * @param bool $absolute Get the absolute of the difference
243 *
244 * @return int
245 */
246 public function diffInHoursFiltered(Closure $callback, $date = null, $absolute = true)
247 {
248 return $this->diffFiltered(CarbonInterval::hour(), $callback, $date, $absolute);
249 }
250
251 /**
252 * Get the difference by the given interval using a filter closure.
253 *
254 * @param CarbonInterval $ci An interval to traverse by
255 * @param Closure $callback
256 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
257 * @param bool $absolute Get the absolute of the difference
258 *
259 * @return int
260 */
261 public function diffFiltered(CarbonInterval $ci, Closure $callback, $date = null, $absolute = true)
262 {
263 $start = $this;
264 $end = $this->resolveCarbon($date);
265 $inverse = false;
266
267 if ($end < $start) {
268 $start = $end;
269 $end = $this;
270 $inverse = true;
271 }
272
273 $options = CarbonPeriod::EXCLUDE_END_DATE | ($this->isMutable() ? 0 : CarbonPeriod::IMMUTABLE);
274 $diff = $ci->toPeriod($start, $end, $options)->filter($callback)->count();
275
276 return $inverse && !$absolute ? -$diff : $diff;
277 }
278
279 /**
280 * Get the difference in weekdays rounded down.
281 *
282 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
283 * @param bool $absolute Get the absolute of the difference
284 *
285 * @return int
286 */
287 public function diffInWeekdays($date = null, $absolute = true)
288 {
289 return $this->diffInDaysFiltered(function (CarbonInterface $date) {
290 return $date->isWeekday();
291 }, $date, $absolute);
292 }
293
294 /**
295 * Get the difference in weekend days using a filter rounded down.
296 *
297 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
298 * @param bool $absolute Get the absolute of the difference
299 *
300 * @return int
301 */
302 public function diffInWeekendDays($date = null, $absolute = true)
303 {
304 return $this->diffInDaysFiltered(function (CarbonInterface $date) {
305 return $date->isWeekend();
306 }, $date, $absolute);
307 }
308
309 /**
310 * Get the difference in hours rounded down.
311 *
312 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
313 * @param bool $absolute Get the absolute of the difference
314 *
315 * @return int
316 */
317 public function diffInHours($date = null, $absolute = true)
318 {
319 return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR);
320 }
321
322 /**
323 * Get the difference in hours rounded down using timestamps.
324 *
325 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
326 * @param bool $absolute Get the absolute of the difference
327 *
328 * @return int
329 */
330 public function diffInRealHours($date = null, $absolute = true)
331 {
332 return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE / static::MINUTES_PER_HOUR);
333 }
334
335 /**
336 * Get the difference in minutes rounded down.
337 *
338 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
339 * @param bool $absolute Get the absolute of the difference
340 *
341 * @return int
342 */
343 public function diffInMinutes($date = null, $absolute = true)
344 {
345 return (int) ($this->diffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE);
346 }
347
348 /**
349 * Get the difference in minutes rounded down using timestamps.
350 *
351 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
352 * @param bool $absolute Get the absolute of the difference
353 *
354 * @return int
355 */
356 public function diffInRealMinutes($date = null, $absolute = true)
357 {
358 return (int) ($this->diffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE);
359 }
360
361 /**
362 * Get the difference in seconds rounded down.
363 *
364 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
365 * @param bool $absolute Get the absolute of the difference
366 *
367 * @return int
368 */
369 public function diffInSeconds($date = null, $absolute = true)
370 {
371 $diff = $this->diff($date);
372
373 if ($diff->days === 0) {
374 $diff = static::fixDiffInterval($diff, $absolute);
375 }
376
377 $value = (((($diff->m || $diff->y ? $diff->days : $diff->d) * static::HOURS_PER_DAY) +
378 $diff->h) * static::MINUTES_PER_HOUR +
379 $diff->i) * static::SECONDS_PER_MINUTE +
380 $diff->s;
381
382 return $absolute || !$diff->invert ? $value : -$value;
383 }
384
385 /**
386 * Get the difference in microseconds.
387 *
388 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
389 * @param bool $absolute Get the absolute of the difference
390 *
391 * @return int
392 */
393 public function diffInMicroseconds($date = null, $absolute = true)
394 {
395 $diff = $this->diff($date);
396 $value = (int) round(((((($diff->m || $diff->y ? $diff->days : $diff->d) * static::HOURS_PER_DAY) +
397 $diff->h) * static::MINUTES_PER_HOUR +
398 $diff->i) * static::SECONDS_PER_MINUTE +
399 ($diff->f + $diff->s)) * static::MICROSECONDS_PER_SECOND);
400
401 return $absolute || !$diff->invert ? $value : -$value;
402 }
403
404 /**
405 * Get the difference in milliseconds rounded down.
406 *
407 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
408 * @param bool $absolute Get the absolute of the difference
409 *
410 * @return int
411 */
412 public function diffInMilliseconds($date = null, $absolute = true)
413 {
414 return (int) ($this->diffInMicroseconds($date, $absolute) / static::MICROSECONDS_PER_MILLISECOND);
415 }
416
417 /**
418 * Get the difference in seconds using timestamps.
419 *
420 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
421 * @param bool $absolute Get the absolute of the difference
422 *
423 * @return int
424 */
425 public function diffInRealSeconds($date = null, $absolute = true)
426 {
427 /** @var CarbonInterface $date */
428 $date = $this->resolveCarbon($date);
429 $value = $date->getTimestamp() - $this->getTimestamp();
430
431 return $absolute ? abs($value) : $value;
432 }
433
434 /**
435 * Get the difference in microseconds using timestamps.
436 *
437 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
438 * @param bool $absolute Get the absolute of the difference
439 *
440 * @return int
441 */
442 public function diffInRealMicroseconds($date = null, $absolute = true)
443 {
444 /** @var CarbonInterface $date */
445 $date = $this->resolveCarbon($date);
446 $value = ($date->timestamp - $this->timestamp) * static::MICROSECONDS_PER_SECOND +
447 $date->micro - $this->micro;
448
449 return $absolute ? abs($value) : $value;
450 }
451
452 /**
453 * Get the difference in milliseconds rounded down using timestamps.
454 *
455 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
456 * @param bool $absolute Get the absolute of the difference
457 *
458 * @return int
459 */
460 public function diffInRealMilliseconds($date = null, $absolute = true)
461 {
462 return (int) ($this->diffInRealMicroseconds($date, $absolute) / static::MICROSECONDS_PER_MILLISECOND);
463 }
464
465 /**
466 * Get the difference in seconds as float (microsecond-precision).
467 *
468 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
469 * @param bool $absolute Get the absolute of the difference
470 *
471 * @return float
472 */
473 public function floatDiffInSeconds($date = null, $absolute = true)
474 {
475 return $this->diffInMicroseconds($date, $absolute) / static::MICROSECONDS_PER_SECOND;
476 }
477
478 /**
479 * Get the difference in minutes as float (microsecond-precision).
480 *
481 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
482 * @param bool $absolute Get the absolute of the difference
483 *
484 * @return float
485 */
486 public function floatDiffInMinutes($date = null, $absolute = true)
487 {
488 return $this->floatDiffInSeconds($date, $absolute) / static::SECONDS_PER_MINUTE;
489 }
490
491 /**
492 * Get the difference in hours as float (microsecond-precision).
493 *
494 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
495 * @param bool $absolute Get the absolute of the difference
496 *
497 * @return float
498 */
499 public function floatDiffInHours($date = null, $absolute = true)
500 {
501 return $this->floatDiffInMinutes($date, $absolute) / static::MINUTES_PER_HOUR;
502 }
503
504 /**
505 * Get the difference in days as float (microsecond-precision).
506 *
507 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
508 * @param bool $absolute Get the absolute of the difference
509 *
510 * @return float
511 */
512 public function floatDiffInDays($date = null, $absolute = true)
513 {
514 $hoursDiff = $this->floatDiffInHours($date, $absolute);
515 $interval = $this->diff($date, $absolute);
516
517 if ($interval->y === 0 && $interval->m === 0 && $interval->d === 0) {
518 return $hoursDiff / static::HOURS_PER_DAY;
519 }
520
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100521 $daysDiff = $this->getIntervalDayDiff($interval);
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200522
523 return $daysDiff + fmod($hoursDiff, static::HOURS_PER_DAY) / static::HOURS_PER_DAY;
524 }
525
526 /**
527 * Get the difference in weeks as float (microsecond-precision).
528 *
529 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
530 * @param bool $absolute Get the absolute of the difference
531 *
532 * @return float
533 */
534 public function floatDiffInWeeks($date = null, $absolute = true)
535 {
536 return $this->floatDiffInDays($date, $absolute) / static::DAYS_PER_WEEK;
537 }
538
539 /**
540 * Get the difference in months as float (microsecond-precision).
541 *
542 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
543 * @param bool $absolute Get the absolute of the difference
544 *
545 * @return float
546 */
547 public function floatDiffInMonths($date = null, $absolute = true)
548 {
549 $start = $this;
550 $end = $this->resolveCarbon($date);
551 $ascending = ($start <= $end);
552 $sign = $absolute || $ascending ? 1 : -1;
553 if (!$ascending) {
554 [$start, $end] = [$end, $start];
555 }
556 $monthsDiff = $start->diffInMonths($end);
557 /** @var Carbon|CarbonImmutable $floorEnd */
558 $floorEnd = $start->avoidMutation()->addMonths($monthsDiff);
559
560 if ($floorEnd >= $end) {
561 return $sign * $monthsDiff;
562 }
563
564 /** @var Carbon|CarbonImmutable $startOfMonthAfterFloorEnd */
565 $startOfMonthAfterFloorEnd = $floorEnd->avoidMutation()->addMonth()->startOfMonth();
566
567 if ($startOfMonthAfterFloorEnd > $end) {
568 return $sign * ($monthsDiff + $floorEnd->floatDiffInDays($end) / $floorEnd->daysInMonth);
569 }
570
571 return $sign * ($monthsDiff + $floorEnd->floatDiffInDays($startOfMonthAfterFloorEnd) / $floorEnd->daysInMonth + $startOfMonthAfterFloorEnd->floatDiffInDays($end) / $end->daysInMonth);
572 }
573
574 /**
575 * Get the difference in year as float (microsecond-precision).
576 *
577 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
578 * @param bool $absolute Get the absolute of the difference
579 *
580 * @return float
581 */
582 public function floatDiffInYears($date = null, $absolute = true)
583 {
584 $start = $this;
585 $end = $this->resolveCarbon($date);
586 $ascending = ($start <= $end);
587 $sign = $absolute || $ascending ? 1 : -1;
588 if (!$ascending) {
589 [$start, $end] = [$end, $start];
590 }
591 $yearsDiff = $start->diffInYears($end);
592 /** @var Carbon|CarbonImmutable $floorEnd */
593 $floorEnd = $start->avoidMutation()->addYears($yearsDiff);
594
595 if ($floorEnd >= $end) {
596 return $sign * $yearsDiff;
597 }
598
599 /** @var Carbon|CarbonImmutable $startOfYearAfterFloorEnd */
600 $startOfYearAfterFloorEnd = $floorEnd->avoidMutation()->addYear()->startOfYear();
601
602 if ($startOfYearAfterFloorEnd > $end) {
603 return $sign * ($yearsDiff + $floorEnd->floatDiffInDays($end) / $floorEnd->daysInYear);
604 }
605
606 return $sign * ($yearsDiff + $floorEnd->floatDiffInDays($startOfYearAfterFloorEnd) / $floorEnd->daysInYear + $startOfYearAfterFloorEnd->floatDiffInDays($end) / $end->daysInYear);
607 }
608
609 /**
610 * Get the difference in seconds as float (microsecond-precision) using timestamps.
611 *
612 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
613 * @param bool $absolute Get the absolute of the difference
614 *
615 * @return float
616 */
617 public function floatDiffInRealSeconds($date = null, $absolute = true)
618 {
619 return $this->diffInRealMicroseconds($date, $absolute) / static::MICROSECONDS_PER_SECOND;
620 }
621
622 /**
623 * Get the difference in minutes as float (microsecond-precision) using timestamps.
624 *
625 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
626 * @param bool $absolute Get the absolute of the difference
627 *
628 * @return float
629 */
630 public function floatDiffInRealMinutes($date = null, $absolute = true)
631 {
632 return $this->floatDiffInRealSeconds($date, $absolute) / static::SECONDS_PER_MINUTE;
633 }
634
635 /**
636 * Get the difference in hours as float (microsecond-precision) using timestamps.
637 *
638 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
639 * @param bool $absolute Get the absolute of the difference
640 *
641 * @return float
642 */
643 public function floatDiffInRealHours($date = null, $absolute = true)
644 {
645 return $this->floatDiffInRealMinutes($date, $absolute) / static::MINUTES_PER_HOUR;
646 }
647
648 /**
649 * Get the difference in days as float (microsecond-precision).
650 *
651 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
652 * @param bool $absolute Get the absolute of the difference
653 *
654 * @return float
655 */
656 public function floatDiffInRealDays($date = null, $absolute = true)
657 {
658 $date = $this->resolveUTC($date);
659 $utc = $this->avoidMutation()->utc();
660 $hoursDiff = $utc->floatDiffInRealHours($date, $absolute);
661
662 return ($hoursDiff < 0 ? -1 : 1) * $utc->diffInDays($date) + fmod($hoursDiff, static::HOURS_PER_DAY) / static::HOURS_PER_DAY;
663 }
664
665 /**
666 * Get the difference in weeks as float (microsecond-precision).
667 *
668 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
669 * @param bool $absolute Get the absolute of the difference
670 *
671 * @return float
672 */
673 public function floatDiffInRealWeeks($date = null, $absolute = true)
674 {
675 return $this->floatDiffInRealDays($date, $absolute) / static::DAYS_PER_WEEK;
676 }
677
678 /**
679 * Get the difference in months as float (microsecond-precision) using timestamps.
680 *
681 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
682 * @param bool $absolute Get the absolute of the difference
683 *
684 * @return float
685 */
686 public function floatDiffInRealMonths($date = null, $absolute = true)
687 {
688 $start = $this;
689 $end = $this->resolveCarbon($date);
690 $ascending = ($start <= $end);
691 $sign = $absolute || $ascending ? 1 : -1;
692 if (!$ascending) {
693 [$start, $end] = [$end, $start];
694 }
695 $monthsDiff = $start->diffInMonths($end);
696 /** @var Carbon|CarbonImmutable $floorEnd */
697 $floorEnd = $start->avoidMutation()->addMonths($monthsDiff);
698
699 if ($floorEnd >= $end) {
700 return $sign * $monthsDiff;
701 }
702
703 /** @var Carbon|CarbonImmutable $startOfMonthAfterFloorEnd */
704 $startOfMonthAfterFloorEnd = $floorEnd->avoidMutation()->addMonth()->startOfMonth();
705
706 if ($startOfMonthAfterFloorEnd > $end) {
707 return $sign * ($monthsDiff + $floorEnd->floatDiffInRealDays($end) / $floorEnd->daysInMonth);
708 }
709
710 return $sign * ($monthsDiff + $floorEnd->floatDiffInRealDays($startOfMonthAfterFloorEnd) / $floorEnd->daysInMonth + $startOfMonthAfterFloorEnd->floatDiffInRealDays($end) / $end->daysInMonth);
711 }
712
713 /**
714 * Get the difference in year as float (microsecond-precision) using timestamps.
715 *
716 * @param \Carbon\CarbonInterface|\DateTimeInterface|string|null $date
717 * @param bool $absolute Get the absolute of the difference
718 *
719 * @return float
720 */
721 public function floatDiffInRealYears($date = null, $absolute = true)
722 {
723 $start = $this;
724 $end = $this->resolveCarbon($date);
725 $ascending = ($start <= $end);
726 $sign = $absolute || $ascending ? 1 : -1;
727 if (!$ascending) {
728 [$start, $end] = [$end, $start];
729 }
730 $yearsDiff = $start->diffInYears($end);
731 /** @var Carbon|CarbonImmutable $floorEnd */
732 $floorEnd = $start->avoidMutation()->addYears($yearsDiff);
733
734 if ($floorEnd >= $end) {
735 return $sign * $yearsDiff;
736 }
737
738 /** @var Carbon|CarbonImmutable $startOfYearAfterFloorEnd */
739 $startOfYearAfterFloorEnd = $floorEnd->avoidMutation()->addYear()->startOfYear();
740
741 if ($startOfYearAfterFloorEnd > $end) {
742 return $sign * ($yearsDiff + $floorEnd->floatDiffInRealDays($end) / $floorEnd->daysInYear);
743 }
744
745 return $sign * ($yearsDiff + $floorEnd->floatDiffInRealDays($startOfYearAfterFloorEnd) / $floorEnd->daysInYear + $startOfYearAfterFloorEnd->floatDiffInRealDays($end) / $end->daysInYear);
746 }
747
748 /**
749 * The number of seconds since midnight.
750 *
751 * @return int
752 */
753 public function secondsSinceMidnight()
754 {
755 return $this->diffInSeconds($this->avoidMutation()->startOfDay());
756 }
757
758 /**
759 * The number of seconds until 23:59:59.
760 *
761 * @return int
762 */
763 public function secondsUntilEndOfDay()
764 {
765 return $this->diffInSeconds($this->avoidMutation()->endOfDay());
766 }
767
768 /**
769 * Get the difference in a human readable format in the current locale from current instance to an other
770 * instance given (or now if null given).
771 *
772 * @example
773 * ```
774 * echo Carbon::tomorrow()->diffForHumans() . "\n";
775 * echo Carbon::tomorrow()->diffForHumans(['parts' => 2]) . "\n";
776 * echo Carbon::tomorrow()->diffForHumans(['parts' => 3, 'join' => true]) . "\n";
777 * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday()) . "\n";
778 * echo Carbon::tomorrow()->diffForHumans(Carbon::yesterday(), ['short' => true]) . "\n";
779 * ```
780 *
781 * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
782 * if null passed, now will be used as comparison reference;
783 * if any other type, it will be converted to date and used as reference.
784 * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
785 * - 'syntax' entry (see below)
786 * - 'short' entry (see below)
787 * - 'parts' entry (see below)
788 * - 'options' entry (see below)
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100789 * - 'skip' entry, list of units to skip (array of strings or a single string,
790 * ` it can be the unit name (singular or plural) or its shortcut
791 * ` (y, m, w, d, h, min, s, ms, µs).
792 * - 'aUnit' entry, prefer "an hour" over "1 hour" if true
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200793 * - 'join' entry determines how to join multiple parts of the string
794 * ` - if $join is a string, it's used as a joiner glue
795 * ` - if $join is a callable/closure, it get the list of string and should return a string
796 * ` - if $join is an array, the first item will be the default glue, and the second item
797 * ` will be used instead of the glue for the last item
798 * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
799 * ` - if $join is missing, a space will be used as glue
800 * - 'other' entry (see above)
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100801 * - 'minimumUnit' entry determines the smallest unit of time to display can be long or
802 * ` short form of the units, e.g. 'hour' or 'h' (default value: s)
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200803 * if int passed, it add modifiers:
804 * Possible values:
805 * - CarbonInterface::DIFF_ABSOLUTE no modifiers
806 * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
807 * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
808 * Default value: CarbonInterface::DIFF_ABSOLUTE
809 * @param bool $short displays short format of time units
810 * @param int $parts maximum number of parts to display (default value: 1: single unit)
811 * @param int $options human diff options
812 *
813 * @return string
814 */
815 public function diffForHumans($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
816 {
817 /* @var CarbonInterface $this */
818 if (\is_array($other)) {
819 $other['syntax'] = \array_key_exists('syntax', $other) ? $other['syntax'] : $syntax;
820 $syntax = $other;
821 $other = $syntax['other'] ?? null;
822 }
823
824 $intSyntax = &$syntax;
825 if (\is_array($syntax)) {
826 $syntax['syntax'] = $syntax['syntax'] ?? null;
827 $intSyntax = &$syntax['syntax'];
828 }
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +0100829 $intSyntax = (int) ($intSyntax ?? static::DIFF_RELATIVE_AUTO);
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +0200830 $intSyntax = $intSyntax === static::DIFF_RELATIVE_AUTO && $other === null ? static::DIFF_RELATIVE_TO_NOW : $intSyntax;
831
832 $parts = min(7, max(1, (int) $parts));
833
834 return $this->diffAsCarbonInterval($other, false)
835 ->setLocalTranslator($this->getLocalTranslator())
836 ->forHumans($syntax, (bool) $short, $parts, $options ?? $this->localHumanDiffOptions ?? static::getHumanDiffOptions());
837 }
838
839 /**
840 * @alias diffForHumans
841 *
842 * Get the difference in a human readable format in the current locale from current instance to an other
843 * instance given (or now if null given).
844 *
845 * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
846 * if null passed, now will be used as comparison reference;
847 * if any other type, it will be converted to date and used as reference.
848 * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
849 * - 'syntax' entry (see below)
850 * - 'short' entry (see below)
851 * - 'parts' entry (see below)
852 * - 'options' entry (see below)
853 * - 'join' entry determines how to join multiple parts of the string
854 * ` - if $join is a string, it's used as a joiner glue
855 * ` - if $join is a callable/closure, it get the list of string and should return a string
856 * ` - if $join is an array, the first item will be the default glue, and the second item
857 * ` will be used instead of the glue for the last item
858 * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
859 * ` - if $join is missing, a space will be used as glue
860 * - 'other' entry (see above)
861 * if int passed, it add modifiers:
862 * Possible values:
863 * - CarbonInterface::DIFF_ABSOLUTE no modifiers
864 * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
865 * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
866 * Default value: CarbonInterface::DIFF_ABSOLUTE
867 * @param bool $short displays short format of time units
868 * @param int $parts maximum number of parts to display (default value: 1: single unit)
869 * @param int $options human diff options
870 *
871 * @return string
872 */
873 public function from($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
874 {
875 return $this->diffForHumans($other, $syntax, $short, $parts, $options);
876 }
877
878 /**
879 * @alias diffForHumans
880 *
881 * Get the difference in a human readable format in the current locale from current instance to an other
882 * instance given (or now if null given).
883 */
884 public function since($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
885 {
886 return $this->diffForHumans($other, $syntax, $short, $parts, $options);
887 }
888
889 /**
890 * Get the difference in a human readable format in the current locale from an other
891 * instance given (or now if null given) to current instance.
892 *
893 * When comparing a value in the past to default now:
894 * 1 hour from now
895 * 5 months from now
896 *
897 * When comparing a value in the future to default now:
898 * 1 hour ago
899 * 5 months ago
900 *
901 * When comparing a value in the past to another value:
902 * 1 hour after
903 * 5 months after
904 *
905 * When comparing a value in the future to another value:
906 * 1 hour before
907 * 5 months before
908 *
909 * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
910 * if null passed, now will be used as comparison reference;
911 * if any other type, it will be converted to date and used as reference.
912 * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
913 * - 'syntax' entry (see below)
914 * - 'short' entry (see below)
915 * - 'parts' entry (see below)
916 * - 'options' entry (see below)
917 * - 'join' entry determines how to join multiple parts of the string
918 * ` - if $join is a string, it's used as a joiner glue
919 * ` - if $join is a callable/closure, it get the list of string and should return a string
920 * ` - if $join is an array, the first item will be the default glue, and the second item
921 * ` will be used instead of the glue for the last item
922 * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
923 * ` - if $join is missing, a space will be used as glue
924 * - 'other' entry (see above)
925 * if int passed, it add modifiers:
926 * Possible values:
927 * - CarbonInterface::DIFF_ABSOLUTE no modifiers
928 * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
929 * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
930 * Default value: CarbonInterface::DIFF_ABSOLUTE
931 * @param bool $short displays short format of time units
932 * @param int $parts maximum number of parts to display (default value: 1: single unit)
933 * @param int $options human diff options
934 *
935 * @return string
936 */
937 public function to($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
938 {
939 if (!$syntax && !$other) {
940 $syntax = CarbonInterface::DIFF_RELATIVE_TO_NOW;
941 }
942
943 return $this->resolveCarbon($other)->diffForHumans($this, $syntax, $short, $parts, $options);
944 }
945
946 /**
947 * @alias to
948 *
949 * Get the difference in a human readable format in the current locale from an other
950 * instance given (or now if null given) to current instance.
951 *
952 * @param Carbon|\DateTimeInterface|string|array|null $other if array passed, will be used as parameters array, see $syntax below;
953 * if null passed, now will be used as comparison reference;
954 * if any other type, it will be converted to date and used as reference.
955 * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
956 * - 'syntax' entry (see below)
957 * - 'short' entry (see below)
958 * - 'parts' entry (see below)
959 * - 'options' entry (see below)
960 * - 'join' entry determines how to join multiple parts of the string
961 * ` - if $join is a string, it's used as a joiner glue
962 * ` - if $join is a callable/closure, it get the list of string and should return a string
963 * ` - if $join is an array, the first item will be the default glue, and the second item
964 * ` will be used instead of the glue for the last item
965 * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
966 * ` - if $join is missing, a space will be used as glue
967 * - 'other' entry (see above)
968 * if int passed, it add modifiers:
969 * Possible values:
970 * - CarbonInterface::DIFF_ABSOLUTE no modifiers
971 * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
972 * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
973 * Default value: CarbonInterface::DIFF_ABSOLUTE
974 * @param bool $short displays short format of time units
975 * @param int $parts maximum number of parts to display (default value: 1: single unit)
976 * @param int $options human diff options
977 *
978 * @return string
979 */
980 public function until($other = null, $syntax = null, $short = false, $parts = 1, $options = null)
981 {
982 return $this->to($other, $syntax, $short, $parts, $options);
983 }
984
985 /**
986 * Get the difference in a human readable format in the current locale from current
987 * instance to now.
988 *
989 * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
990 * - 'syntax' entry (see below)
991 * - 'short' entry (see below)
992 * - 'parts' entry (see below)
993 * - 'options' entry (see below)
994 * - 'join' entry determines how to join multiple parts of the string
995 * ` - if $join is a string, it's used as a joiner glue
996 * ` - if $join is a callable/closure, it get the list of string and should return a string
997 * ` - if $join is an array, the first item will be the default glue, and the second item
998 * ` will be used instead of the glue for the last item
999 * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
1000 * ` - if $join is missing, a space will be used as glue
1001 * if int passed, it add modifiers:
1002 * Possible values:
1003 * - CarbonInterface::DIFF_ABSOLUTE no modifiers
1004 * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
1005 * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
1006 * Default value: CarbonInterface::DIFF_ABSOLUTE
1007 * @param bool $short displays short format of time units
1008 * @param int $parts maximum number of parts to display (default value: 1: single unit)
1009 * @param int $options human diff options
1010 *
1011 * @return string
1012 */
1013 public function fromNow($syntax = null, $short = false, $parts = 1, $options = null)
1014 {
1015 $other = null;
1016
1017 if ($syntax instanceof DateTimeInterface) {
1018 [$other, $syntax, $short, $parts, $options] = array_pad(\func_get_args(), 5, null);
1019 }
1020
1021 return $this->from($other, $syntax, $short, $parts, $options);
1022 }
1023
1024 /**
1025 * Get the difference in a human readable format in the current locale from an other
1026 * instance given to now
1027 *
1028 * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
1029 * - 'syntax' entry (see below)
1030 * - 'short' entry (see below)
1031 * - 'parts' entry (see below)
1032 * - 'options' entry (see below)
1033 * - 'join' entry determines how to join multiple parts of the string
1034 * ` - if $join is a string, it's used as a joiner glue
1035 * ` - if $join is a callable/closure, it get the list of string and should return a string
1036 * ` - if $join is an array, the first item will be the default glue, and the second item
1037 * ` will be used instead of the glue for the last item
1038 * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
1039 * ` - if $join is missing, a space will be used as glue
1040 * if int passed, it add modifiers:
1041 * Possible values:
1042 * - CarbonInterface::DIFF_ABSOLUTE no modifiers
1043 * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
1044 * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
1045 * Default value: CarbonInterface::DIFF_ABSOLUTE
1046 * @param bool $short displays short format of time units
1047 * @param int $parts maximum number of parts to display (default value: 1: single part)
1048 * @param int $options human diff options
1049 *
1050 * @return string
1051 */
1052 public function toNow($syntax = null, $short = false, $parts = 1, $options = null)
1053 {
1054 return $this->to(null, $syntax, $short, $parts, $options);
1055 }
1056
1057 /**
1058 * Get the difference in a human readable format in the current locale from an other
1059 * instance given to now
1060 *
1061 * @param int|array $syntax if array passed, parameters will be extracted from it, the array may contains:
1062 * - 'syntax' entry (see below)
1063 * - 'short' entry (see below)
1064 * - 'parts' entry (see below)
1065 * - 'options' entry (see below)
1066 * - 'join' entry determines how to join multiple parts of the string
1067 * ` - if $join is a string, it's used as a joiner glue
1068 * ` - if $join is a callable/closure, it get the list of string and should return a string
1069 * ` - if $join is an array, the first item will be the default glue, and the second item
1070 * ` will be used instead of the glue for the last item
1071 * ` - if $join is true, it will be guessed from the locale ('list' translation file entry)
1072 * ` - if $join is missing, a space will be used as glue
1073 * if int passed, it add modifiers:
1074 * Possible values:
1075 * - CarbonInterface::DIFF_ABSOLUTE no modifiers
1076 * - CarbonInterface::DIFF_RELATIVE_TO_NOW add ago/from now modifier
1077 * - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
1078 * Default value: CarbonInterface::DIFF_ABSOLUTE
1079 * @param bool $short displays short format of time units
1080 * @param int $parts maximum number of parts to display (default value: 1: single part)
1081 * @param int $options human diff options
1082 *
1083 * @return string
1084 */
1085 public function ago($syntax = null, $short = false, $parts = 1, $options = null)
1086 {
1087 $other = null;
1088
1089 if ($syntax instanceof DateTimeInterface) {
1090 [$other, $syntax, $short, $parts, $options] = array_pad(\func_get_args(), 5, null);
1091 }
1092
1093 return $this->from($other, $syntax, $short, $parts, $options);
1094 }
1095
1096 /**
1097 * Get the difference in a human readable format in the current locale from current instance to an other
1098 * instance given (or now if null given).
1099 *
1100 * @return string
1101 */
1102 public function timespan($other = null, $timezone = null)
1103 {
1104 if (!$other instanceof DateTimeInterface) {
1105 $other = static::parse($other, $timezone);
1106 }
1107
1108 return $this->diffForHumans($other, [
1109 'join' => ', ',
1110 'syntax' => CarbonInterface::DIFF_ABSOLUTE,
1111 'options' => CarbonInterface::NO_ZERO_DIFF,
1112 'parts' => -1,
1113 ]);
1114 }
1115
1116 /**
1117 * Returns either day of week + time (e.g. "Last Friday at 3:30 PM") if reference time is within 7 days,
1118 * or a calendar date (e.g. "10/29/2017") otherwise.
1119 *
1120 * Language, date and time formats will change according to the current locale.
1121 *
1122 * @param Carbon|\DateTimeInterface|string|null $referenceTime
1123 * @param array $formats
1124 *
1125 * @return string
1126 */
1127 public function calendar($referenceTime = null, array $formats = [])
1128 {
1129 /** @var CarbonInterface $current */
1130 $current = $this->avoidMutation()->startOfDay();
1131 /** @var CarbonInterface $other */
1132 $other = $this->resolveCarbon($referenceTime)->avoidMutation()->setTimezone($this->getTimezone())->startOfDay();
1133 $diff = $other->diffInDays($current, false);
1134 $format = $diff < -6 ? 'sameElse' : (
1135 $diff < -1 ? 'lastWeek' : (
1136 $diff < 0 ? 'lastDay' : (
1137 $diff < 1 ? 'sameDay' : (
1138 $diff < 2 ? 'nextDay' : (
1139 $diff < 7 ? 'nextWeek' : 'sameElse'
1140 )
1141 )
1142 )
1143 )
1144 );
1145 $format = array_merge($this->getCalendarFormats(), $formats)[$format];
1146 if ($format instanceof Closure) {
1147 $format = $format($current, $other) ?? '';
1148 }
1149
1150 return $this->isoFormat((string) $format);
1151 }
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001152
1153 private function getIntervalDayDiff(DateInterval $interval): int
1154 {
1155 $daysDiff = (int) $interval->format('%a');
1156 $sign = $interval->format('%r') === '-' ? -1 : 1;
1157
1158 if (\is_int($interval->days) &&
1159 $interval->y === 0 &&
1160 $interval->m === 0 &&
1161 version_compare(PHP_VERSION, '8.1.0-dev', '<') &&
1162 abs($interval->d - $daysDiff) === 1
1163 ) {
1164 $daysDiff = abs($interval->d);
1165 }
1166
1167 return $daysDiff * $sign;
1168 }
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001169}