diff --git a/.appveyor.yml b/.appveyor.yml index 17723f34aa873..594fdc8d5c850 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -27,7 +27,7 @@ install: - echo max_execution_time=1200 >> php.ini-min - echo post_max_size=2047M >> php.ini-min - echo upload_max_filesize=2047M >> php.ini-min - - echo date.timezone="America/Los_Angeles" >> php.ini-min + - echo date.timezone="UTC" >> php.ini-min - echo extension_dir=ext >> php.ini-min - echo extension=php_xsl.dll >> php.ini-min - copy /Y php.ini-min php.ini-max diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index b7ea092591d51..52ac9d9e9d4b4 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -82,7 +82,8 @@ public function transform(mixed $dateTime): string throw new TransformationFailedException('Expected a \DateTimeInterface.'); } - $value = $this->getIntlDateFormatter()->format($dateTime->getTimestamp()); + $timestamp = $this->getCalendarConvertingFormatter()->parse($dateTime->format('Y-m-d H:i:s')); + $value = $this->getIntlDateFormatter()->format($timestamp); if (0 != intl_get_error_code()) { throw new TransformationFailedException(intl_get_error_message()); @@ -134,7 +135,7 @@ public function reverseTransform(mixed $value): ?\DateTime try { if ($dateOnly) { // we only care about year-month-date, which has been delivered as a timestamp pointing to UTC midnight - $dateTime = new \DateTime(gmdate('Y-m-d', $timestamp), new \DateTimeZone($this->outputTimezone)); + $dateTime = new \DateTime($this->getCalendarConvertingFormatter()->format($timestamp), new \DateTimeZone($this->outputTimezone)); } else { // read timestamp into DateTime object - the formatter delivers a timestamp $dateTime = new \DateTime(sprintf('@%s', $timestamp)); @@ -196,4 +197,11 @@ protected function isPatternDateOnly(): bool // check for the absence of time-related placeholders return 0 === preg_match('#[ahHkKmsSAzZOvVxX]#', $pattern); } + + private function getCalendarConvertingFormatter(): \IntlDateFormatter + { + $timezone = $this->isPatternDateOnly() ? 'UTC' : $this->inputTimezone; + + return new \IntlDateFormatter(\Locale::getDefault(), \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE, new \DateTimeZone($timezone), \IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd HH:mm:ss'); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php index dfbae933092da..e3fd5618fd815 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/DateTypeTest.php @@ -1160,4 +1160,19 @@ protected function getTestOptions(): array { return ['widget' => 'choice']; } + + public function testSubmitDateBeforeBeginningOfGregorianCalendar() + { + if (\PHP_INT_SIZE < 8) { + $this->markTestSkipped('Parsing three digits years requires a 64bit PHP.'); + } + + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'widget' => 'single_text', + ]); + $form->submit('950-12-19'); + + $this->assertSame('0950-12-19', $form->getData()->format('Y-m-d')); + $this->assertSame('0950-12-19', $form->getViewData()); + } }