Subversion-Projekte lars-tiefland.laravel_shop

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\Traits;
13
 
14
use Carbon\Carbon;
15
use Carbon\CarbonImmutable;
16
use Carbon\CarbonInterface;
17
use Carbon\Exceptions\InvalidDateException;
18
use Carbon\Exceptions\InvalidFormatException;
19
use Carbon\Exceptions\OutOfRangeException;
20
use Carbon\Translator;
21
use Closure;
22
use DateTimeInterface;
23
use DateTimeZone;
24
use Exception;
25
use ReturnTypeWillChange;
26
 
27
/**
28
 * Trait Creator.
29
 *
30
 * Static creators.
31
 *
32
 * Depends on the following methods:
33
 *
34
 * @method static Carbon|CarbonImmutable getTestNow()
35
 */
36
trait Creator
37
{
38
    use ObjectInitialisation;
39
 
40
    /**
41
     * The errors that can occur.
42
     *
43
     * @var array
44
     */
45
    protected static $lastErrors;
46
 
47
    /**
48
     * Create a new Carbon instance.
49
     *
50
     * Please see the testing aids section (specifically static::setTestNow())
51
     * for more on the possibility of this constructor returning a test instance.
52
     *
53
     * @param DateTimeInterface|string|null $time
54
     * @param DateTimeZone|string|null      $tz
55
     *
56
     * @throws InvalidFormatException
57
     */
58
    public function __construct($time = null, $tz = null)
59
    {
60
        if ($time instanceof DateTimeInterface) {
61
            $time = $this->constructTimezoneFromDateTime($time, $tz)->format('Y-m-d H:i:s.u');
62
        }
63
 
64
        if (is_numeric($time) && (!\is_string($time) || !preg_match('/^\d{1,14}$/', $time))) {
65
            $time = static::createFromTimestampUTC($time)->format('Y-m-d\TH:i:s.uP');
66
        }
67
 
68
        // If the class has a test now set and we are trying to create a now()
69
        // instance then override as required
70
        $isNow = empty($time) || $time === 'now';
71
 
72
        if (method_exists(static::class, 'hasTestNow') &&
73
            method_exists(static::class, 'getTestNow') &&
74
            static::hasTestNow() &&
75
            ($isNow || static::hasRelativeKeywords($time))
76
        ) {
77
            static::mockConstructorParameters($time, $tz);
78
        }
79
 
80
        // Work-around for PHP bug https://bugs.php.net/bug.php?id=67127
81
        if (!str_contains((string) .1, '.')) {
82
            $locale = setlocale(LC_NUMERIC, '0'); // @codeCoverageIgnore
83
            setlocale(LC_NUMERIC, 'C'); // @codeCoverageIgnore
84
        }
85
 
86
        try {
87
            parent::__construct($time ?: 'now', static::safeCreateDateTimeZone($tz) ?: null);
88
        } catch (Exception $exception) {
89
            throw new InvalidFormatException($exception->getMessage(), 0, $exception);
90
        }
91
 
92
        $this->constructedObjectId = spl_object_hash($this);
93
 
94
        if (isset($locale)) {
95
            setlocale(LC_NUMERIC, $locale); // @codeCoverageIgnore
96
        }
97
 
98
        self::setLastErrors(parent::getLastErrors());
99
    }
100
 
101
    /**
102
     * Get timezone from a datetime instance.
103
     *
104
     * @param DateTimeInterface        $date
105
     * @param DateTimeZone|string|null $tz
106
     *
107
     * @return DateTimeInterface
108
     */
109
    private function constructTimezoneFromDateTime(DateTimeInterface $date, &$tz)
110
    {
111
        if ($tz !== null) {
112
            $safeTz = static::safeCreateDateTimeZone($tz);
113
 
114
            if ($safeTz) {
115
                return $date->setTimezone($safeTz);
116
            }
117
 
118
            return $date;
119
        }
120
 
121
        $tz = $date->getTimezone();
122
 
123
        return $date;
124
    }
125
 
126
    /**
127
     * Update constructedObjectId on cloned.
128
     */
129
    public function __clone()
130
    {
131
        $this->constructedObjectId = spl_object_hash($this);
132
    }
133
 
134
    /**
135
     * Create a Carbon instance from a DateTime one.
136
     *
137
     * @param DateTimeInterface $date
138
     *
139
     * @return static
140
     */
141
    public static function instance($date)
142
    {
143
        if ($date instanceof static) {
144
            return clone $date;
145
        }
146
 
147
        static::expectDateTime($date);
148
 
149
        $instance = new static($date->format('Y-m-d H:i:s.u'), $date->getTimezone());
150
 
151
        if ($date instanceof CarbonInterface) {
152
            $settings = $date->getSettings();
153
 
154
            if (!$date->hasLocalTranslator()) {
155
                unset($settings['locale']);
156
            }
157
 
158
            $instance->settings($settings);
159
        }
160
 
161
        return $instance;
162
    }
163
 
164
    /**
165
     * Create a carbon instance from a string.
166
     *
167
     * This is an alias for the constructor that allows better fluent syntax
168
     * as it allows you to do Carbon::parse('Monday next week')->fn() rather
169
     * than (new Carbon('Monday next week'))->fn().
170
     *
171
     * @param string|DateTimeInterface|null $time
172
     * @param DateTimeZone|string|null      $tz
173
     *
174
     * @throws InvalidFormatException
175
     *
176
     * @return static
177
     */
178
    public static function rawParse($time = null, $tz = null)
179
    {
180
        if ($time instanceof DateTimeInterface) {
181
            return static::instance($time);
182
        }
183
 
184
        try {
185
            return new static($time, $tz);
186
        } catch (Exception $exception) {
187
            $date = @static::now($tz)->change($time);
188
 
189
            if (!$date) {
190
                throw new InvalidFormatException("Could not parse '$time': ".$exception->getMessage(), 0, $exception);
191
            }
192
 
193
            return $date;
194
        }
195
    }
196
 
197
    /**
198
     * Create a carbon instance from a string.
199
     *
200
     * This is an alias for the constructor that allows better fluent syntax
201
     * as it allows you to do Carbon::parse('Monday next week')->fn() rather
202
     * than (new Carbon('Monday next week'))->fn().
203
     *
204
     * @param string|DateTimeInterface|null $time
205
     * @param DateTimeZone|string|null      $tz
206
     *
207
     * @throws InvalidFormatException
208
     *
209
     * @return static
210
     */
211
    public static function parse($time = null, $tz = null)
212
    {
213
        $function = static::$parseFunction;
214
 
215
        if (!$function) {
216
            return static::rawParse($time, $tz);
217
        }
218
 
219
        if (\is_string($function) && method_exists(static::class, $function)) {
220
            $function = [static::class, $function];
221
        }
222
 
223
        return $function(...\func_get_args());
224
    }
225
 
226
    /**
227
     * Create a carbon instance from a localized string (in French, Japanese, Arabic, etc.).
228
     *
229
     * @param string                   $time   date/time string in the given language (may also contain English).
230
     * @param string|null              $locale if locale is null or not specified, current global locale will be
231
     *                                         used instead.
232
     * @param DateTimeZone|string|null $tz     optional timezone for the new instance.
233
     *
234
     * @throws InvalidFormatException
235
     *
236
     * @return static
237
     */
238
    public static function parseFromLocale($time, $locale = null, $tz = null)
239
    {
240
        return static::rawParse(static::translateTimeString($time, $locale, 'en'), $tz);
241
    }
242
 
243
    /**
244
     * Get a Carbon instance for the current date and time.
245
     *
246
     * @param DateTimeZone|string|null $tz
247
     *
248
     * @return static
249
     */
250
    public static function now($tz = null)
251
    {
252
        return new static(null, $tz);
253
    }
254
 
255
    /**
256
     * Create a Carbon instance for today.
257
     *
258
     * @param DateTimeZone|string|null $tz
259
     *
260
     * @return static
261
     */
262
    public static function today($tz = null)
263
    {
264
        return static::rawParse('today', $tz);
265
    }
266
 
267
    /**
268
     * Create a Carbon instance for tomorrow.
269
     *
270
     * @param DateTimeZone|string|null $tz
271
     *
272
     * @return static
273
     */
274
    public static function tomorrow($tz = null)
275
    {
276
        return static::rawParse('tomorrow', $tz);
277
    }
278
 
279
    /**
280
     * Create a Carbon instance for yesterday.
281
     *
282
     * @param DateTimeZone|string|null $tz
283
     *
284
     * @return static
285
     */
286
    public static function yesterday($tz = null)
287
    {
288
        return static::rawParse('yesterday', $tz);
289
    }
290
 
291
    /**
292
     * Create a Carbon instance for the greatest supported date.
293
     *
294
     * @return static
295
     */
296
    public static function maxValue()
297
    {
298
        if (self::$PHPIntSize === 4) {
299
            // 32 bit
300
            return static::createFromTimestamp(PHP_INT_MAX); // @codeCoverageIgnore
301
        }
302
 
303
        // 64 bit
304
        return static::create(9999, 12, 31, 23, 59, 59);
305
    }
306
 
307
    /**
308
     * Create a Carbon instance for the lowest supported date.
309
     *
310
     * @return static
311
     */
312
    public static function minValue()
313
    {
314
        if (self::$PHPIntSize === 4) {
315
            // 32 bit
316
            return static::createFromTimestamp(~PHP_INT_MAX); // @codeCoverageIgnore
317
        }
318
 
319
        // 64 bit
320
        return static::create(1, 1, 1, 0, 0, 0);
321
    }
322
 
323
    private static function assertBetween($unit, $value, $min, $max)
324
    {
325
        if (static::isStrictModeEnabled() && ($value < $min || $value > $max)) {
326
            throw new OutOfRangeException($unit, $min, $max, $value);
327
        }
328
    }
329
 
330
    private static function createNowInstance($tz)
331
    {
332
        if (!static::hasTestNow()) {
333
            return static::now($tz);
334
        }
335
 
336
        $now = static::getTestNow();
337
 
338
        if ($now instanceof Closure) {
339
            return $now(static::now($tz));
340
        }
341
 
342
        return $now->avoidMutation()->tz($tz);
343
    }
344
 
345
    /**
346
     * Create a new Carbon instance from a specific date and time.
347
     *
348
     * If any of $year, $month or $day are set to null their now() values will
349
     * be used.
350
     *
351
     * If $hour is null it will be set to its now() value and the default
352
     * values for $minute and $second will be their now() values.
353
     *
354
     * If $hour is not null then the default values for $minute and $second
355
     * will be 0.
356
     *
357
     * @param DateTimeInterface|int|null $year
358
     * @param int|null                   $month
359
     * @param int|null                   $day
360
     * @param int|null                   $hour
361
     * @param int|null                   $minute
362
     * @param int|null                   $second
363
     * @param DateTimeZone|string|null   $tz
364
     *
365
     * @throws InvalidFormatException
366
     *
367
     * @return static|false
368
     */
369
    public static function create($year = 0, $month = 1, $day = 1, $hour = 0, $minute = 0, $second = 0, $tz = null)
370
    {
371
        if ((\is_string($year) && !is_numeric($year)) || $year instanceof DateTimeInterface) {
372
            return static::parse($year, $tz ?: (\is_string($month) || $month instanceof DateTimeZone ? $month : null));
373
        }
374
 
375
        $defaults = null;
376
        $getDefault = function ($unit) use ($tz, &$defaults) {
377
            if ($defaults === null) {
378
                $now = self::createNowInstance($tz);
379
 
380
                $defaults = array_combine([
381
                    'year',
382
                    'month',
383
                    'day',
384
                    'hour',
385
                    'minute',
386
                    'second',
387
                ], explode('-', $now->rawFormat('Y-n-j-G-i-s.u')));
388
            }
389
 
390
            return $defaults[$unit];
391
        };
392
 
393
        $year = $year ?? $getDefault('year');
394
        $month = $month ?? $getDefault('month');
395
        $day = $day ?? $getDefault('day');
396
        $hour = $hour ?? $getDefault('hour');
397
        $minute = $minute ?? $getDefault('minute');
398
        $second = (float) ($second ?? $getDefault('second'));
399
 
400
        self::assertBetween('month', $month, 0, 99);
401
        self::assertBetween('day', $day, 0, 99);
402
        self::assertBetween('hour', $hour, 0, 99);
403
        self::assertBetween('minute', $minute, 0, 99);
404
        self::assertBetween('second', $second, 0, 99);
405
 
406
        $fixYear = null;
407
 
408
        if ($year < 0) {
409
            $fixYear = $year;
410
            $year = 0;
411
        } elseif ($year > 9999) {
412
            $fixYear = $year - 9999;
413
            $year = 9999;
414
        }
415
 
416
        $second = ($second < 10 ? '0' : '').number_format($second, 6);
417
        $instance = static::rawCreateFromFormat('!Y-n-j G:i:s.u', sprintf('%s-%s-%s %s:%02s:%02s', $year, $month, $day, $hour, $minute, $second), $tz);
418
 
419
        if ($fixYear !== null) {
420
            $instance = $instance->addYears($fixYear);
421
        }
422
 
423
        return $instance;
424
    }
425
 
426
    /**
427
     * Create a new safe Carbon instance from a specific date and time.
428
     *
429
     * If any of $year, $month or $day are set to null their now() values will
430
     * be used.
431
     *
432
     * If $hour is null it will be set to its now() value and the default
433
     * values for $minute and $second will be their now() values.
434
     *
435
     * If $hour is not null then the default values for $minute and $second
436
     * will be 0.
437
     *
438
     * If one of the set values is not valid, an InvalidDateException
439
     * will be thrown.
440
     *
441
     * @param int|null                 $year
442
     * @param int|null                 $month
443
     * @param int|null                 $day
444
     * @param int|null                 $hour
445
     * @param int|null                 $minute
446
     * @param int|null                 $second
447
     * @param DateTimeZone|string|null $tz
448
     *
449
     * @throws InvalidDateException
450
     *
451
     * @return static|false
452
     */
453
    public static function createSafe($year = null, $month = null, $day = null, $hour = null, $minute = null, $second = null, $tz = null)
454
    {
455
        $fields = static::getRangesByUnit();
456
 
457
        foreach ($fields as $field => $range) {
458
            if ($$field !== null && (!\is_int($$field) || $$field < $range[0] || $$field > $range[1])) {
459
                if (static::isStrictModeEnabled()) {
460
                    throw new InvalidDateException($field, $$field);
461
                }
462
 
463
                return false;
464
            }
465
        }
466
 
467
        $instance = static::create($year, $month, $day, $hour, $minute, $second, $tz);
468
 
469
        foreach (array_reverse($fields) as $field => $range) {
470
            if ($$field !== null && (!\is_int($$field) || $$field !== $instance->$field)) {
471
                if (static::isStrictModeEnabled()) {
472
                    throw new InvalidDateException($field, $$field);
473
                }
474
 
475
                return false;
476
            }
477
        }
478
 
479
        return $instance;
480
    }
481
 
482
    /**
483
     * Create a new Carbon instance from a specific date and time using strict validation.
484
     *
485
     * @see create()
486
     *
487
     * @param int|null                 $year
488
     * @param int|null                 $month
489
     * @param int|null                 $day
490
     * @param int|null                 $hour
491
     * @param int|null                 $minute
492
     * @param int|null                 $second
493
     * @param DateTimeZone|string|null $tz
494
     *
495
     * @throws InvalidFormatException
496
     *
497
     * @return static
498
     */
499
    public static function createStrict(?int $year = 0, ?int $month = 1, ?int $day = 1, ?int $hour = 0, ?int $minute = 0, ?int $second = 0, $tz = null): self
500
    {
501
        $initialStrictMode = static::isStrictModeEnabled();
502
        static::useStrictMode(true);
503
 
504
        try {
505
            $date = static::create($year, $month, $day, $hour, $minute, $second, $tz);
506
        } finally {
507
            static::useStrictMode($initialStrictMode);
508
        }
509
 
510
        return $date;
511
    }
512
 
513
    /**
514
     * Create a Carbon instance from just a date. The time portion is set to now.
515
     *
516
     * @param int|null                 $year
517
     * @param int|null                 $month
518
     * @param int|null                 $day
519
     * @param DateTimeZone|string|null $tz
520
     *
521
     * @throws InvalidFormatException
522
     *
523
     * @return static
524
     */
525
    public static function createFromDate($year = null, $month = null, $day = null, $tz = null)
526
    {
527
        return static::create($year, $month, $day, null, null, null, $tz);
528
    }
529
 
530
    /**
531
     * Create a Carbon instance from just a date. The time portion is set to midnight.
532
     *
533
     * @param int|null                 $year
534
     * @param int|null                 $month
535
     * @param int|null                 $day
536
     * @param DateTimeZone|string|null $tz
537
     *
538
     * @throws InvalidFormatException
539
     *
540
     * @return static
541
     */
542
    public static function createMidnightDate($year = null, $month = null, $day = null, $tz = null)
543
    {
544
        return static::create($year, $month, $day, 0, 0, 0, $tz);
545
    }
546
 
547
    /**
548
     * Create a Carbon instance from just a time. The date portion is set to today.
549
     *
550
     * @param int|null                 $hour
551
     * @param int|null                 $minute
552
     * @param int|null                 $second
553
     * @param DateTimeZone|string|null $tz
554
     *
555
     * @throws InvalidFormatException
556
     *
557
     * @return static
558
     */
559
    public static function createFromTime($hour = 0, $minute = 0, $second = 0, $tz = null)
560
    {
561
        return static::create(null, null, null, $hour, $minute, $second, $tz);
562
    }
563
 
564
    /**
565
     * Create a Carbon instance from a time string. The date portion is set to today.
566
     *
567
     * @param string                   $time
568
     * @param DateTimeZone|string|null $tz
569
     *
570
     * @throws InvalidFormatException
571
     *
572
     * @return static
573
     */
574
    public static function createFromTimeString($time, $tz = null)
575
    {
576
        return static::today($tz)->setTimeFromTimeString($time);
577
    }
578
 
579
    /**
580
     * @param string                         $format     Datetime format
581
     * @param string                         $time
582
     * @param DateTimeZone|string|false|null $originalTz
583
     *
584
     * @return DateTimeInterface|false
585
     */
586
    private static function createFromFormatAndTimezone($format, $time, $originalTz)
587
    {
588
        // Work-around for https://bugs.php.net/bug.php?id=75577
589
        // @codeCoverageIgnoreStart
590
        if (version_compare(PHP_VERSION, '7.3.0-dev', '<')) {
591
            $format = str_replace('.v', '.u', $format);
592
        }
593
        // @codeCoverageIgnoreEnd
594
 
595
        if ($originalTz === null) {
596
            return parent::createFromFormat($format, (string) $time);
597
        }
598
 
599
        $tz = \is_int($originalTz)
600
            ? @timezone_name_from_abbr('', (int) ($originalTz * static::MINUTES_PER_HOUR * static::SECONDS_PER_MINUTE), 1)
601
            : $originalTz;
602
 
603
        $tz = static::safeCreateDateTimeZone($tz, $originalTz);
604
 
605
        if ($tz === false) {
606
            return false;
607
        }
608
 
609
        return parent::createFromFormat($format, (string) $time, $tz);
610
    }
611
 
612
    /**
613
     * Create a Carbon instance from a specific format.
614
     *
615
     * @param string                         $format Datetime format
616
     * @param string                         $time
617
     * @param DateTimeZone|string|false|null $tz
618
     *
619
     * @throws InvalidFormatException
620
     *
621
     * @return static|false
622
     */
623
    public static function rawCreateFromFormat($format, $time, $tz = null)
624
    {
625
        // Work-around for https://bugs.php.net/bug.php?id=80141
626
        $format = preg_replace('/(?<!\\\\)((?:\\\\{2})*)c/', '$1Y-m-d\TH:i:sP', $format);
627
 
628
        if (preg_match('/(?<!\\\\)(?:\\\\{2})*(a|A)/', $format, $aMatches, PREG_OFFSET_CAPTURE) &&
629
            preg_match('/(?<!\\\\)(?:\\\\{2})*(h|g|H|G)/', $format, $hMatches, PREG_OFFSET_CAPTURE) &&
630
            $aMatches[1][1] < $hMatches[1][1] &&
631
            preg_match('/(am|pm|AM|PM)/', $time)
632
        ) {
633
            $format = preg_replace('/^(.*)(?<!\\\\)((?:\\\\{2})*)(a|A)(.*)$/U', '$1$2$4 $3', $format);
634
            $time = preg_replace('/^(.*)(am|pm|AM|PM)(.*)$/U', '$1$3 $2', $time);
635
        }
636
 
637
        // First attempt to create an instance, so that error messages are based on the unmodified format.
638
        $date = self::createFromFormatAndTimezone($format, $time, $tz);
639
        $lastErrors = parent::getLastErrors();
640
        /** @var \Carbon\CarbonImmutable|\Carbon\Carbon|null $mock */
641
        $mock = static::getMockedTestNow($tz);
642
 
643
        if ($mock && $date instanceof DateTimeInterface) {
644
            // Set timezone from mock if custom timezone was neither given directly nor as a part of format.
645
            // First let's skip the part that will be ignored by the parser.
646
            $nonEscaped = '(?<!\\\\)(\\\\{2})*';
647
 
648
            $nonIgnored = preg_replace("/^.*{$nonEscaped}!/s", '', $format);
649
 
650
            if ($tz === null && !preg_match("/{$nonEscaped}[eOPT]/", $nonIgnored)) {
651
                $tz = clone $mock->getTimezone();
652
            }
653
 
654
            // Set microseconds to zero to match behavior of DateTime::createFromFormat()
655
            // See https://bugs.php.net/bug.php?id=74332
656
            $mock = $mock->copy()->microsecond(0);
657
 
658
            // Prepend mock datetime only if the format does not contain non escaped unix epoch reset flag.
659
            if (!preg_match("/{$nonEscaped}[!|]/", $format)) {
660
                $format = static::MOCK_DATETIME_FORMAT.' '.$format;
661
                $time = ($mock instanceof self ? $mock->rawFormat(static::MOCK_DATETIME_FORMAT) : $mock->format(static::MOCK_DATETIME_FORMAT)).' '.$time;
662
            }
663
 
664
            // Regenerate date from the modified format to base result on the mocked instance instead of now.
665
            $date = self::createFromFormatAndTimezone($format, $time, $tz);
666
        }
667
 
668
        if ($date instanceof DateTimeInterface) {
669
            $instance = static::instance($date);
670
            $instance::setLastErrors($lastErrors);
671
 
672
            return $instance;
673
        }
674
 
675
        if (static::isStrictModeEnabled()) {
676
            throw new InvalidFormatException(implode(PHP_EOL, $lastErrors['errors']));
677
        }
678
 
679
        return false;
680
    }
681
 
682
    /**
683
     * Create a Carbon instance from a specific format.
684
     *
685
     * @param string                         $format Datetime format
686
     * @param string                         $time
687
     * @param DateTimeZone|string|false|null $tz
688
     *
689
     * @throws InvalidFormatException
690
     *
691
     * @return static|false
692
     */
693
    #[ReturnTypeWillChange]
694
    public static function createFromFormat($format, $time, $tz = null)
695
    {
696
        $function = static::$createFromFormatFunction;
697
 
698
        if (!$function) {
699
            return static::rawCreateFromFormat($format, $time, $tz);
700
        }
701
 
702
        if (\is_string($function) && method_exists(static::class, $function)) {
703
            $function = [static::class, $function];
704
        }
705
 
706
        return $function(...\func_get_args());
707
    }
708
 
709
    /**
710
     * Create a Carbon instance from a specific ISO format (same replacements as ->isoFormat()).
711
     *
712
     * @param string                                             $format     Datetime format
713
     * @param string                                             $time
714
     * @param DateTimeZone|string|false|null                     $tz         optional timezone
715
     * @param string|null                                        $locale     locale to be used for LTS, LT, LL, LLL, etc. macro-formats (en by fault, unneeded if no such macro-format in use)
716
     * @param \Symfony\Component\Translation\TranslatorInterface $translator optional custom translator to use for macro-formats
717
     *
718
     * @throws InvalidFormatException
719
     *
720
     * @return static|false
721
     */
722
    public static function createFromIsoFormat($format, $time, $tz = null, $locale = 'en', $translator = null)
723
    {
724
        $format = preg_replace_callback('/(?<!\\\\)(\\\\{2})*(LTS|LT|[Ll]{1,4})/', function ($match) use ($locale, $translator) {
725
            [$code] = $match;
726
 
727
            static $formats = null;
728
 
729
            if ($formats === null) {
730
                $translator = $translator ?: Translator::get($locale);
731
 
732
                $formats = [
733
                    'LT' => static::getTranslationMessageWith($translator, 'formats.LT', $locale, 'h:mm A'),
734
                    'LTS' => static::getTranslationMessageWith($translator, 'formats.LTS', $locale, 'h:mm:ss A'),
735
                    'L' => static::getTranslationMessageWith($translator, 'formats.L', $locale, 'MM/DD/YYYY'),
736
                    'LL' => static::getTranslationMessageWith($translator, 'formats.LL', $locale, 'MMMM D, YYYY'),
737
                    'LLL' => static::getTranslationMessageWith($translator, 'formats.LLL', $locale, 'MMMM D, YYYY h:mm A'),
738
                    'LLLL' => static::getTranslationMessageWith($translator, 'formats.LLLL', $locale, 'dddd, MMMM D, YYYY h:mm A'),
739
                ];
740
            }
741
 
742
            return $formats[$code] ?? preg_replace_callback(
743
                '/MMMM|MM|DD|dddd/',
744
                function ($code) {
745
                    return mb_substr($code[0], 1);
746
                },
747
                $formats[strtoupper($code)] ?? ''
748
            );
749
        }, $format);
750
 
751
        $format = preg_replace_callback('/(?<!\\\\)(\\\\{2})*('.CarbonInterface::ISO_FORMAT_REGEXP.'|[A-Za-z])/', function ($match) {
752
            [$code] = $match;
753
 
754
            static $replacements = null;
755
 
756
            if ($replacements === null) {
757
                $replacements = [
758
                    'OD' => 'd',
759
                    'OM' => 'M',
760
                    'OY' => 'Y',
761
                    'OH' => 'G',
762
                    'Oh' => 'g',
763
                    'Om' => 'i',
764
                    'Os' => 's',
765
                    'D' => 'd',
766
                    'DD' => 'd',
767
                    'Do' => 'd',
768
                    'd' => '!',
769
                    'dd' => '!',
770
                    'ddd' => 'D',
771
                    'dddd' => 'D',
772
                    'DDD' => 'z',
773
                    'DDDD' => 'z',
774
                    'DDDo' => 'z',
775
                    'e' => '!',
776
                    'E' => '!',
777
                    'H' => 'G',
778
                    'HH' => 'H',
779
                    'h' => 'g',
780
                    'hh' => 'h',
781
                    'k' => 'G',
782
                    'kk' => 'G',
783
                    'hmm' => 'gi',
784
                    'hmmss' => 'gis',
785
                    'Hmm' => 'Gi',
786
                    'Hmmss' => 'Gis',
787
                    'm' => 'i',
788
                    'mm' => 'i',
789
                    'a' => 'a',
790
                    'A' => 'a',
791
                    's' => 's',
792
                    'ss' => 's',
793
                    'S' => '*',
794
                    'SS' => '*',
795
                    'SSS' => '*',
796
                    'SSSS' => '*',
797
                    'SSSSS' => '*',
798
                    'SSSSSS' => 'u',
799
                    'SSSSSSS' => 'u*',
800
                    'SSSSSSSS' => 'u*',
801
                    'SSSSSSSSS' => 'u*',
802
                    'M' => 'm',
803
                    'MM' => 'm',
804
                    'MMM' => 'M',
805
                    'MMMM' => 'M',
806
                    'Mo' => 'm',
807
                    'Q' => '!',
808
                    'Qo' => '!',
809
                    'G' => '!',
810
                    'GG' => '!',
811
                    'GGG' => '!',
812
                    'GGGG' => '!',
813
                    'GGGGG' => '!',
814
                    'g' => '!',
815
                    'gg' => '!',
816
                    'ggg' => '!',
817
                    'gggg' => '!',
818
                    'ggggg' => '!',
819
                    'W' => '!',
820
                    'WW' => '!',
821
                    'Wo' => '!',
822
                    'w' => '!',
823
                    'ww' => '!',
824
                    'wo' => '!',
825
                    'x' => 'U???',
826
                    'X' => 'U',
827
                    'Y' => 'Y',
828
                    'YY' => 'y',
829
                    'YYYY' => 'Y',
830
                    'YYYYY' => 'Y',
831
                    'YYYYYY' => 'Y',
832
                    'z' => 'e',
833
                    'zz' => 'e',
834
                    'Z' => 'e',
835
                    'ZZ' => 'e',
836
                ];
837
            }
838
 
839
            $format = $replacements[$code] ?? '?';
840
 
841
            if ($format === '!') {
842
                throw new InvalidFormatException("Format $code not supported for creation.");
843
            }
844
 
845
            return $format;
846
        }, $format);
847
 
848
        return static::rawCreateFromFormat($format, $time, $tz);
849
    }
850
 
851
    /**
852
     * Create a Carbon instance from a specific format and a string in a given language.
853
     *
854
     * @param string                         $format Datetime format
855
     * @param string                         $locale
856
     * @param string                         $time
857
     * @param DateTimeZone|string|false|null $tz
858
     *
859
     * @throws InvalidFormatException
860
     *
861
     * @return static|false
862
     */
863
    public static function createFromLocaleFormat($format, $locale, $time, $tz = null)
864
    {
865
        $format = preg_replace_callback(
866
            '/(?:\\\\[a-zA-Z]|[bfkqCEJKQRV]){2,}/',
867
            static function (array $match) use ($locale): string {
868
                $word = str_replace('\\', '', $match[0]);
869
                $translatedWord = static::translateTimeString($word, $locale, 'en');
870
 
871
                return $word === $translatedWord
872
                    ? $match[0]
873
                    : preg_replace('/[a-zA-Z]/', '\\\\$0', $translatedWord);
874
            },
875
            $format
876
        );
877
 
878
        return static::rawCreateFromFormat($format, static::translateTimeString($time, $locale, 'en'), $tz);
879
    }
880
 
881
    /**
882
     * Create a Carbon instance from a specific ISO format and a string in a given language.
883
     *
884
     * @param string                         $format Datetime ISO format
885
     * @param string                         $locale
886
     * @param string                         $time
887
     * @param DateTimeZone|string|false|null $tz
888
     *
889
     * @throws InvalidFormatException
890
     *
891
     * @return static|false
892
     */
893
    public static function createFromLocaleIsoFormat($format, $locale, $time, $tz = null)
894
    {
895
        $time = static::translateTimeString($time, $locale, 'en', CarbonInterface::TRANSLATE_MONTHS | CarbonInterface::TRANSLATE_DAYS | CarbonInterface::TRANSLATE_MERIDIEM);
896
 
897
        return static::createFromIsoFormat($format, $time, $tz, $locale);
898
    }
899
 
900
    /**
901
     * Make a Carbon instance from given variable if possible.
902
     *
903
     * Always return a new instance. Parse only strings and only these likely to be dates (skip intervals
904
     * and recurrences). Throw an exception for invalid format, but otherwise return null.
905
     *
906
     * @param mixed $var
907
     *
908
     * @throws InvalidFormatException
909
     *
910
     * @return static|null
911
     */
912
    public static function make($var)
913
    {
914
        if ($var instanceof DateTimeInterface) {
915
            return static::instance($var);
916
        }
917
 
918
        $date = null;
919
 
920
        if (\is_string($var)) {
921
            $var = trim($var);
922
 
923
            if (!preg_match('/^P[\dT]/', $var) &&
924
                !preg_match('/^R\d/', $var) &&
925
                preg_match('/[a-z\d]/i', $var)
926
            ) {
927
                $date = static::parse($var);
928
            }
929
        }
930
 
931
        return $date;
932
    }
933
 
934
    /**
935
     * Set last errors.
936
     *
937
     * @param array|bool $lastErrors
938
     *
939
     * @return void
940
     */
941
    private static function setLastErrors($lastErrors)
942
    {
943
        if (\is_array($lastErrors) || $lastErrors === false) {
944
            static::$lastErrors = \is_array($lastErrors) ? $lastErrors : [
945
                'warning_count' => 0,
946
                'warnings' => [],
947
                'error_count' => 0,
948
                'errors' => [],
949
            ];
950
        }
951
    }
952
 
953
    /**
954
     * {@inheritdoc}
955
     *
956
     * @return array
957
     */
958
    #[ReturnTypeWillChange]
959
    public static function getLastErrors()
960
    {
961
        return static::$lastErrors;
962
    }
963
}