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