Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 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
 
12
namespace Carbon;
13
 
14
use Carbon\Exceptions\BadFluentConstructorException;
15
use Carbon\Exceptions\BadFluentSetterException;
16
use Carbon\Exceptions\InvalidCastException;
17
use Carbon\Exceptions\InvalidIntervalException;
18
use Carbon\Exceptions\ParseErrorException;
19
use Carbon\Exceptions\UnitNotConfiguredException;
20
use Carbon\Exceptions\UnknownGetterException;
21
use Carbon\Exceptions\UnknownSetterException;
22
use Carbon\Exceptions\UnknownUnitException;
23
use Carbon\Traits\IntervalRounding;
24
use Carbon\Traits\IntervalStep;
25
use Carbon\Traits\MagicParameter;
26
use Carbon\Traits\Mixin;
27
use Carbon\Traits\Options;
28
use Carbon\Traits\ToStringFormat;
29
use Closure;
30
use DateInterval;
31
use DateTimeInterface;
32
use DateTimeZone;
33
use Exception;
34
use ReflectionException;
35
use ReturnTypeWillChange;
36
use Throwable;
37
 
38
/**
39
 * A simple API extension for DateInterval.
40
 * The implementation provides helpers to handle weeks but only days are saved.
41
 * Weeks are calculated based on the total days of the current instance.
42
 *
43
 * @property int $years Total years of the current interval.
44
 * @property int $months Total months of the current interval.
45
 * @property int $weeks Total weeks of the current interval calculated from the days.
46
 * @property int $dayz Total days of the current interval (weeks * 7 + days).
47
 * @property int $hours Total hours of the current interval.
48
 * @property int $minutes Total minutes of the current interval.
49
 * @property int $seconds Total seconds of the current interval.
50
 * @property int $microseconds Total microseconds of the current interval.
51
 * @property int $milliseconds Total milliseconds of the current interval.
52
 * @property int $microExcludeMilli Remaining microseconds without the milliseconds.
53
 * @property int $dayzExcludeWeeks Total days remaining in the final week of the current instance (days % 7).
54
 * @property int $daysExcludeWeeks alias of dayzExcludeWeeks
55
 * @property-read float $totalYears Number of years equivalent to the interval.
56
 * @property-read float $totalMonths Number of months equivalent to the interval.
57
 * @property-read float $totalWeeks Number of weeks equivalent to the interval.
58
 * @property-read float $totalDays Number of days equivalent to the interval.
59
 * @property-read float $totalDayz Alias for totalDays.
60
 * @property-read float $totalHours Number of hours equivalent to the interval.
61
 * @property-read float $totalMinutes Number of minutes equivalent to the interval.
62
 * @property-read float $totalSeconds Number of seconds equivalent to the interval.
63
 * @property-read float $totalMilliseconds Number of milliseconds equivalent to the interval.
64
 * @property-read float $totalMicroseconds Number of microseconds equivalent to the interval.
65
 * @property-read string $locale locale of the current instance
66
 *
67
 * @method static CarbonInterval years($years = 1) Create instance specifying a number of years or modify the number of years if called on an instance.
68
 * @method static CarbonInterval year($years = 1) Alias for years()
69
 * @method static CarbonInterval months($months = 1) Create instance specifying a number of months or modify the number of months if called on an instance.
70
 * @method static CarbonInterval month($months = 1) Alias for months()
71
 * @method static CarbonInterval weeks($weeks = 1) Create instance specifying a number of weeks or modify the number of weeks if called on an instance.
72
 * @method static CarbonInterval week($weeks = 1) Alias for weeks()
73
 * @method static CarbonInterval days($days = 1) Create instance specifying a number of days or modify the number of days if called on an instance.
74
 * @method static CarbonInterval dayz($days = 1) Alias for days()
75
 * @method static CarbonInterval daysExcludeWeeks($days = 1) Create instance specifying a number of days or modify the number of days (keeping the current number of weeks) if called on an instance.
76
 * @method static CarbonInterval dayzExcludeWeeks($days = 1) Alias for daysExcludeWeeks()
77
 * @method static CarbonInterval day($days = 1) Alias for days()
78
 * @method static CarbonInterval hours($hours = 1) Create instance specifying a number of hours or modify the number of hours if called on an instance.
79
 * @method static CarbonInterval hour($hours = 1) Alias for hours()
80
 * @method static CarbonInterval minutes($minutes = 1) Create instance specifying a number of minutes or modify the number of minutes if called on an instance.
81
 * @method static CarbonInterval minute($minutes = 1) Alias for minutes()
82
 * @method static CarbonInterval seconds($seconds = 1) Create instance specifying a number of seconds or modify the number of seconds if called on an instance.
83
 * @method static CarbonInterval second($seconds = 1) Alias for seconds()
84
 * @method static CarbonInterval milliseconds($milliseconds = 1) Create instance specifying a number of milliseconds or modify the number of milliseconds if called on an instance.
85
 * @method static CarbonInterval millisecond($milliseconds = 1) Alias for milliseconds()
86
 * @method static CarbonInterval microseconds($microseconds = 1) Create instance specifying a number of microseconds or modify the number of microseconds if called on an instance.
87
 * @method static CarbonInterval microsecond($microseconds = 1) Alias for microseconds()
88
 * @method $this addYears(int $years) Add given number of years to the current interval
89
 * @method $this subYears(int $years) Subtract given number of years to the current interval
90
 * @method $this addMonths(int $months) Add given number of months to the current interval
91
 * @method $this subMonths(int $months) Subtract given number of months to the current interval
92
 * @method $this addWeeks(int|float $weeks) Add given number of weeks to the current interval
93
 * @method $this subWeeks(int|float $weeks) Subtract given number of weeks to the current interval
94
 * @method $this addDays(int|float $days) Add given number of days to the current interval
95
 * @method $this subDays(int|float $days) Subtract given number of days to the current interval
96
 * @method $this addHours(int|float $hours) Add given number of hours to the current interval
97
 * @method $this subHours(int|float $hours) Subtract given number of hours to the current interval
98
 * @method $this addMinutes(int|float $minutes) Add given number of minutes to the current interval
99
 * @method $this subMinutes(int|float $minutes) Subtract given number of minutes to the current interval
100
 * @method $this addSeconds(int|float $seconds) Add given number of seconds to the current interval
101
 * @method $this subSeconds(int|float $seconds) Subtract given number of seconds to the current interval
102
 * @method $this addMilliseconds(int|float $milliseconds) Add given number of milliseconds to the current interval
103
 * @method $this subMilliseconds(int|float $milliseconds) Subtract given number of milliseconds to the current interval
104
 * @method $this addMicroseconds(int|float $microseconds) Add given number of microseconds to the current interval
105
 * @method $this subMicroseconds(int|float $microseconds) Subtract given number of microseconds to the current interval
106
 * @method $this roundYear(int|float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
107
 * @method $this roundYears(int|float $precision = 1, string $function = "round") Round the current instance year with given precision using the given function.
108
 * @method $this floorYear(int|float $precision = 1) Truncate the current instance year with given precision.
109
 * @method $this floorYears(int|float $precision = 1) Truncate the current instance year with given precision.
110
 * @method $this ceilYear(int|float $precision = 1) Ceil the current instance year with given precision.
111
 * @method $this ceilYears(int|float $precision = 1) Ceil the current instance year with given precision.
112
 * @method $this roundMonth(int|float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
113
 * @method $this roundMonths(int|float $precision = 1, string $function = "round") Round the current instance month with given precision using the given function.
114
 * @method $this floorMonth(int|float $precision = 1) Truncate the current instance month with given precision.
115
 * @method $this floorMonths(int|float $precision = 1) Truncate the current instance month with given precision.
116
 * @method $this ceilMonth(int|float $precision = 1) Ceil the current instance month with given precision.
117
 * @method $this ceilMonths(int|float $precision = 1) Ceil the current instance month with given precision.
118
 * @method $this roundWeek(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
119
 * @method $this roundWeeks(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
120
 * @method $this floorWeek(int|float $precision = 1) Truncate the current instance day with given precision.
121
 * @method $this floorWeeks(int|float $precision = 1) Truncate the current instance day with given precision.
122
 * @method $this ceilWeek(int|float $precision = 1) Ceil the current instance day with given precision.
123
 * @method $this ceilWeeks(int|float $precision = 1) Ceil the current instance day with given precision.
124
 * @method $this roundDay(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
125
 * @method $this roundDays(int|float $precision = 1, string $function = "round") Round the current instance day with given precision using the given function.
126
 * @method $this floorDay(int|float $precision = 1) Truncate the current instance day with given precision.
127
 * @method $this floorDays(int|float $precision = 1) Truncate the current instance day with given precision.
128
 * @method $this ceilDay(int|float $precision = 1) Ceil the current instance day with given precision.
129
 * @method $this ceilDays(int|float $precision = 1) Ceil the current instance day with given precision.
130
 * @method $this roundHour(int|float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
131
 * @method $this roundHours(int|float $precision = 1, string $function = "round") Round the current instance hour with given precision using the given function.
132
 * @method $this floorHour(int|float $precision = 1) Truncate the current instance hour with given precision.
133
 * @method $this floorHours(int|float $precision = 1) Truncate the current instance hour with given precision.
134
 * @method $this ceilHour(int|float $precision = 1) Ceil the current instance hour with given precision.
135
 * @method $this ceilHours(int|float $precision = 1) Ceil the current instance hour with given precision.
136
 * @method $this roundMinute(int|float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
137
 * @method $this roundMinutes(int|float $precision = 1, string $function = "round") Round the current instance minute with given precision using the given function.
138
 * @method $this floorMinute(int|float $precision = 1) Truncate the current instance minute with given precision.
139
 * @method $this floorMinutes(int|float $precision = 1) Truncate the current instance minute with given precision.
140
 * @method $this ceilMinute(int|float $precision = 1) Ceil the current instance minute with given precision.
141
 * @method $this ceilMinutes(int|float $precision = 1) Ceil the current instance minute with given precision.
142
 * @method $this roundSecond(int|float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
143
 * @method $this roundSeconds(int|float $precision = 1, string $function = "round") Round the current instance second with given precision using the given function.
144
 * @method $this floorSecond(int|float $precision = 1) Truncate the current instance second with given precision.
145
 * @method $this floorSeconds(int|float $precision = 1) Truncate the current instance second with given precision.
146
 * @method $this ceilSecond(int|float $precision = 1) Ceil the current instance second with given precision.
147
 * @method $this ceilSeconds(int|float $precision = 1) Ceil the current instance second with given precision.
148
 * @method $this roundMillennium(int|float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
149
 * @method $this roundMillennia(int|float $precision = 1, string $function = "round") Round the current instance millennium with given precision using the given function.
150
 * @method $this floorMillennium(int|float $precision = 1) Truncate the current instance millennium with given precision.
151
 * @method $this floorMillennia(int|float $precision = 1) Truncate the current instance millennium with given precision.
152
 * @method $this ceilMillennium(int|float $precision = 1) Ceil the current instance millennium with given precision.
153
 * @method $this ceilMillennia(int|float $precision = 1) Ceil the current instance millennium with given precision.
154
 * @method $this roundCentury(int|float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
155
 * @method $this roundCenturies(int|float $precision = 1, string $function = "round") Round the current instance century with given precision using the given function.
156
 * @method $this floorCentury(int|float $precision = 1) Truncate the current instance century with given precision.
157
 * @method $this floorCenturies(int|float $precision = 1) Truncate the current instance century with given precision.
158
 * @method $this ceilCentury(int|float $precision = 1) Ceil the current instance century with given precision.
159
 * @method $this ceilCenturies(int|float $precision = 1) Ceil the current instance century with given precision.
160
 * @method $this roundDecade(int|float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
161
 * @method $this roundDecades(int|float $precision = 1, string $function = "round") Round the current instance decade with given precision using the given function.
162
 * @method $this floorDecade(int|float $precision = 1) Truncate the current instance decade with given precision.
163
 * @method $this floorDecades(int|float $precision = 1) Truncate the current instance decade with given precision.
164
 * @method $this ceilDecade(int|float $precision = 1) Ceil the current instance decade with given precision.
165
 * @method $this ceilDecades(int|float $precision = 1) Ceil the current instance decade with given precision.
166
 * @method $this roundQuarter(int|float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
167
 * @method $this roundQuarters(int|float $precision = 1, string $function = "round") Round the current instance quarter with given precision using the given function.
168
 * @method $this floorQuarter(int|float $precision = 1) Truncate the current instance quarter with given precision.
169
 * @method $this floorQuarters(int|float $precision = 1) Truncate the current instance quarter with given precision.
170
 * @method $this ceilQuarter(int|float $precision = 1) Ceil the current instance quarter with given precision.
171
 * @method $this ceilQuarters(int|float $precision = 1) Ceil the current instance quarter with given precision.
172
 * @method $this roundMillisecond(int|float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
173
 * @method $this roundMilliseconds(int|float $precision = 1, string $function = "round") Round the current instance millisecond with given precision using the given function.
174
 * @method $this floorMillisecond(int|float $precision = 1) Truncate the current instance millisecond with given precision.
175
 * @method $this floorMilliseconds(int|float $precision = 1) Truncate the current instance millisecond with given precision.
176
 * @method $this ceilMillisecond(int|float $precision = 1) Ceil the current instance millisecond with given precision.
177
 * @method $this ceilMilliseconds(int|float $precision = 1) Ceil the current instance millisecond with given precision.
178
 * @method $this roundMicrosecond(int|float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
179
 * @method $this roundMicroseconds(int|float $precision = 1, string $function = "round") Round the current instance microsecond with given precision using the given function.
180
 * @method $this floorMicrosecond(int|float $precision = 1) Truncate the current instance microsecond with given precision.
181
 * @method $this floorMicroseconds(int|float $precision = 1) Truncate the current instance microsecond with given precision.
182
 * @method $this ceilMicrosecond(int|float $precision = 1) Ceil the current instance microsecond with given precision.
183
 * @method $this ceilMicroseconds(int|float $precision = 1) Ceil the current instance microsecond with given precision.
184
 */
185
class CarbonInterval extends DateInterval implements CarbonConverterInterface
186
{
187
    use IntervalRounding;
188
    use IntervalStep;
189
    use MagicParameter;
190
    use Mixin {
191
        Mixin::mixin as baseMixin;
192
    }
193
    use Options;
194
    use ToStringFormat;
195
 
196
    /**
197
     * Interval spec period designators
198
     */
199
    public const PERIOD_PREFIX = 'P';
200
    public const PERIOD_YEARS = 'Y';
201
    public const PERIOD_MONTHS = 'M';
202
    public const PERIOD_DAYS = 'D';
203
    public const PERIOD_TIME_PREFIX = 'T';
204
    public const PERIOD_HOURS = 'H';
205
    public const PERIOD_MINUTES = 'M';
206
    public const PERIOD_SECONDS = 'S';
207
 
208
    /**
209
     * A translator to ... er ... translate stuff
210
     *
211
     * @var \Symfony\Component\Translation\TranslatorInterface
212
     */
213
    protected static $translator;
214
 
215
    /**
216
     * @var array|null
217
     */
218
    protected static $cascadeFactors;
219
 
220
    /**
221
     * @var array
222
     */
223
    protected static $formats = [
224
        'y' => 'y',
225
        'Y' => 'y',
226
        'o' => 'y',
227
        'm' => 'm',
228
        'n' => 'm',
229
        'W' => 'weeks',
230
        'd' => 'd',
231
        'j' => 'd',
232
        'z' => 'd',
233
        'h' => 'h',
234
        'g' => 'h',
235
        'H' => 'h',
236
        'G' => 'h',
237
        'i' => 'i',
238
        's' => 's',
239
        'u' => 'micro',
240
        'v' => 'milli',
241
    ];
242
 
243
    /**
244
     * @var array|null
245
     */
246
    private static $flipCascadeFactors;
247
 
248
    /**
249
     * The registered macros.
250
     *
251
     * @var array
252
     */
253
    protected static $macros = [];
254
 
255
    /**
256
     * Timezone handler for settings() method.
257
     *
258
     * @var mixed
259
     */
260
    protected $tzName;
261
 
262
    /**
263
     * Set the instance's timezone from a string or object.
264
     *
265
     * @param \DateTimeZone|string $tzName
266
     *
267
     * @return static
268
     */
269
    public function setTimezone($tzName)
270
    {
271
        $this->tzName = $tzName;
272
 
273
        return $this;
274
    }
275
 
276
    /**
277
     * @internal
278
     *
279
     * Set the instance's timezone from a string or object and add/subtract the offset difference.
280
     *
281
     * @param \DateTimeZone|string $tzName
282
     *
283
     * @return static
284
     */
285
    public function shiftTimezone($tzName)
286
    {
287
        $this->tzName = $tzName;
288
 
289
        return $this;
290
    }
291
 
292
    /**
293
     * Mapping of units and factors for cascading.
294
     *
295
     * Should only be modified by changing the factors or referenced constants.
296
     *
297
     * @return array
298
     */
299
    public static function getCascadeFactors()
300
    {
301
        return static::$cascadeFactors ?: [
302
            'milliseconds' => [Carbon::MICROSECONDS_PER_MILLISECOND, 'microseconds'],
303
            'seconds' => [Carbon::MILLISECONDS_PER_SECOND, 'milliseconds'],
304
            'minutes' => [Carbon::SECONDS_PER_MINUTE, 'seconds'],
305
            'hours' => [Carbon::MINUTES_PER_HOUR, 'minutes'],
306
            'dayz' => [Carbon::HOURS_PER_DAY, 'hours'],
307
            'weeks' => [Carbon::DAYS_PER_WEEK, 'dayz'],
308
            'months' => [Carbon::WEEKS_PER_MONTH, 'weeks'],
309
            'years' => [Carbon::MONTHS_PER_YEAR, 'months'],
310
        ];
311
    }
312
 
313
    private static function standardizeUnit($unit)
314
    {
315
        $unit = rtrim($unit, 'sz').'s';
316
 
317
        return $unit === 'days' ? 'dayz' : $unit;
318
    }
319
 
320
    private static function getFlipCascadeFactors()
321
    {
322
        if (!self::$flipCascadeFactors) {
323
            self::$flipCascadeFactors = [];
324
 
325
            foreach (static::getCascadeFactors() as $to => [$factor, $from]) {
326
                self::$flipCascadeFactors[self::standardizeUnit($from)] = [self::standardizeUnit($to), $factor];
327
            }
328
        }
329
 
330
        return self::$flipCascadeFactors;
331
    }
332
 
333
    /**
334
     * Set default cascading factors for ->cascade() method.
335
     *
336
     * @param array $cascadeFactors
337
     */
338
    public static function setCascadeFactors(array $cascadeFactors)
339
    {
340
        self::$flipCascadeFactors = null;
341
        static::$cascadeFactors = $cascadeFactors;
342
    }
343
 
344
    ///////////////////////////////////////////////////////////////////
345
    //////////////////////////// CONSTRUCTORS /////////////////////////
346
    ///////////////////////////////////////////////////////////////////
347
 
348
    /**
349
     * Create a new CarbonInterval instance.
350
     *
351
     * @param Closure|DateInterval|string|int|null $years
352
     * @param int|null                             $months
353
     * @param int|null                             $weeks
354
     * @param int|null                             $days
355
     * @param int|null                             $hours
356
     * @param int|null                             $minutes
357
     * @param int|null                             $seconds
358
     * @param int|null                             $microseconds
359
     *
360
     * @throws Exception when the interval_spec (passed as $years) cannot be parsed as an interval.
361
     */
362
    public function __construct($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null)
363
    {
364
        if ($years instanceof Closure) {
365
            $this->step = $years;
366
            $years = null;
367
        }
368
 
369
        if ($years instanceof DateInterval) {
370
            parent::__construct(static::getDateIntervalSpec($years));
371
            $this->f = $years->f;
372
            self::copyNegativeUnits($years, $this);
373
 
374
            return;
375
        }
376
 
377
        $spec = $years;
378
 
379
        if (!\is_string($spec) || (float) $years || preg_match('/^[\d.]/', $years)) {
380
            $spec = static::PERIOD_PREFIX;
381
 
382
            $spec .= $years > 0 ? $years.static::PERIOD_YEARS : '';
383
            $spec .= $months > 0 ? $months.static::PERIOD_MONTHS : '';
384
 
385
            $specDays = 0;
386
            $specDays += $weeks > 0 ? $weeks * static::getDaysPerWeek() : 0;
387
            $specDays += $days > 0 ? $days : 0;
388
 
389
            $spec .= $specDays > 0 ? $specDays.static::PERIOD_DAYS : '';
390
 
391
            if ($hours > 0 || $minutes > 0 || $seconds > 0) {
392
                $spec .= static::PERIOD_TIME_PREFIX;
393
                $spec .= $hours > 0 ? $hours.static::PERIOD_HOURS : '';
394
                $spec .= $minutes > 0 ? $minutes.static::PERIOD_MINUTES : '';
395
                $spec .= $seconds > 0 ? $seconds.static::PERIOD_SECONDS : '';
396
            }
397
 
398
            if ($spec === static::PERIOD_PREFIX) {
399
                // Allow the zero interval.
400
                $spec .= '0'.static::PERIOD_YEARS;
401
            }
402
        }
403
 
404
        parent::__construct($spec);
405
 
406
        if ($microseconds !== null) {
407
            $this->f = $microseconds / Carbon::MICROSECONDS_PER_SECOND;
408
        }
409
    }
410
 
411
    /**
412
     * Returns the factor for a given source-to-target couple.
413
     *
414
     * @param string $source
415
     * @param string $target
416
     *
417
     * @return int|float|null
418
     */
419
    public static function getFactor($source, $target)
420
    {
421
        $source = self::standardizeUnit($source);
422
        $target = self::standardizeUnit($target);
423
        $factors = self::getFlipCascadeFactors();
424
 
425
        if (isset($factors[$source])) {
426
            [$to, $factor] = $factors[$source];
427
 
428
            if ($to === $target) {
429
                return $factor;
430
            }
431
 
432
            return $factor * static::getFactor($to, $target);
433
        }
434
 
435
        return null;
436
    }
437
 
438
    /**
439
     * Returns the factor for a given source-to-target couple if set,
440
     * else try to find the appropriate constant as the factor, such as Carbon::DAYS_PER_WEEK.
441
     *
442
     * @param string $source
443
     * @param string $target
444
     *
445
     * @return int|float|null
446
     */
447
    public static function getFactorWithDefault($source, $target)
448
    {
449
        $factor = self::getFactor($source, $target);
450
 
451
        if ($factor) {
452
            return $factor;
453
        }
454
 
455
        static $defaults = [
456
            'month' => ['year' => Carbon::MONTHS_PER_YEAR],
457
            'week' => ['month' => Carbon::WEEKS_PER_MONTH],
458
            'day' => ['week' => Carbon::DAYS_PER_WEEK],
459
            'hour' => ['day' => Carbon::HOURS_PER_DAY],
460
            'minute' => ['hour' => Carbon::MINUTES_PER_HOUR],
461
            'second' => ['minute' => Carbon::SECONDS_PER_MINUTE],
462
            'millisecond' => ['second' => Carbon::MILLISECONDS_PER_SECOND],
463
            'microsecond' => ['millisecond' => Carbon::MICROSECONDS_PER_MILLISECOND],
464
        ];
465
 
466
        return $defaults[$source][$target] ?? null;
467
    }
468
 
469
    /**
470
     * Returns current config for days per week.
471
     *
472
     * @return int|float
473
     */
474
    public static function getDaysPerWeek()
475
    {
476
        return static::getFactor('dayz', 'weeks') ?: Carbon::DAYS_PER_WEEK;
477
    }
478
 
479
    /**
480
     * Returns current config for hours per day.
481
     *
482
     * @return int|float
483
     */
484
    public static function getHoursPerDay()
485
    {
486
        return static::getFactor('hours', 'dayz') ?: Carbon::HOURS_PER_DAY;
487
    }
488
 
489
    /**
490
     * Returns current config for minutes per hour.
491
     *
492
     * @return int|float
493
     */
494
    public static function getMinutesPerHour()
495
    {
496
        return static::getFactor('minutes', 'hours') ?: Carbon::MINUTES_PER_HOUR;
497
    }
498
 
499
    /**
500
     * Returns current config for seconds per minute.
501
     *
502
     * @return int|float
503
     */
504
    public static function getSecondsPerMinute()
505
    {
506
        return static::getFactor('seconds', 'minutes') ?: Carbon::SECONDS_PER_MINUTE;
507
    }
508
 
509
    /**
510
     * Returns current config for microseconds per second.
511
     *
512
     * @return int|float
513
     */
514
    public static function getMillisecondsPerSecond()
515
    {
516
        return static::getFactor('milliseconds', 'seconds') ?: Carbon::MILLISECONDS_PER_SECOND;
517
    }
518
 
519
    /**
520
     * Returns current config for microseconds per second.
521
     *
522
     * @return int|float
523
     */
524
    public static function getMicrosecondsPerMillisecond()
525
    {
526
        return static::getFactor('microseconds', 'milliseconds') ?: Carbon::MICROSECONDS_PER_MILLISECOND;
527
    }
528
 
529
    /**
530
     * Create a new CarbonInterval instance from specific values.
531
     * This is an alias for the constructor that allows better fluent
532
     * syntax as it allows you to do CarbonInterval::create(1)->fn() rather than
533
     * (new CarbonInterval(1))->fn().
534
     *
535
     * @param int $years
536
     * @param int $months
537
     * @param int $weeks
538
     * @param int $days
539
     * @param int $hours
540
     * @param int $minutes
541
     * @param int $seconds
542
     * @param int $microseconds
543
     *
544
     * @throws Exception when the interval_spec (passed as $years) cannot be parsed as an interval.
545
     *
546
     * @return static
547
     */
548
    public static function create($years = 1, $months = null, $weeks = null, $days = null, $hours = null, $minutes = null, $seconds = null, $microseconds = null)
549
    {
550
        return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds, $microseconds);
551
    }
