diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php index cac50e16c7a0a..352b7c85ff506 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php @@ -117,20 +117,29 @@ public function reverseTransform($value) return; } - $timestamp = $this->getIntlDateFormatter()->parse($value); + // date-only patterns require parsing to be done in UTC, as midnight might not exist in the local timezone due + // to DST changes + $dateOnly = $this->isPatternDateOnly(); + + $timestamp = $this->getIntlDateFormatter($dateOnly)->parse($value); if (intl_get_error_code() != 0) { throw new TransformationFailedException(intl_get_error_message()); } try { - // read timestamp into DateTime object - the formatter delivers in UTC - $dateTime = new \DateTime(sprintf('@%s', $timestamp)); + if ($dateOnly) { + // we only care about year-month-date, which has been delivered as a timestamp pointing to UTC midnight + return new \DateTime(gmdate('Y-m-d', $timestamp), new \DateTimeZone($this->inputTimezone)); + } + + // read timestamp into DateTime object - the formatter delivers a timestamp + $dateTime = new \DateTime(sprintf('@%s', $timestamp), new \DateTimeZone($this->outputTimezone)); } catch (\Exception $e) { throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e); } - if ('UTC' !== $this->inputTimezone) { + if ($this->outputTimezone !== $this->inputTimezone) { $dateTime->setTimezone(new \DateTimeZone($this->inputTimezone)); } @@ -140,15 +149,17 @@ public function reverseTransform($value) /** * Returns a preconfigured IntlDateFormatter instance. * + * @param bool $ignoreTimezone Use UTC regardless of the configured timezone. + * * @return \IntlDateFormatter * * @throws TransformationFailedException in case the date formatter can not be constructed. */ - protected function getIntlDateFormatter() + protected function getIntlDateFormatter($ignoreTimezone = false) { $dateFormat = $this->dateFormat; $timeFormat = $this->timeFormat; - $timezone = $this->outputTimezone; + $timezone = $ignoreTimezone ? 'UTC' : $this->outputTimezone; $calendar = $this->calendar; $pattern = $this->pattern; @@ -163,4 +174,24 @@ protected function getIntlDateFormatter() return $intlDateFormatter; } + + /** + * Checks if the pattern contains only a date. + * + * @param string $pattern The input pattern + * + * @return bool + */ + protected function isPatternDateOnly() + { + if (null === $this->pattern) { + return false; + } + + // strip escaped text + $pattern = preg_replace("#'(.*?)'#", '', $this->pattern); + + // check for the absence of time-related placeholders + return 0 === preg_match('#[ahHkKmsSAzZOvVxX]#', $pattern); + } } diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php index 96dd4a3cd1555..444cdd4582330 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php @@ -230,6 +230,26 @@ public function testReverseTransformWithDifferentPatterns() $this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('02*2010*03 04|05|06')); } + public function testReverseTransformDateOnlyWithDstIssue() + { + $transformer = new DateTimeToLocalizedStringTransformer('Europe/Rome', 'Europe/Rome', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'dd/MM/yyyy'); + + $this->assertDateTimeEquals( + new \DateTime('1978-05-28', new \DateTimeZone('Europe/Rome')), + $transformer->reverseTransform('28/05/1978') + ); + } + + public function testReverseTransformDateOnlyWithDstIssueAndEscapedText() + { + $transformer = new DateTimeToLocalizedStringTransformer('Europe/Rome', 'Europe/Rome', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, "'day': dd 'month': MM 'year': yyyy"); + + $this->assertDateTimeEquals( + new \DateTime('1978-05-28', new \DateTimeZone('Europe/Rome')), + $transformer->reverseTransform('day: 28 month: 05 year: 1978') + ); + } + public function testReverseTransformEmpty() { $transformer = new DateTimeToLocalizedStringTransformer();
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: