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