552
 
553
    /**
554
     * Parse a string into a new CarbonInterval object according to the specified format.
555
     *
556
     * @example
557
     * ```
558
     * echo Carboninterval::createFromFormat('H:i', '1:30');
559
     * ```
560
     *
561
     * @param string      $format   Format of the $interval input string
562
     * @param string|null $interval Input string to convert into an interval
563
     *
564
     * @throws \Carbon\Exceptions\ParseErrorException when the $interval cannot be parsed as an interval.
565
     *
566
     * @return static
567
     */
568
    public static function createFromFormat(string $format, ?string $interval)
569
    {
570
        $instance = new static(0);
571
        $length = mb_strlen($format);
572
 
573
        if (preg_match('/s([,.])([uv])$/', $format, $match)) {
574
            $interval = explode($match[1], $interval);
575
            $index = \count($interval) - 1;
576
            $interval[$index] = str_pad($interval[$index], $match[2] === 'v' ? 3 : 6, '0');
577
            $interval = implode($match[1], $interval);
578
        }
579
 
580
        $interval = $interval ?? '';
581
 
582
        for ($index = 0; $index < $length; $index++) {
583
            $expected = mb_substr($format, $index, 1);
584
            $nextCharacter = mb_substr($interval, 0, 1);
585
            $unit = static::$formats[$expected] ?? null;
586
 
587
            if ($unit) {
588
                if (!preg_match('/^-?\d+/', $interval, $match)) {
589
                    throw new ParseErrorException('number', $nextCharacter);
590
                }
591
 
592
                $interval = mb_substr($interval, mb_strlen($match[0]));
593
                $instance->$unit += (int) ($match[0]);
594
 
595
                continue;
596
            }
597
 
598
            if ($nextCharacter !== $expected) {
599
                throw new ParseErrorException(
600
                    "'$expected'",
601
                    $nextCharacter,
602
                    'Allowed substitutes for interval formats are '.implode(', ', array_keys(static::$formats))."\n".
603
                    'See https://php.net/manual/en/function.date.php for their meaning'
604
                );
605
            }
606
 
607
            $interval = mb_substr($interval, 1);
608
        }
609
 
610
        if ($interval !== '') {
611
            throw new ParseErrorException(
612
                'end of string',
613
                $interval
614
            );
615
        }
616
 
617
        return $instance;
618
    }
619
 
620
    /**
621
     * Get a copy of the instance.
622
     *
623
     * @return static
624
     */
625
    public function copy()
626
    {
627
        $date = new static(0);
628
        $date->copyProperties($this);
629
        $date->step = $this->step;
630
 
631
        return $date;
632
    }
633
 
634
    /**
635
     * Get a copy of the instance.
636
     *
637
     * @return static
638
     */
639
    public function clone()
640
    {
641
        return $this->copy();
642
    }
643
 
644
    /**
645
     * Provide static helpers to create instances.  Allows CarbonInterval::years(3).
646
     *
647
     * Note: This is done using the magic method to allow static and instance methods to
648
     *       have the same names.
649
     *
650
     * @param string $method     magic method name called
651
     * @param array  $parameters parameters list
652
     *
653
     * @return static|null
654
     */
655
    public static function __callStatic($method, $parameters)
656
    {
657
        try {
658
            $interval = new static(0);
659
            $localStrictModeEnabled = $interval->localStrictModeEnabled;
660
            $interval->localStrictModeEnabled = true;
661
 
662
            $result = static::hasMacro($method)
663
                ? static::bindMacroContext(null, function () use (&$method, &$parameters, &$interval) {
664
                    return $interval->callMacro($method, $parameters);
665
                })
666
                : $interval->$method(...$parameters);
667
 
668
            $interval->localStrictModeEnabled = $localStrictModeEnabled;
669
 
670
            return $result;
671
        } catch (BadFluentSetterException $exception) {
672
            if (Carbon::isStrictModeEnabled()) {
673
                throw new BadFluentConstructorException($method, 0, $exception);
674
            }
675
 
676
            return null;
677
        }
678
    }
679
 
680
    /**
681
     * Return the current context from inside a macro callee or a new one if static.
682
     *
683
     * @return static
684
     */
685
    protected static function this()
686
    {
687
        return end(static::$macroContextStack) ?: new static(0);
688
    }
689
 
690
    /**
691
     * Creates a CarbonInterval from string.
692
     *
693
     * Format:
694
     *
695
     * Suffix | Unit    | Example | DateInterval expression
696
     * -------|---------|---------|------------------------
697
     * y      | years   |   1y    | P1Y
698
     * mo     | months  |   3mo   | P3M
699
     * w      | weeks   |   2w    | P2W
700
     * d      | days    |  28d    | P28D
701
     * h      | hours   |   4h    | PT4H
702
     * m      | minutes |  12m    | PT12M
703
     * s      | seconds |  59s    | PT59S
704
     *
705
     * e. g. `1w 3d 4h 32m 23s` is converted to 10 days 4 hours 32 minutes and 23 seconds.
706
     *
707
     * Special cases:
708
     *  - An empty string will return a zero interval
709
     *  - Fractions are allowed for weeks, days, hours and minutes and will be converted
710
     *    and rounded to the next smaller value (caution: 0.5w = 4d)
711
     *
712
     * @param string $intervalDefinition
713
     *
714
     * @return static
715
     */
716
    public static function fromString($intervalDefinition)
717
    {
718
        if (empty($intervalDefinition)) {
719
            return new static(0);
720
        }
721
 
722
        $years = 0;
723
        $months = 0;
724
        $weeks = 0;
725
        $days = 0;
726
        $hours = 0;
727
        $minutes = 0;
728
        $seconds = 0;
729
        $milliseconds = 0;
730
        $microseconds = 0;
731
 
732
        $pattern = '/(\d+(?:\.\d+)?)\h*([^\d\h]*)/i';
733
        preg_match_all($pattern, $intervalDefinition, $parts, PREG_SET_ORDER);
734
 
735
        while ([$part, $value, $unit] = array_shift($parts)) {
736
            $intValue = (int) $value;
737
            $fraction = (float) $value - $intValue;
738
 
739
            // Fix calculation precision
740
            switch (round($fraction, 6)) {
741
                case 1:
742
                    $fraction = 0;
743
                    $intValue++;
744
 
745
                    break;
746
                case 0:
747
                    $fraction = 0;
748
 
749
                    break;
750
            }
751
 
752
            switch ($unit === 'µs' ? 'µs' : strtolower($unit)) {
753
                case 'millennia':
754
                case 'millennium':
755
                    $years += $intValue * CarbonInterface::YEARS_PER_MILLENNIUM;
756
 
757
                    break;
758
 
759
                case 'century':
760
                case 'centuries':
761
                    $years += $intValue * CarbonInterface::YEARS_PER_CENTURY;
762
 
763
                    break;
764
 
765
                case 'decade':
766
                case 'decades':
767
                    $years += $intValue * CarbonInterface::YEARS_PER_DECADE;
768
 
769
                    break;
770
 
771
                case 'year':
772
                case 'years':
773
                case 'y':
774
                case 'yr':
775
                case 'yrs':
776
                    $years += $intValue;
777
 
778
                    break;
779
 
780
                case 'quarter':
781
                case 'quarters':
782
                    $months += $intValue * CarbonInterface::MONTHS_PER_QUARTER;
783
 
784
                    break;
785
 
786
                case 'month':
787
                case 'months':
788
                case 'mo':
789
                case 'mos':
790
                    $months += $intValue;
791
 
792
                    break;
793
 
794
                case 'week':
795
                case 'weeks':
796
                case 'w':
797
                    $weeks += $intValue;
798
 
799
                    if ($fraction) {
800
                        $parts[] = [null, $fraction * static::getDaysPerWeek(), 'd'];
801
                    }
802
 
803
                    break;
804
 
805
                case 'day':
806
                case 'days':
807
                case 'd':
808
                    $days += $intValue;
809
 
810
                    if ($fraction) {
811
                        $parts[] = [null, $fraction * static::getHoursPerDay(), 'h'];
812
                    }
813
 
814
                    break;
815
 
816
                case 'hour':
817
                case 'hours':
818
                case 'h':
819
                    $hours += $intValue;
820
 
821
                    if ($fraction) {
822
                        $parts[] = [null, $fraction * static::getMinutesPerHour(), 'm'];
823
                    }
824
 
825
                    break;
826
 
827
                case 'minute':
828
                case 'minutes':
829
                case 'm':
830
                    $minutes += $intValue;
831
 
832
                    if ($fraction) {
833
                        $parts[] = [null, $fraction * static::getSecondsPerMinute(), 's'];
834
                    }
835
 
836
                    break;
837
 
838
                case 'second':
839
                case 'seconds':
840
                case 's':
841
                    $seconds += $intValue;
842
 
843
                    if ($fraction) {
844
                        $parts[] = [null, $fraction * static::getMillisecondsPerSecond(), 'ms'];
845
                    }
846
 
847
                    break;
848
 
849
                case 'millisecond':
850
                case 'milliseconds':
851
                case 'milli':
852
                case 'ms':
853
                    $milliseconds += $intValue;
854
 
855
                    if ($fraction) {
856
                        $microseconds += round($fraction * static::getMicrosecondsPerMillisecond());
857
                    }
858
 
859
                    break;
860
 
861
                case 'microsecond':
862
                case 'microseconds':
863
                case 'micro':
864
                case 'µs':
865
                    $microseconds += $intValue;
866
 
867
                    break;
868
 
869
                default:
870
                    throw new InvalidIntervalException(
871
                        sprintf('Invalid part %s in definition %s', $part, $intervalDefinition)
872
                    );
873
            }
874
        }
875
 
876
        return new static($years, $months, $weeks, $days, $hours, $minutes, $seconds, $milliseconds * Carbon::MICROSECONDS_PER_MILLISECOND + $microseconds);
877
    }
878
 
879
    /**
880
     * Creates a CarbonInterval from string using a different locale.
881
     *
882
     * @param string      $interval interval string in the given language (may also contain English).
883
     * @param string|null $locale   if locale is null or not specified, current global locale will be used instead.
884
     *
885
     * @return static
886
     */
887
    public static function parseFromLocale($interval, $locale = null)
888
    {
889
        return static::fromString(Carbon::translateTimeString($interval, $locale ?: static::getLocale(), 'en'));
890
    }
891
 
892
    private static function castIntervalToClass(DateInterval $interval, string $className, array $skip = [])
893
    {
894
        $mainClass = DateInterval::class;
895
 
896
        if (!is_a($className, $mainClass, true)) {
897
            throw new InvalidCastException("$className is not a sub-class of $mainClass.");
898
        }
899
 
900
        $microseconds = $interval->f;
901
        $instance = new $className(static::getDateIntervalSpec($interval, false, $skip));
902
 
903
        if ($microseconds) {
904
            $instance->f = $microseconds;
905
        }
906
 
907
        if ($interval instanceof self && is_a($className, self::class, true)) {
908
            self::copyStep($interval, $instance);
909
        }
910
 
911
        self::copyNegativeUnits($interval, $instance);
912
 
913
        return $instance;
914
    }
915
 
916
    private static function copyNegativeUnits(DateInterval $from, DateInterval $to): void
917
    {
918
        $to->invert = $from->invert;
919
 
920
        foreach (['y', 'm', 'd', 'h', 'i', 's'] as $unit) {
921
            if ($from->$unit < 0) {
922
                $to->$unit *= -1;
923
            }
924
        }
925
    }
926
 
927
    private static function copyStep(self $from, self $to): void
928
    {
929
        $to->setStep($from->getStep());
930
    }
931
 
932
    /**
933
     * Cast the current instance into the given class.
934
     *
935
     * @param string $className The $className::instance() method will be called to cast the current object.
936
     *
937
     * @return DateInterval
938
     */
939
    public function cast(string $className)
940
    {
941
        return self::castIntervalToClass($this, $className);
942
    }
943
 
944
    /**
945
     * Create a CarbonInterval instance from a DateInterval one.  Can not instance
946
     * DateInterval objects created from DateTime::diff() as you can't externally
947
     * set the $days field.
948
     *
949
     * @param DateInterval $interval
950
     *
951
     * @return static
952
     */
953
    public static function instance(DateInterval $interval, array $skip = [])
954
    {
955
        return self::castIntervalToClass($interval, static::class, $skip);
956
    }
957
 
958
    /**
959
     * Make a CarbonInterval instance from given variable if possible.
960
     *
961
     * Always return a new instance. Parse only strings and only these likely to be intervals (skip dates
962
     * and recurrences). Throw an exception for invalid format, but otherwise return null.
963
     *
964
     * @param mixed|int|DateInterval|string|Closure|null $interval interval or number of the given $unit
965
     * @param string|null                                $unit     if specified, $interval must be an integer
966
     *
967
     * @return static|null
968
     */
969
    public static function make($interval, $unit = null)
970
    {
971
        if ($unit) {
972
            $interval = "$interval ".Carbon::pluralUnit($unit);
973
        }
974
 
975
        if ($interval instanceof DateInterval) {
976
            return static::instance($interval);
977
        }
978
 
979
        if ($interval instanceof Closure) {
980
            return new static($interval);
981
        }
982
 
983
        if (!\is_string($interval)) {
984
            return null;
985
        }
986
 
987
        return static::makeFromString($interval);
988
    }
989
 
990
    protected static function makeFromString(string $interval)
991
    {
992
        $interval = preg_replace('/\s+/', ' ', trim($interval));
993
 
994
        if (preg_match('/^P[T\d]/', $interval)) {
995
            return new static($interval);
996
        }
997
 
998
        if (preg_match('/^(?:\h*\d+(?:\.\d+)?\h*[a-z]+)+$/i', $interval)) {
999
            return static::fromString($interval);
1000
        }
1001
 
1002
        /** @var static $interval */
1003
        $interval = static::createFromDateString($interval);
1004
 
1005
        return !$interval || $interval->isEmpty() ? null : $interval;
1006
    }
1007
 
1008
    protected function resolveInterval($interval)
1009
    {
1010
        if (!($interval instanceof self)) {
1011
            return self::make($interval);
1012
        }
1013
 
1014
        return $interval;
1015
    }
1016
 
1017
    /**
1018
     * Sets up a DateInterval from the relative parts of the string.
1019
     *
1020
     * @param string $time
1021
     *
1022
     * @return static
1023
     *
1024
     * @link https://php.net/manual/en/dateinterval.createfromdatestring.php
1025
     */
1026
    #[ReturnTypeWillChange]
1027
    public static function createFromDateString($time)
1028
    {
1029
        $interval = @parent::createFromDateString(strtr($time, [
1030
            ',' => ' ',
1031
            ' and ' => ' ',
1032
        ]));
1033
 
1034
        if ($interval instanceof DateInterval) {
1035
            $interval = static::instance($interval);
1036
        }
1037
 
1038
        return $interval;
1039
    }
1040
 
1041
    ///////////////////////////////////////////////////////////////////
1042
    ///////////////////////// GETTERS AND SETTERS /////////////////////
1043
    ///////////////////////////////////////////////////////////////////
1044
 
1045
    /**
1046
     * Get a part of the CarbonInterval object.
1047
     *
1048
     * @param string $name
1049
     *
1050
     * @throws UnknownGetterException
1051
     *
1052
     * @return int|float|string
1053
     */
1054
    public function get($name)
1055
    {
1056
        if (str_starts_with($name, 'total')) {
1057
            return $this->total(substr($name, 5));
1058
        }
1059
 
1060
        switch ($name) {
1061
            case 'years':
1062
                return $this->y;
1063
 
1064
            case 'months':
1065
                return $this->m;
1066
 
1067
            case 'dayz':
1068
                return $this->d;
1069
 
1070
            case 'hours':
1071
                return $this->h;
1072
 
1073
            case 'minutes':
1074
                return $this->i;
1075
 
1076
            case 'seconds':
1077
                return $this->s;
1078
 
1079
            case 'milli':
1080
            case 'milliseconds':
1081
                return (int) (round($this->f * Carbon::MICROSECONDS_PER_SECOND) / Carbon::MICROSECONDS_PER_MILLISECOND);
1082
 
1083
            case 'micro':
1084
            case 'microseconds':
1085
                return (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND);
1086
 
1087
            case 'microExcludeMilli':
1088
                return (int) round($this->f * Carbon::MICROSECONDS_PER_SECOND) % Carbon::MICROSECONDS_PER_MILLISECOND;
1089
 
1090
            case 'weeks':
1091
                return (int) ($this->d / (int) static::getDaysPerWeek());
1092
 
1093
            case 'daysExcludeWeeks':
1094
            case 'dayzExcludeWeeks':
1095
                return $this->d % (int) static::getDaysPerWeek();
1096
 
1097
            case 'locale':
1098
                return $this->getTranslatorLocale();
1099
 
1100
            default:
1101
                throw new UnknownGetterException($name);
1102
        }
1103
    }
1104
 
1105
    /**
1106
     * Get a part of the CarbonInterval object.
1107
     *
1108
     * @param string $name
1109
     *
1110
     * @throws UnknownGetterException
1111
     *
1112
     * @return int|float|string
1113
     */
1114
    public function __get($name)
1115
    {
1116
        return $this->get($name);
1117
    }
1118
 
1119
    /**
1120
     * Set a part of the CarbonInterval object.
1121
     *
1122
     * @param string|array $name
1123
     * @param int          $value
1124
     *
1125
     * @throws UnknownSetterException
1126
     *
1127
     * @return $this
1128
     */
1129
    public function set($name, $value = null)
1130
    {
1131
        $properties = \is_array($name) ? $name : [$name => $value];
1132
 
1133
        foreach ($properties as $key => $value) {
1134
            switch (Carbon::singularUnit(rtrim($key, 'z'))) {
1135
                case 'year':
1136
                    $this->y = $value;
1137
 
1138
                    break;
1139
 
1140
                case 'month':
1141
                    $this->m = $value;
1142
 
1143
                    break;
1144
 
1145
                case 'week':
1146
                    $this->d = $value * (int) static::getDaysPerWeek();
1147
 
1148
                    break;
1149
 
1150
                case 'day':
1151
                    $this->d = $value;
1152
 
1153
                    break;
1154
 
1155
                case 'daysexcludeweek':
1156
                case 'dayzexcludeweek':
1157
                    $this->d = $this->weeks * (int) static::getDaysPerWeek() + $value;
1158
 
1159
                    break;
1160
 
1161
                case 'hour':
1162
                    $this->h = $value;
1163
 
1164
                    break;
1165
 
1166
                case 'minute':
1167
                    $this->i = $value;
1168
 
1169
                    break;
1170
 
1171
                case 'second':
1172
                    $this->s = $value;
1173
 
1174
                    break;
1175
 
1176
                case 'milli':
1177
                case 'millisecond':
1178
                    $this->microseconds = $value * Carbon::MICROSECONDS_PER_MILLISECOND + $this->microseconds % Carbon::MICROSECONDS_PER_MILLISECOND;
1179
 
1180
                    break;
1181
 
1182
                case 'micro':
1183
                case 'microsecond':
1184
                    $this->f = $value / Carbon::MICROSECONDS_PER_SECOND;
1185
 
1186
                    break;
1187
 
1188
                default:
1189
                    if ($this->localStrictModeEnabled ?? Carbon::isStrictModeEnabled()) {
1190
                        throw new UnknownSetterException($key);
1191
                    }
1192
 
1193
                    $this->$key = $value;
1194
            }
1195
        }
1196
 
1197
        return $this;
1198
    }
1199
 
1200
    /**
1201
     * Set a part of the CarbonInterval object.
1202
     *
1203
     * @param string $name
1204
     * @param int    $value
1205
     *
1206
     * @throws UnknownSetterException
1207
     */
1208
    public function __set($name, $value)
1209
    {
1210
        $this->set($name, $value);
1211
    }
1212
 
1213
    /**
1214
     * Allow setting of weeks and days to be cumulative.
1215
     *
1216
     * @param int $weeks Number of weeks to set
1217
     * @param int $days  Number of days to set
1218
     *
1219
     * @return static
1220
     */
1221
    public function weeksAndDays($weeks, $days)
1222
    {
1223
        $this->dayz = ($weeks * static::getDaysPerWeek()) + $days;
1224
 
1225
        return $this;
1226
    }
1227
 
1228
    /**
1229
     * Returns true if the interval is empty for each unit.
1230
     *
1231
     * @return bool
1232
     */
1233
    public function isEmpty()
1234
    {
1235
        return $this->years === 0 &&
1236
            $this->months === 0 &&
1237
            $this->dayz === 0 &&
1238
            !$this->days &&
1239
            $this->hours === 0 &&
1240
            $this->minutes === 0 &&
1241
            $this->seconds === 0 &&
1242
            $this->microseconds === 0;
1243
    }
1244
 
1245
    /**
1246
     * Register a custom macro.
1247
     *
1248
     * @example
1249
     * ```
1250
     * CarbonInterval::macro('twice', function () {
1251
     *   return $this->times(2);
1252
     * });
1253
     * echo CarbonInterval::hours(2)->twice();
1254
     * ```
1255
     *
1256
     * @param string          $name
1257
     * @param object|callable $macro
1258
     *
1259
     * @return void
1260
     */
1261
    public static function macro($name, $macro)
1262
    {
1263
        static::$macros[$name] = $macro;
1264
    }
1265
 
1266
    /**
1267
     * Register macros from a mixin object.
1268
     *
1269
     * @example
1270
     * ```
1271
     * CarbonInterval::mixin(new class {
1272
     *   public function daysToHours() {
1273
     *     return function () {
1274
     *       $this->hours += $this->days;
1275
     *       $this->days = 0;
1276
     *
1277
     *       return $this;
1278
     *     };
1279
     *   }
1280
     *   public function hoursToDays() {
1281
     *     return function () {
1282
     *       $this->days += $this->hours;
1283
     *       $this->hours = 0;
1284
     *
1285
     *       return $this;
1286
     *     };
1287
     *   }
1288
     * });
1289
     * echo CarbonInterval::hours(5)->hoursToDays() . "\n";
1290
     * echo CarbonInterval::days(5)->daysToHours() . "\n";
1291
     * ```
1292
     *
1293
     * @param object|string $mixin
1294
     *
1295
     * @throws ReflectionException
1296
     *
1297
     * @return void
1298
     */
1299
    public static function mixin($mixin)
1300
    {
1301
        static::baseMixin($mixin);
1302
    }
1303
 
1304
    /**
1305
     * Check if macro is registered.
1306
     *
1307
     * @param string $name
1308
     *
1309
     * @return bool
1310
     */
1311
    public static function hasMacro($name)
1312
    {
1313
        return isset(static::$macros[$name]);
1314
    }
1315
 
1316
    /**
1317
     * Call given macro.
1318
     *
1319
     * @param string $name
1320
     * @param array  $parameters
1321
     *
1322
     * @return mixed
1323
     */
1324
    protected function callMacro($name, $parameters)
1325
    {
1326
        $macro = static::$macros[$name];
1327
 
1328
        if ($macro instanceof Closure) {
1329
            $boundMacro = @$macro->bindTo($this, static::class) ?: @$macro->bindTo(null, static::class);
1330
 
1331
            return ($boundMacro ?: $macro)(...$parameters);
1332
        }
1333
 
1334
        return $macro(...$parameters);
1335
    }
1336
 
1337
    /**
1338
     * Allow fluent calls on the setters... CarbonInterval::years(3)->months(5)->day().
1339
     *
1340
     * Note: This is done using the magic method to allow static and instance methods to
1341
     *       have the same names.
1342
     *
1343
     * @param string $method     magic method name called
1344
     * @param array  $parameters parameters list
1345
     *
1346
     * @throws BadFluentSetterException|Throwable
1347
     *
1348
     * @return static
1349
     */
1350
    public function __call($method, $parameters)
1351
    {
1352
        if (static::hasMacro($method)) {
1353
            return static::bindMacroContext($this, function () use (&$method, &$parameters) {
1354
                return $this->callMacro($method, $parameters);
1355
            });
1356
        }
1357
 
1358
        $roundedValue = $this->callRoundMethod($method, $parameters);
1359
 
1360
        if ($roundedValue !== null) {
1361
            return $roundedValue;
1362
        }
1363
 
1364
        if (preg_match('/^(?<method>add|sub)(?<unit>[A-Z].*)$/', $method, $match)) {
1365
            $value = $this->getMagicParameter($parameters, 0, Carbon::pluralUnit($match['unit']), 0);
1366
 
1367
            return $this->{$match['method']}($value, $match['unit']);
1368
        }
1369
 
1370
        $value = $this->getMagicParameter($parameters, 0, Carbon::pluralUnit($method), 1);
1371
 
1372
        try {
1373
            $this->set($method, $value);
1374
        } catch (UnknownSetterException $exception) {
1375
            if ($this->localStrictModeEnabled ?? Carbon::isStrictModeEnabled()) {
1376
                throw new BadFluentSetterException($method, 0, $exception);
1377
            }
1378
        }
1379
 
1380
        return $this;
1381
    }
1382
 
1383
    protected function getForHumansInitialVariables($syntax, $short)
1384
    {
1385
        if (\is_array($syntax)) {
1386
            return $syntax;
1387
        }
1388
 
1389
        if (\is_int($short)) {
1390
            return [
1391
                'parts' => $short,
1392
                'short' => false,
1393
            ];
1394
        }
1395
 
1396
        if (\is_bool($syntax)) {
1397
            return [
1398
                'short' => $syntax,
1399
                'syntax' => CarbonInterface::DIFF_ABSOLUTE,
1400
            ];
1401
        }
1402
 
1403
        return [];
1404
    }
1405
 
1406
    /**
1407
     * @param mixed $syntax
1408
     * @param mixed $short
1409
     * @param mixed $parts
1410
     * @param mixed $options
1411
     *
1412
     * @return array
1413
     */
1414
    protected function getForHumansParameters($syntax = null, $short = false, $parts = -1, $options = null)
1415
    {
1416
        $optionalSpace = ' ';
1417
        $default = $this->getTranslationMessage('list.0') ?? $this->getTranslationMessage('list') ?? ' ';
1418
        $join = $default === '' ? '' : ' ';
1419
        $altNumbers = false;
1420
        $aUnit = false;
1421
        $minimumUnit = 's';
1422
        $skip = [];
1423
        extract($this->getForHumansInitialVariables($syntax, $short));
1424
        $skip = array_map('strtolower', array_filter((array) $skip, static function ($value) {
1425
            return \is_string($value) && $value !== '';
1426
        }));
1427
 
1428
        if ($syntax === null) {
1429
            $syntax = CarbonInterface::DIFF_ABSOLUTE;
1430
        }
1431
 
1432
        if ($parts === -1) {
1433
            $parts = INF;
1434
        }
1435
 
1436
        if ($options === null) {
1437
            $options = static::getHumanDiffOptions();
1438
        }
1439
 
1440
        if ($join === false) {
1441
            $join = ' ';
1442
        } elseif ($join === true) {
1443
            $join = [
1444
                $default,
1445
                $this->getTranslationMessage('list.1') ?? $default,
1446
            ];
1447
        }
1448
 
1449
        if ($altNumbers && $altNumbers !== true) {
1450
            $language = new Language($this->locale);
1451
            $altNumbers = \in_array($language->getCode(), (array) $altNumbers, true);
1452
        }
1453
 
1454
        if (\is_array($join)) {
1455
            [$default, $last] = $join;
1456
 
1457
            if ($default !== ' ') {
1458
                $optionalSpace = '';
1459
            }
1460
 
1461
            $join = function ($list) use ($default, $last) {
1462
                if (\count($list) < 2) {
1463
                    return implode('', $list);
1464
                }
1465
 
1466
                $end = array_pop($list);
1467
 
1468
                return implode($default, $list).$last.$end;
1469
            };
1470
        }
1471
 
1472
        if (\is_string($join)) {
1473
            if ($join !== ' ') {
1474
                $optionalSpace = '';
1475
            }
1476
 
1477
            $glue = $join;
1478
            $join = function ($list) use ($glue) {
1479
                return implode($glue, $list);
1480
            };
1481
        }
1482
 
1483
        $interpolations = [
1484
            ':optional-space' => $optionalSpace,
1485
        ];
1486
 
1487
        return [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip];
1488
    }
1489
 
1490
    protected static function getRoundingMethodFromOptions(int $options): ?string
1491
    {
1492
        if ($options & CarbonInterface::ROUND) {
1493
            return 'round';
1494
        }
1495
 
1496
        if ($options & CarbonInterface::CEIL) {
1497
            return 'ceil';
1498
        }
1499
 
1500
        if ($options & CarbonInterface::FLOOR) {
1501
            return 'floor';
1502
        }
1503
 
1504
        return null;
1505
    }
1506
 
1507
    /**
1508
     * Returns interval values as an array where key are the unit names and values the counts.
1509
     *
1510
     * @return int[]
1511
     */
1512
    public function toArray()
1513
    {
1514
        return [
1515
            'years' => $this->years,
1516
            'months' => $this->months,
1517
            'weeks' => $this->weeks,
1518
            'days' => $this->daysExcludeWeeks,
1519
            'hours' => $this->hours,
1520
            'minutes' => $this->minutes,
1521
            'seconds' => $this->seconds,
1522
            'microseconds' => $this->microseconds,
1523
        ];
1524
    }
1525
 
1526
    /**
1527
     * Returns interval non-zero values as an array where key are the unit names and values the counts.
1528
     *
1529
     * @return int[]
1530
     */
1531
    public function getNonZeroValues()
1532
    {
1533
        return array_filter($this->toArray(), 'intval');
1534
    }
1535
 
1536
    /**
1537
     * Returns interval values as an array where key are the unit names and values the counts
1538
     * from the biggest non-zero one the the smallest non-zero one.
1539
     *
1540
     * @return int[]
1541
     */
1542
    public function getValuesSequence()
1543
    {
1544
        $nonZeroValues = $this->getNonZeroValues();
1545
 
1546
        if ($nonZeroValues === []) {
1547
            return [];
1548
        }
1549
 
1550
        $keys = array_keys($nonZeroValues);
1551
        $firstKey = $keys[0];
1552
        $lastKey = $keys[\count($keys) - 1];
1553
        $values = [];
1554
        $record = false;
1555
 
1556
        foreach ($this->toArray() as $unit => $count) {
1557
            if ($unit === $firstKey) {
1558
                $record = true;
1559
            }
1560
 
1561
            if ($record) {
1562
                $values[$unit] = $count;
1563
            }
1564
 
1565
            if ($unit === $lastKey) {
1566
                $record = false;
1567
            }
1568
        }
1569
 
1570
        return $values;
1571
    }
1572
 
1573
    /**
1574
     * Get the current interval in a human readable format in the current locale.
1575
     *
1576
     * @example
1577
     * ```
1578
     * echo CarbonInterval::fromString('4d 3h 40m')->forHumans() . "\n";
1579
     * echo CarbonInterval::fromString('4d 3h 40m')->forHumans(['parts' => 2]) . "\n";
1580
     * echo CarbonInterval::fromString('4d 3h 40m')->forHumans(['parts' => 3, 'join' => true]) . "\n";
1581
     * echo CarbonInterval::fromString('4d 3h 40m')->forHumans(['short' => true]) . "\n";
1582
     * echo CarbonInterval::fromString('1d 24h')->forHumans(['join' => ' or ']) . "\n";
1583
     * echo CarbonInterval::fromString('1d 24h')->forHumans(['minimumUnit' => 'hour']) . "\n";
1584
     * ```
1585
     *
1586
     * @param int|array $syntax  if array passed, parameters will be extracted from it, the array may contains:
1587
     *                           - 'syntax' entry (see below)
1588
     *                           - 'short' entry (see below)
1589
     *                           - 'parts' entry (see below)
1590
     *                           - 'options' entry (see below)
1591
     *                           - 'skip' entry, list of units to skip (array of strings or a single string,
1592
     *                           ` it can be the unit name (singular or plural) or its shortcut
1593
     *                           ` (y, m, w, d, h, min, s, ms, µs).
1594
     *                           - 'aUnit' entry, prefer "an hour" over "1 hour" if true
1595
     *                           - 'join' entry determines how to join multiple parts of the string
1596
     *                           `  - if $join is a string, it's used as a joiner glue
1597
     *                           `  - if $join is a callable/closure, it get the list of string and should return a string
1598
     *                           `  - if $join is an array, the first item will be the default glue, and the second item
1599
     *                           `    will be used instead of the glue for the last item
1600
     *                           `  - if $join is true, it will be guessed from the locale ('list' translation file entry)
1601
     *                           `  - if $join is missing, a space will be used as glue
1602
     *                           - 'minimumUnit' entry determines the smallest unit of time to display can be long or
1603
     *                           `  short form of the units, e.g. 'hour' or 'h' (default value: s)
1604
     *                           if int passed, it add modifiers:
1605
     *                           Possible values:
1606
     *                           - CarbonInterface::DIFF_ABSOLUTE          no modifiers
1607
     *                           - CarbonInterface::DIFF_RELATIVE_TO_NOW   add ago/from now modifier
1608
     *                           - CarbonInterface::DIFF_RELATIVE_TO_OTHER add before/after modifier
1609
     *                           Default value: CarbonInterface::DIFF_ABSOLUTE
1610
     * @param bool      $short   displays short format of time units
1611
     * @param int       $parts   maximum number of parts to display (default value: -1: no limits)
1612
     * @param int       $options human diff options
1613
     *
1614
     * @throws Exception
1615
     *
1616
     * @return string
1617
     */
1618
    public function forHumans($syntax = null, $short = false, $parts = -1, $options = null)
1619
    {
1620
        [$syntax, $short, $parts, $options, $join, $aUnit, $altNumbers, $interpolations, $minimumUnit, $skip] = $this
1621
            ->getForHumansParameters($syntax, $short, $parts, $options);
1622
 
1623
        $interval = [];
1624
 
1625
        $syntax = (int) ($syntax ?? CarbonInterface::DIFF_ABSOLUTE);
1626
        $absolute = $syntax === CarbonInterface::DIFF_ABSOLUTE;
1627
        $relativeToNow = $syntax === CarbonInterface::DIFF_RELATIVE_TO_NOW;
1628
        $count = 1;
1629
        $unit = $short ? 's' : 'second';
1630
        $isFuture = $this->invert === 1;
1631
        $transId = $relativeToNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
1632
        $declensionMode = null;
1633
 
1634
        /** @var \Symfony\Component\Translation\Translator $translator */
1635
        $translator = $this->getLocalTranslator();
1636
 
1637
        $handleDeclensions = function ($unit, $count, $index = 0, $parts = 1) use ($interpolations, $transId, $translator, $altNumbers, $absolute, &$declensionMode) {
1638
            if (!$absolute) {
1639
                $declensionMode = $declensionMode ?? $this->translate($transId.'_mode');
1640
 
1641
                if ($this->needsDeclension($declensionMode, $index, $parts)) {
1642
                    // Some languages have special pluralization for past and future tense.
1643
                    $key = $unit.'_'.$transId;
1644
                    $result = $this->translate($key, $interpolations, $count, $translator, $altNumbers);
1645
 
1646
                    if ($result !== $key) {
1647
                        return $result;
1648
                    }
1649
                }
1650
            }
1651
 
1652
            $result = $this->translate($unit, $interpolations, $count, $translator, $altNumbers);
1653
 
1654
            if ($result !== $unit) {
1655
                return $result;
1656
            }
1657
 
1658
            return null;
1659
        };
1660
 
1661
        $intervalValues = $this;
1662
        $method = static::getRoundingMethodFromOptions($options);
1663
 
1664
        if ($method) {
1665
            $previousCount = INF;
1666
 
1667
            while (
1668
                \count($intervalValues->getNonZeroValues()) > $parts &&
1669
                ($count = \count($keys = array_keys($intervalValues->getValuesSequence()))) > 1
1670
            ) {
1671
                $index = min($count, $previousCount - 1) - 2;
1672
 
1673
                if ($index < 0) {
1674
                    break;
1675
                }
1676
 
1677
                $intervalValues = $this->copy()->roundUnit(
1678
                    $keys[$index],
1679
                    1,
1680
                    $method
1681
                );
1682
                $previousCount = $count;
1683
            }
1684
        }
1685
 
1686
        $diffIntervalArray = [
1687
            ['value' => $intervalValues->years,             'unit' => 'year',        'unitShort' => 'y'],
1688
            ['value' => $intervalValues->months,            'unit' => 'month',       'unitShort' => 'm'],
1689
            ['value' => $intervalValues->weeks,             'unit' => 'week',        'unitShort' => 'w'],
1690
            ['value' => $intervalValues->daysExcludeWeeks,  'unit' => 'day',         'unitShort' => 'd'],
1691
            ['value' => $intervalValues->hours,             'unit' => 'hour',        'unitShort' => 'h'],
1692
            ['value' => $intervalValues->minutes,           'unit' => 'minute',      'unitShort' => 'min'],
1693
            ['value' => $intervalValues->seconds,           'unit' => 'second',      'unitShort' => 's'],
1694
            ['value' => $intervalValues->milliseconds,      'unit' => 'millisecond', 'unitShort' => 'ms'],
1695
            ['value' => $intervalValues->microExcludeMilli, 'unit' => 'microsecond', 'unitShort' => 'µs'],
1696
        ];
1697
 
1698
        if (!empty($skip)) {
1699
            foreach ($diffIntervalArray as $index => &$unitData) {
1700
                $nextIndex = $index + 1;
1701
 
1702
                if ($unitData['value'] &&
1703
                    isset($diffIntervalArray[$nextIndex]) &&
1704
                    \count(array_intersect([$unitData['unit'], $unitData['unit'].'s', $unitData['unitShort']], $skip))
1705
                ) {
1706
                    $diffIntervalArray[$nextIndex]['value'] += $unitData['value'] *
1707
                        self::getFactorWithDefault($diffIntervalArray[$nextIndex]['unit'], $unitData['unit']);
1708
                    $unitData['value'] = 0;
1709
                }
1710
            }
1711
        }
1712
 
1713
        $transChoice = function ($short, $unitData, $index, $parts) use ($absolute, $handleDeclensions, $translator, $aUnit, $altNumbers, $interpolations) {
1714
            $count = $unitData['value'];
1715
 
1716
            if ($short) {
1717
                $result = $handleDeclensions($unitData['unitShort'], $count, $index, $parts);
1718
 
1719
                if ($result !== null) {
1720
                    return $result;
1721
                }
1722
            } elseif ($aUnit) {
1723
                $result = $handleDeclensions('a_'.$unitData['unit'], $count, $index, $parts);
1724
 
1725
                if ($result !== null) {
1726
                    return $result;
1727
                }
1728
            }
1729
 
1730
            if (!$absolute) {
1731
                return $handleDeclensions($unitData['unit'], $count, $index, $parts);
1732
            }
1733
 
1734
            return $this->translate($unitData['unit'], $interpolations, $count, $translator, $altNumbers);
1735
        };
1736
 
1737
        $fallbackUnit = ['second', 's'];
1738
 
1739
        foreach ($diffIntervalArray as $diffIntervalData) {
1740
            if ($diffIntervalData['value'] > 0) {
1741
                $unit = $short ? $diffIntervalData['unitShort'] : $diffIntervalData['unit'];
1742
                $count = $diffIntervalData['value'];
1743
                $interval[] = [$short, $diffIntervalData];
1744
            } elseif ($options & CarbonInterface::SEQUENTIAL_PARTS_ONLY && \count($interval) > 0) {
1745
                break;
1746
            }
1747
 
1748
            // break the loop after we get the required number of parts in array
1749
            if (\count($interval) >= $parts) {
1750
                break;
1751
            }
1752
 
1753
            // break the loop after we have reached the minimum unit
1754
            if (\in_array($minimumUnit, [$diffIntervalData['unit'], $diffIntervalData['unitShort']], true)) {
1755
                $fallbackUnit = [$diffIntervalData['unit'], $diffIntervalData['unitShort']];
1756
 
1757
                break;
1758
            }
1759
        }
1760
 
1761
        $actualParts = \count($interval);
1762
 
1763
        foreach ($interval as $index => &$item) {
1764
            $item = $transChoice($item[0], $item[1], $index, $actualParts);
1765
        }
1766
 
1767
        if (\count($interval) === 0) {
1768
            if ($relativeToNow && $options & CarbonInterface::JUST_NOW) {
1769
                $key = 'diff_now';
1770
                $translation = $this->translate($key, $interpolations, null, $translator);
1771
 
1772
                if ($translation !== $key) {
1773
                    return $translation;
1774
                }
1775
            }
1776
 
1777
            $count = $options & CarbonInterface::NO_ZERO_DIFF ? 1 : 0;
1778
            $unit = $fallbackUnit[$short ? 1 : 0];
1779
            $interval[] = $this->translate($unit, $interpolations, $count, $translator, $altNumbers);
1780
        }
1781
 
1782
        // join the interval parts by a space
1783
        $time = $join($interval);
1784
 
1785
        unset($diffIntervalArray, $interval);
1786
 
1787
        if ($absolute) {
1788
            return $time;
1789
        }
1790
 
1791
        $isFuture = $this->invert === 1;
1792
 
1793
        $transId = $relativeToNow ? ($isFuture ? 'from_now' : 'ago') : ($isFuture ? 'after' : 'before');
1794
 
1795
        if ($parts === 1) {
1796
            if ($relativeToNow && $unit === 'day') {
1797
                if ($count === 1 && $options & CarbonInterface::ONE_DAY_WORDS) {
1798
                    $key = $isFuture ? 'diff_tomorrow' : 'diff_yesterday';
1799
                    $translation = $this->translate($key, $interpolations, null, $translator);
1800
 
1801
                    if ($translation !== $key) {
1802
                        return $translation;
1803
                    }
1804
                }
1805
 
1806
                if ($count === 2 && $options & CarbonInterface::TWO_DAY_WORDS) {
1807
                    $key = $isFuture ? 'diff_after_tomorrow' : 'diff_before_yesterday';
1808
                    $translation = $this->translate($key, $interpolations, null, $translator);
1809
 
1810
                    if ($translation !== $key) {
1811
                        return $translation;
1812
                    }
1813
                }
1814
            }
1815
 
1816
            $aTime = $aUnit ? $handleDeclensions('a_'.$unit, $count) : null;
1817
 
1818
            $time = $aTime ?: $handleDeclensions($unit, $count) ?: $time;
1819
        }
1820
 
1821
        $time = [':time' => $time];
1822
 
1823
        return $this->translate($transId, array_merge($time, $interpolations, $time), null, $translator);
1824
    }
1825
 
1826
    /**
1827
     * Format the instance as a string using the forHumans() function.
1828
     *
1829
     * @throws Exception
1830
     *
1831
     * @return string
1832
     */
1833
    public function __toString()
1834
    {
1835
        $format = $this->localToStringFormat ?? static::$toStringFormat;
1836
 
1837
        if (!$format) {
1838
            return $this->forHumans();
1839
        }
1840
 
1841
        if ($format instanceof Closure) {
1842
            return $format($this);
1843
        }
1844
 
1845
        return $this->format($format);
1846
    }
1847
 
1848
    /**
1849
     * Return native DateInterval PHP object matching the current instance.
1850
     *
1851
     * @example
1852
     * ```
1853
     * var_dump(CarbonInterval::hours(2)->toDateInterval());
1854
     * ```
1855
     *
1856
     * @return DateInterval
1857
     */
1858
    public function toDateInterval()
1859
    {
1860
        return self::castIntervalToClass($this, DateInterval::class);
1861
    }
1862
 
1863
    /**
1864
     * Convert the interval to a CarbonPeriod.
1865
     *
1866
     * @param DateTimeInterface|string|int ...$params Start date, [end date or recurrences] and optional settings.
1867
     *
1868
     * @return CarbonPeriod
1869
     */
1870
    public function toPeriod(...$params)
1871
    {
1872
        if ($this->tzName) {
1873
            $tz = \is_string($this->tzName) ? new DateTimeZone($this->tzName) : $this->tzName;
1874
 
1875
            if ($tz instanceof DateTimeZone) {
1876
                array_unshift($params, $tz);
1877
            }
1878
        }
1879
 
1880
        return CarbonPeriod::create($this, ...$params);
1881
    }
1882
 
1883
    /**
1884
     * Invert the interval.
1885
     *
1886
     * @param bool|int $inverted if a parameter is passed, the passed value casted as 1 or 0 is used
1887
     *                           as the new value of the ->invert property.
1888
     *
1889
     * @return $this
1890
     */
1891
    public function invert($inverted = null)
1892
    {
1893
        $this->invert = (\func_num_args() === 0 ? !$this->invert : $inverted) ? 1 : 0;
1894
 
1895
        return $this;
1896
    }
1897
 
1898
    protected function solveNegativeInterval()
1899
    {
1900
        if (!$this->isEmpty() && $this->years <= 0 && $this->months <= 0 && $this->dayz <= 0 && $this->hours <= 0 && $this->minutes <= 0 && $this->seconds <= 0 && $this->microseconds <= 0) {
1901
            $this->years *= -1;
1902
            $this->months *= -1;
1903
            $this->dayz *= -1;
1904
            $this->hours *= -1;
1905
            $this->minutes *= -1;
1906
            $this->seconds *= -1;
1907
            $this->microseconds *= -1;
1908
            $this->invert();
1909
        }
1910
 
1911
        return $this;
1912
    }
1913
 
1914
    /**
1915
     * Add the passed interval to the current instance.
1916
     *
1917
     * @param string|DateInterval $unit
1918
     * @param int|float           $value
1919
     *
1920
     * @return $this
1921
     */
1922
    public function add($unit, $value = 1)
1923
    {
1924
        if (is_numeric($unit)) {
1925
            [$value, $unit] = [$unit, $value];
1926
        }
1927
 
1928
        if (\is_string($unit) && !preg_match('/^\s*\d/', $unit)) {
1929
            $unit = "$value $unit";
1930
            $value = 1;
1931
        }
1932
 
1933
        $interval = static::make($unit);
1934
 
1935
        if (!$interval) {
1936
            throw new InvalidIntervalException('This type of data cannot be added/subtracted.');
1937
        }
1938
 
1939
        if ($value !== 1) {
1940
            $interval->times($value);
1941
        }
1942
 
1943
        $sign = ($this->invert === 1) !== ($interval->invert === 1) ? -1 : 1;
1944
        $this->years += $interval->y * $sign;
1945
        $this->months += $interval->m * $sign;
1946
        $this->dayz += ($interval->days === false ? $interval->d : $interval->days) * $sign;
1947
        $this->hours += $interval->h * $sign;
1948
        $this->minutes += $interval->i * $sign;
1949
        $this->seconds += $interval->s * $sign;
1950
        $this->microseconds += $interval->microseconds * $sign;
1951
 
1952
        $this->solveNegativeInterval();
1953
 
1954
        return $this;
1955
    }
1956
 
1957
    /**
1958
     * Subtract the passed interval to the current instance.
1959
     *
1960
     * @param string|DateInterval $unit
1961
     * @param int|float           $value
1962
     *
1963
     * @return $this
1964
     */
1965
    public function sub($unit, $value = 1)
1966
    {
1967
        if (is_numeric($unit)) {
1968
            [$value, $unit] = [$unit, $value];
1969
        }
1970
 
1971
        return $this->add($unit, -(float) $value);
1972
    }
1973
 
1974
    /**
1975
     * Subtract the passed interval to the current instance.
1976
     *
1977
     * @param string|DateInterval $unit
1978
     * @param int|float           $value
1979
     *
1980
     * @return $this
1981
     */
1982
    public function subtract($unit, $value = 1)
1983
    {
1984
        return $this->sub($unit, $value);
1985
    }
1986
 
1987
    /**
1988
     * Add given parameters to the current interval.
1989
     *
1990
     * @param int       $years
1991
     * @param int       $months
1992
     * @param int|float $weeks
1993
     * @param int|float $days
1994
     * @param int|float $hours
1995
     * @param int|float $minutes
1996
     * @param int|float $seconds
1997
     * @param int|float $microseconds
1998
     *
1999
     * @return $this
2000
     */
2001
    public function plus(
2002
        $years = 0,
2003
        $months = 0,
2004
        $weeks = 0,
2005
        $days = 0,
2006
        $hours = 0,
2007
        $minutes = 0,
2008
        $seconds = 0,
2009
        $microseconds = 0
2010
    ): self {
2011
        return $this->add("
2012
            $years years $months months $weeks weeks $days days
2013
            $hours hours $minutes minutes $seconds seconds $microseconds microseconds
2014
        ");
2015
    }
2016
 
2017
    /**
2018
     * Add given parameters to the current interval.
2019
     *
2020
     * @param int       $years
2021
     * @param int       $months
2022
     * @param int|float $weeks
2023
     * @param int|float $days
2024
     * @param int|float $hours
2025
     * @param int|float $minutes
2026
     * @param int|float $seconds
2027
     * @param int|float $microseconds
2028
     *
2029
     * @return $this
2030
     */
2031
    public function minus(
2032
        $years = 0,
2033
        $months = 0,
2034
        $weeks = 0,
2035
        $days = 0,
2036
        $hours = 0,
2037
        $minutes = 0,
2038
        $seconds = 0,
2039
        $microseconds = 0
2040
    ): self {
2041
        return $this->sub("
2042
            $years years $months months $weeks weeks $days days
2043
            $hours hours $minutes minutes $seconds seconds $microseconds microseconds
2044
        ");
2045
    }
2046
 
2047
    /**
2048
     * Multiply current instance given number of times. times() is naive, it multiplies each unit
2049
     * (so day can be greater than 31, hour can be greater than 23, etc.) and the result is rounded
2050
     * separately for each unit.
2051
     *
2052
     * Use times() when you want a fast and approximated calculation that does not cascade units.
2053
     *
2054
     * For a precise and cascaded calculation,
2055
     *
2056
     * @see multiply()
2057
     *
2058
     * @param float|int $factor
2059
     *
2060
     * @return $this
2061
     */
2062
    public function times($factor)
2063
    {
2064
        if ($factor < 0) {
2065
            $this->invert = $this->invert ? 0 : 1;
2066
            $factor = -$factor;
2067
        }
2068
 
2069
        $this->years = (int) round($this->years * $factor);
2070
        $this->months = (int) round($this->months * $factor);
2071
        $this->dayz = (int) round($this->dayz * $factor);
2072
        $this->hours = (int) round($this->hours * $factor);
2073
        $this->minutes = (int) round($this->minutes * $factor);
2074
        $this->seconds = (int) round($this->seconds * $factor);
2075
        $this->microseconds = (int) round($this->microseconds * $factor);
2076
 
2077
        return $this;
2078
    }
2079
 
2080
    /**
2081
     * Divide current instance by a given divider. shares() is naive, it divides each unit separately
2082
     * and the result is rounded for each unit. So 5 hours and 20 minutes shared by 3 becomes 2 hours
2083
     * and 7 minutes.
2084
     *
2085
     * Use shares() when you want a fast and approximated calculation that does not cascade units.
2086
     *
2087
     * For a precise and cascaded calculation,
2088
     *
2089
     * @see divide()
2090
     *
2091
     * @param float|int $divider
2092
     *
2093
     * @return $this
2094
     */
2095
    public function shares($divider)
2096
    {
2097
        return $this->times(1 / $divider);
2098
    }
2099
 
2100
    protected function copyProperties(self $interval, $ignoreSign = false)
2101
    {
2102
        $this->years = $interval->years;
2103
        $this->months = $interval->months;
2104
        $this->dayz = $interval->dayz;
2105
        $this->hours = $interval->hours;
2106
        $this->minutes = $interval->minutes;
2107
        $this->seconds = $interval->seconds;
2108
        $this->microseconds = $interval->microseconds;
2109
 
2110
        if (!$ignoreSign) {
2111
            $this->invert = $interval->invert;
2112
        }
2113
 
2114
        return $this;
2115
    }
2116
 
2117
    /**
2118
     * Multiply and cascade current instance by a given factor.
2119
     *
2120
     * @param float|int $factor
2121
     *
2122
     * @return $this
2123
     */
2124
    public function multiply($factor)
2125
    {
2126
        if ($factor < 0) {
2127
            $this->invert = $this->invert ? 0 : 1;
2128
            $factor = -$factor;
2129
        }
2130
 
2131
        $yearPart = (int) floor($this->years * $factor); // Split calculation to prevent imprecision
2132
 
2133
        if ($yearPart) {
2134
            $this->years -= $yearPart / $factor;
2135
        }
2136
 
2137
        return $this->copyProperties(
2138
            static::create($yearPart)
2139
                ->microseconds(abs($this->totalMicroseconds) * $factor)
2140
                ->cascade(),
2141
            true
2142
        );
2143
    }
2144
 
2145
    /**
2146
     * Divide and cascade current instance by a given divider.
2147
     *
2148
     * @param float|int $divider
2149
     *
2150
     * @return $this
2151
     */
2152
    public function divide($divider)
2153
    {
2154
        return $this->multiply(1 / $divider);
2155
    }
2156
 
2157
    /**
2158
     * Get the interval_spec string of a date interval.
2159
     *
2160
     * @param DateInterval $interval
2161
     *
2162
     * @return string
2163
     */
2164
    public static function getDateIntervalSpec(DateInterval $interval, bool $microseconds = false, array $skip = [])
2165
    {
2166
        $date = array_filter([
2167
            static::PERIOD_YEARS => abs($interval->y),
2168
            static::PERIOD_MONTHS => abs($interval->m),
2169
            static::PERIOD_DAYS => abs($interval->d),
2170
        ]);
2171
 
2172
        if (
2173
            $interval->days >= CarbonInterface::DAYS_PER_WEEK * CarbonInterface::WEEKS_PER_MONTH &&
2174
            (!isset($date[static::PERIOD_YEARS]) || \count(array_intersect(['y', 'year', 'years'], $skip))) &&
2175
            (!isset($date[static::PERIOD_MONTHS]) || \count(array_intersect(['m', 'month', 'months'], $skip)))
2176
        ) {
2177
            $date = [
2178
                static::PERIOD_DAYS => abs($interval->days),
2179
            ];
2180
        }
2181
 
2182
        $seconds = abs($interval->s);
2183
        if ($microseconds && $interval->f > 0) {
2184
            $seconds = sprintf('%d.%06d', $seconds, abs($interval->f) * 1000000);
2185
        }
2186
 
2187
        $time = array_filter([
2188
            static::PERIOD_HOURS => abs($interval->h),
2189
            static::PERIOD_MINUTES => abs($interval->i),
2190
            static::PERIOD_SECONDS => $seconds,
2191
        ]);
2192
 
2193
        $specString = static::PERIOD_PREFIX;
2194
 
2195
        foreach ($date as $key => $value) {
2196
            $specString .= $value.$key;
2197
        }
2198
 
2199
        if (\count($time) > 0) {
2200
            $specString .= static::PERIOD_TIME_PREFIX;
2201
            foreach ($time as $key => $value) {
2202
                $specString .= $value.$key;
2203
            }
2204
        }
2205
 
2206
        return $specString === static::PERIOD_PREFIX ? 'PT0S' : $specString;
2207
    }
2208
 
2209
    /**
2210
     * Get the interval_spec string.
2211
     *
2212
     * @return string
2213
     */
2214
    public function spec(bool $microseconds = false)
2215
    {
2216
        return static::getDateIntervalSpec($this, $microseconds);
2217
    }
2218
 
2219
    /**
2220
     * Comparing 2 date intervals.
2221
     *
2222
     * @param DateInterval $first
2223
     * @param DateInterval $second
2224
     *
2225
     * @return int
2226
     */
2227
    public static function compareDateIntervals(DateInterval $first, DateInterval $second)
2228
    {
2229
        $current = Carbon::now();
2230
        $passed = $current->avoidMutation()->add($second);
2231
        $current->add($first);
2232
 
2233
        if ($current < $passed) {
2234
            return -1;
2235
        }
2236
        if ($current > $passed) {
2237
            return 1;
2238
        }
2239
 
2240
        return 0;
2241
    }
2242
 
2243
    /**
2244
     * Comparing with passed interval.
2245
     *
2246
     * @param DateInterval $interval
2247
     *
2248
     * @return int
2249
     */
2250
    public function compare(DateInterval $interval)
2251
    {
2252
        return static::compareDateIntervals($this, $interval);
2253
    }
2254
 
2255
    private function invertCascade(array $values)
2256
    {
2257
        return $this->set(array_map(function ($value) {
2258
            return -$value;
2259
        }, $values))->doCascade(true)->invert();
2260
    }
2261
 
2262
    private function doCascade(bool $deep)
2263
    {
2264
        $originalData = $this->toArray();
2265
        $originalData['milliseconds'] = (int) ($originalData['microseconds'] / static::getMicrosecondsPerMillisecond());
2266
        $originalData['microseconds'] = $originalData['microseconds'] % static::getMicrosecondsPerMillisecond();
2267
        $originalData['weeks'] = (int) ($this->d / static::getDaysPerWeek());
2268
        $originalData['daysExcludeWeeks'] = fmod($this->d, static::getDaysPerWeek());
2269
        unset($originalData['days']);
2270
        $newData = $originalData;
2271
        $previous = [];
2272
 
2273
        foreach (self::getFlipCascadeFactors() as $source => [$target, $factor]) {
2274
            foreach (['source', 'target'] as $key) {
2275
                if ($$key === 'dayz') {
2276
                    $$key = 'daysExcludeWeeks';
2277
                }
2278
            }
2279
 
2280
            $value = $newData[$source];
2281
            $modulo = fmod($factor + fmod($value, $factor), $factor);
2282
            $newData[$source] = $modulo;
2283
            $newData[$target] += ($value - $modulo) / $factor;
2284
 
2285
            $decimalPart = fmod($newData[$source], 1);
2286
 
2287
            if ($decimalPart !== 0.0) {
2288
                $unit = $source;
2289
 
2290
                foreach ($previous as [$subUnit, $subFactor]) {
2291
                    $newData[$unit] -= $decimalPart;
2292
                    $newData[$subUnit] += $decimalPart * $subFactor;
2293
                    $decimalPart = fmod($newData[$subUnit], 1);
2294
 
2295
                    if ($decimalPart === 0.0) {
2296
                        break;
2297
                    }
2298
 
2299
                    $unit = $subUnit;
2300
                }
2301
            }
2302
 
2303
            array_unshift($previous, [$source, $factor]);
2304
        }
2305
 
2306
        $positive = null;
2307
 
2308
        if (!$deep) {
2309
            foreach ($newData as $value) {
2310
                if ($value) {
2311
                    if ($positive === null) {
2312
                        $positive = ($value > 0);
2313
 
2314
                        continue;
2315
                    }
2316
 
2317
                    if (($value > 0) !== $positive) {
2318
                        return $this->invertCascade($originalData)
2319
                            ->solveNegativeInterval();
2320
                    }
2321
                }
2322
            }
2323
        }
2324
 
2325
        return $this->set($newData)
2326
            ->solveNegativeInterval();
2327
    }
2328
 
2329
    /**
2330
     * Convert overflowed values into bigger units.
2331
     *
2332
     * @return $this
2333
     */
2334
    public function cascade()
2335
    {
2336
        return $this->doCascade(false);
2337
    }
2338
 
2339
    public function hasNegativeValues(): bool
2340
    {
2341
        foreach ($this->toArray() as $value) {
2342
            if ($value < 0) {
2343
                return true;
2344
            }
2345
        }
2346
 
2347
        return false;
2348
    }
2349
 
2350
    public function hasPositiveValues(): bool
2351
    {
2352
        foreach ($this->toArray() as $value) {
2353
            if ($value > 0) {
2354
                return true;
2355
            }
2356
        }
2357
 
2358
        return false;
2359
    }
2360
 
2361
    /**
2362
     * Get amount of given unit equivalent to the interval.
2363
     *
2364
     * @param string $unit
2365
     *
2366
     * @throws UnknownUnitException|UnitNotConfiguredException
2367
     *
2368
     * @return float
2369
     */
2370
    public function total($unit)
2371
    {
2372
        $realUnit = $unit = strtolower($unit);
2373
 
2374
        if (\in_array($unit, ['days', 'weeks'])) {
2375
            $realUnit = 'dayz';
2376
        } elseif (!\in_array($unit, ['microseconds', 'milliseconds', 'seconds', 'minutes', 'hours', 'dayz', 'months', 'years'])) {
2377
            throw new UnknownUnitException($unit);
2378
        }
2379
 
2380
        $result = 0;
2381
        $cumulativeFactor = 0;
2382
        $unitFound = false;
2383
        $factors = self::getFlipCascadeFactors();
2384
        $daysPerWeek = (int) static::getDaysPerWeek();
2385
 
2386
        $values = [
2387
            'years' => $this->years,
2388
            'months' => $this->months,
2389
            'weeks' => (int) ($this->d / $daysPerWeek),
2390
            'dayz' => fmod($this->d, $daysPerWeek),
2391
            'hours' => $this->hours,
2392
            'minutes' => $this->minutes,
2393
            'seconds' => $this->seconds,
2394
            'milliseconds' => (int) ($this->microseconds / Carbon::MICROSECONDS_PER_MILLISECOND),
2395
            'microseconds' => $this->microseconds % Carbon::MICROSECONDS_PER_MILLISECOND,
2396
        ];
2397
 
2398
        if (isset($factors['dayz']) && $factors['dayz'][0] !== 'weeks') {
2399
            $values['dayz'] += $values['weeks'] * $daysPerWeek;
2400
            $values['weeks'] = 0;
2401
        }
2402
 
2403
        foreach ($factors as $source => [$target, $factor]) {
2404
            if ($source === $realUnit) {
2405
                $unitFound = true;
2406
                $value = $values[$source];
2407
                $result += $value;
2408
                $cumulativeFactor = 1;
2409
            }
2410
 
2411
            if ($factor === false) {
2412
                if ($unitFound) {
2413
                    break;
2414
                }
2415
 
2416
                $result = 0;
2417
                $cumulativeFactor = 0;
2418
 
2419
                continue;
2420
            }
2421
 
2422
            if ($target === $realUnit) {
2423
                $unitFound = true;
2424
            }
2425
 
2426
            if ($cumulativeFactor) {
2427
                $cumulativeFactor *= $factor;
2428
                $result += $values[$target] * $cumulativeFactor;
2429
 
2430
                continue;
2431
            }
2432
 
2433
            $value = $values[$source];
2434
 
2435
            $result = ($result + $value) / $factor;
2436
        }
2437
 
2438
        if (isset($target) && !$cumulativeFactor) {
2439
            $result += $values[$target];
2440
        }
2441
 
2442
        if (!$unitFound) {
2443
            throw new UnitNotConfiguredException($unit);
2444
        }
2445
 
2446
        if ($this->invert) {
2447
            $result *= -1;
2448
        }
2449
 
2450
        if ($unit === 'weeks') {
2451
            $result /= $daysPerWeek;
2452
        }
2453
 
2454
        // Cast as int numbers with no decimal part
2455
        return fmod($result, 1) === 0.0 ? (int) $result : $result;
2456
    }
2457
 
2458
    /**
2459
     * Determines if the instance is equal to another
2460
     *
2461
     * @param CarbonInterval|DateInterval|mixed $interval
2462
     *
2463
     * @see equalTo()
2464
     *
2465
     * @return bool
2466
     */
2467
    public function eq($interval): bool
2468
    {
2469
        return $this->equalTo($interval);
2470
    }
2471
 
2472
    /**
2473
     * Determines if the instance is equal to another
2474
     *
2475
     * @param CarbonInterval|DateInterval|mixed $interval
2476
     *
2477
     * @return bool
2478
     */
2479
    public function equalTo($interval): bool
2480
    {
2481
        $interval = $this->resolveInterval($interval);
2482
 
2483
        return $interval !== null && $this->totalMicroseconds === $interval->totalMicroseconds;
2484
    }
2485
 
2486
    /**
2487
     * Determines if the instance is not equal to another
2488
     *
2489
     * @param CarbonInterval|DateInterval|mixed $interval
2490
     *
2491
     * @see notEqualTo()
2492
     *
2493
     * @return bool
2494
     */
2495
    public function ne($interval): bool
2496
    {
2497
        return $this->notEqualTo($interval);
2498
    }
2499
 
2500
    /**
2501
     * Determines if the instance is not equal to another
2502
     *
2503
     * @param CarbonInterval|DateInterval|mixed $interval
2504
     *
2505
     * @return bool
2506
     */
2507
    public function notEqualTo($interval): bool
2508
    {
2509
        return !$this->eq($interval);
2510
    }
2511
 
2512
    /**
2513
     * Determines if the instance is greater (longer) than another
2514
     *
2515
     * @param CarbonInterval|DateInterval|mixed $interval
2516
     *
2517
     * @see greaterThan()
2518
     *
2519
     * @return bool
2520
     */
2521
    public function gt($interval): bool
2522
    {
2523
        return $this->greaterThan($interval);
2524
    }
2525
 
2526
    /**
2527
     * Determines if the instance is greater (longer) than another
2528
     *
2529
     * @param CarbonInterval|DateInterval|mixed $interval
2530
     *
2531
     * @return bool
2532
     */
2533
    public function greaterThan($interval): bool
2534
    {
2535
        $interval = $this->resolveInterval($interval);
2536
 
2537
        return $interval === null || $this->totalMicroseconds > $interval->totalMicroseconds;
2538
    }
2539
 
2540
    /**
2541
     * Determines if the instance is greater (longer) than or equal to another
2542
     *
2543
     * @param CarbonInterval|DateInterval|mixed $interval
2544
     *
2545
     * @see greaterThanOrEqualTo()
2546
     *
2547
     * @return bool
2548
     */
2549
    public function gte($interval): bool
2550
    {
2551
        return $this->greaterThanOrEqualTo($interval);
2552
    }
2553
 
2554
    /**
2555
     * Determines if the instance is greater (longer) than or equal to another
2556
     *
2557
     * @param CarbonInterval|DateInterval|mixed $interval
2558
     *
2559
     * @return bool
2560
     */
2561
    public function greaterThanOrEqualTo($interval): bool
2562
    {
2563
        return $this->greaterThan($interval) || $this->equalTo($interval);
2564
    }
2565
 
2566
    /**
2567
     * Determines if the instance is less (shorter) than another
2568
     *
2569
     * @param CarbonInterval|DateInterval|mixed $interval
2570
     *
2571
     * @see lessThan()
2572
     *
2573
     * @return bool
2574
     */
2575
    public function lt($interval): bool
2576
    {
2577
        return $this->lessThan($interval);
2578
    }
2579
 
2580
    /**
2581
     * Determines if the instance is less (shorter) than another
2582
     *
2583
     * @param CarbonInterval|DateInterval|mixed $interval
2584
     *
2585
     * @return bool
2586
     */
2587
    public function lessThan($interval): bool
2588
    {
2589
        $interval = $this->resolveInterval($interval);
2590
 
2591
        return $interval !== null && $this->totalMicroseconds < $interval->totalMicroseconds;
2592
    }
2593
 
2594
    /**
2595
     * Determines if the instance is less (shorter) than or equal to another
2596
     *
2597
     * @param CarbonInterval|DateInterval|mixed $interval
2598
     *
2599
     * @see lessThanOrEqualTo()
2600
     *
2601
     * @return bool
2602
     */
2603
    public function lte($interval): bool
2604
    {
2605
        return $this->lessThanOrEqualTo($interval);
2606
    }
2607
 
2608
    /**
2609
     * Determines if the instance is less (shorter) than or equal to another
2610
     *
2611
     * @param CarbonInterval|DateInterval|mixed $interval
2612
     *
2613
     * @return bool
2614
     */
2615
    public function lessThanOrEqualTo($interval): bool
2616
    {
2617
        return $this->lessThan($interval) || $this->equalTo($interval);
2618
    }
2619
 
2620
    /**
2621
     * Determines if the instance is between two others.
2622
     *
2623
     * The third argument allow you to specify if bounds are included or not (true by default)
2624
     * but for when you including/excluding bounds may produce different results in your application,
2625
     * we recommend to use the explicit methods ->betweenIncluded() or ->betweenExcluded() instead.
2626
     *
2627
     * @example
2628
     * ```
2629
     * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::days(3)); // true
2630
     * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::hours(36)); // false
2631
     * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::days(2)); // true
2632
     * CarbonInterval::hours(48)->between(CarbonInterval::day(), CarbonInterval::days(2), false); // false
2633
     * ```
2634
     *
2635
     * @param CarbonInterval|DateInterval|mixed $interval1
2636
     * @param CarbonInterval|DateInterval|mixed $interval2
2637
     * @param bool                              $equal     Indicates if an equal to comparison should be done
2638
     *
2639
     * @return bool
2640
     */
2641
    public function between($interval1, $interval2, $equal = true): bool
2642
    {
2643
        return $equal
2644
            ? $this->greaterThanOrEqualTo($interval1) && $this->lessThanOrEqualTo($interval2)
2645
            : $this->greaterThan($interval1) && $this->lessThan($interval2);
2646
    }
2647
 
2648
    /**
2649
     * Determines if the instance is between two others, bounds excluded.
2650
     *
2651
     * @example
2652
     * ```
2653
     * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(3)); // true
2654
     * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::hours(36)); // false
2655
     * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(2)); // true
2656
     * ```
2657
     *
2658
     * @param CarbonInterval|DateInterval|mixed $interval1
2659
     * @param CarbonInterval|DateInterval|mixed $interval2
2660
     *
2661
     * @return bool
2662
     */
2663
    public function betweenIncluded($interval1, $interval2): bool
2664
    {
2665
        return $this->between($interval1, $interval2, true);
2666
    }
2667
 
2668
    /**
2669
     * Determines if the instance is between two others, bounds excluded.
2670
     *
2671
     * @example
2672
     * ```
2673
     * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(3)); // true
2674
     * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::hours(36)); // false
2675
     * CarbonInterval::hours(48)->betweenExcluded(CarbonInterval::day(), CarbonInterval::days(2)); // false
2676
     * ```
2677
     *
2678
     * @param CarbonInterval|DateInterval|mixed $interval1
2679
     * @param CarbonInterval|DateInterval|mixed $interval2
2680
     *
2681
     * @return bool
2682
     */
2683
    public function betweenExcluded($interval1, $interval2): bool
2684
    {
2685
        return $this->between($interval1, $interval2, false);
2686
    }
2687
 
2688
    /**
2689
     * Determines if the instance is between two others
2690
     *
2691
     * @example
2692
     * ```
2693
     * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::days(3)); // true
2694
     * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::hours(36)); // false
2695
     * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::days(2)); // true
2696
     * CarbonInterval::hours(48)->isBetween(CarbonInterval::day(), CarbonInterval::days(2), false); // false
2697
     * ```
2698
     *
2699
     * @param CarbonInterval|DateInterval|mixed $interval1
2700
     * @param CarbonInterval|DateInterval|mixed $interval2
2701
     * @param bool                              $equal     Indicates if an equal to comparison should be done
2702
     *
2703
     * @return bool
2704
     */
2705
    public function isBetween($interval1, $interval2, $equal = true): bool
2706
    {
2707
        return $this->between($interval1, $interval2, $equal);
2708
    }
2709
 
2710
    /**
2711
     * Round the current instance at the given unit with given precision if specified and the given function.
2712
     *
2713
     * @param string                             $unit
2714
     * @param float|int|string|DateInterval|null $precision
2715
     * @param string                             $function
2716
     *
2717
     * @throws Exception
2718
     *
2719
     * @return $this
2720
     */
2721
    public function roundUnit($unit, $precision = 1, $function = 'round')
2722
    {
2723
        $base = CarbonImmutable::parse('2000-01-01 00:00:00', 'UTC')
2724
            ->roundUnit($unit, $precision, $function);
2725
        $next = $base->add($this);
2726
        $inverted = $next < $base;
2727
 
2728
        if ($inverted) {
2729
            $next = $base->sub($this);
2730
        }
2731
 
2732
        $this->copyProperties(
2733
            $next
2734
                ->roundUnit($unit, $precision, $function)
2735
                ->diffAsCarbonInterval($base)
2736
        );
2737
 
2738
        return $this->invert($inverted);
2739
    }
2740
 
2741
    /**
2742
     * Truncate the current instance at the given unit with given precision if specified.
2743
     *
2744
     * @param string                             $unit
2745
     * @param float|int|string|DateInterval|null $precision
2746
     *
2747
     * @throws Exception
2748
     *
2749
     * @return $this
2750
     */
2751
    public function floorUnit($unit, $precision = 1)
2752
    {
2753
        return $this->roundUnit($unit, $precision, 'floor');
2754
    }
2755
 
2756
    /**
2757
     * Ceil the current instance at the given unit with given precision if specified.
2758
     *
2759
     * @param string                             $unit
2760
     * @param float|int|string|DateInterval|null $precision
2761
     *
2762
     * @throws Exception
2763
     *
2764
     * @return $this
2765
     */
2766
    public function ceilUnit($unit, $precision = 1)
2767
    {
2768
        return $this->roundUnit($unit, $precision, 'ceil');
2769
    }
2770
 
2771
    /**
2772
     * Round the current instance second with given precision if specified.
2773
     *
2774
     * @param float|int|string|DateInterval|null $precision
2775
     * @param string                             $function
2776
     *
2777
     * @throws Exception
2778
     *
2779
     * @return $this
2780
     */
2781
    public function round($precision = 1, $function = 'round')
2782
    {
2783
        return $this->roundWith($precision, $function);
2784
    }
2785
 
2786
    /**
2787
     * Round the current instance second with given precision if specified.
2788
     *
2789
     * @param float|int|string|DateInterval|null $precision
2790
     *
2791
     * @throws Exception
2792
     *
2793
     * @return $this
2794
     */
2795
    public function floor($precision = 1)
2796
    {
2797
        return $this->round($precision, 'floor');
2798
    }
2799
 
2800
    /**
2801
     * Ceil the current instance second with given precision if specified.
2802
     *
2803
     * @param float|int|string|DateInterval|null $precision
2804
     *
2805
     * @throws Exception
2806
     *
2807
     * @return $this
2808
     */
2809
    public function ceil($precision = 1)
2810
    {
2811
        return $this->round($precision, 'ceil');
2812
    }
2813
 
2814
    private function needsDeclension(string $mode, int $index, int $parts): bool
2815
    {
2816
        switch ($mode) {
2817
            case 'last':
2818
                return $index === $parts - 1;
2819
            default:
2820
                return true;
2821
        }
2822
    }
2823
}