8000 bug #19541 Fix #19531 [Form] DateType fails parsing when midnight is … · symfony/symfony@b405df0 · GitHub
[go: up one dir, main page]

Skip to content

Commit b405df0

Browse files
committed
bug #19541 Fix #19531 [Form] DateType fails parsing when midnight is not a valid time (mbeccati)
This PR was merged into the 2.7 branch. Discussion ---------- Fix #19531 [Form] DateType fails parsing when midnight is not a valid time | Q | A | ------------- | --- | Branch? | 2.7 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #19531 | License | MIT | Doc PR | Commits ------- c951bb6 Fix #19531 [Form] DateType fails parsing when midnight is not a valid time
2 parents 4d3411b + c951bb6 commit b405df0

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed

src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformer.php

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -117,20 +117,29 @@ public function reverseTransform($value)
117117
return;
118118
}
119119

120-
$timestamp = $this->getIntlDateFormatter()->parse($value);
120+
// date-only patterns require parsing to be done in UTC, as midnight might not exist in the local timezone due
121+
// to DST changes
122+
$dateOnly = $this->isPatternDateOnly();
123+
124+
$timestamp = $this->getIntlDateFormatter($dateOnly)->parse($value);
121125

122126
if (intl_get_error_code() != 0) {
123127
throw new TransformationFailedException(intl_get_error_message());
124128
}
125129

126130
try {
127-
// read timestamp into DateTime object - the formatter delivers in UTC
128-
$dateTime = new \DateTime(sprintf('@%s', $timestamp));
131+
if ($dateOnly) {
132+
// we only care about year-month-date, which has been delivered as a timestamp pointing to UTC midnight
133+
return new \DateTime(gmdate('Y-m-d', $timestamp), new \DateTimeZone($this->inputTimezone));
134+
}
135+
136+
// read timestamp into DateTime object - the formatter delivers a timestamp
137+
$dateTime = new \DateTime(sprintf('@%s', $timestamp), new \DateTimeZone($this->outputTimezone));
129138
} catch (\Exception $e) {
130139
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
131140
}
132141

133-
if ('UTC' !== $this->inputTimezone) {
142+
if ($this->outputTimezone !== $this->inputTimezone) {
134143
$dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
135144
}
136145

@@ -140,15 +149,17 @@ public function reverseTransform($value)
140149
/**
141150
* Returns a preconfigured IntlDateFormatter instance.
142151
*
152+
* @param bool $ignoreTimezone Use UTC regardless of the configured timezone.
153+
*
143154
* @return \IntlDateFormatter
144155
*
145156
* @throws TransformationFailedException in case the date formatter can not be constructed.
146157
*/
147-
protected function getIntlDateFormatter()
158+
protected function getIntlDateFormatter($ignoreTimezone = false)
148159
{
149160
$dateFormat = $this->dateFormat;
150161
$timeFormat = $this->timeFormat;
151-
$timezone = $this->outputTimezone;
162+
$timezone = $ignoreTimezone ? 'UTC' : $this->outputTimezone;
152163
$calendar = $this->calendar;
153164
$pattern = $this->pattern;
154165

@@ -163,4 +174,24 @@ protected function getIntlDateFormatter()
163174

164175
return $intlDateFormatter;
165176
}
177+
178+
/**
179+
* Checks if the pattern contains only a date.
180+
*
181+
* @param string $pattern The input pattern
182+
*
183+
* @return bool
184+
*/
185+
protected function isPatternDateOnly()
186+
{
187+
if (null === $this->pattern) {
188+
return false;
189+
}
190+
191+
// strip escaped text
192+
$pattern = preg_replace("#'(.*?)'#", '', $this->pattern);
193+
194+
// check for the absence of time-related placeholders
195+
return 0 === preg_match('#[ahHkKmsSAzZOvVxX]#', $pattern);
196+
}
166197
}

src/Symfony/Component/Form/Tests/Extension/Core/DataTransformer/DateTimeToLocalizedStringTransformerTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,26 @@ public function testReverseTransformWithDifferentPatterns()
230230
$this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('02*2010*03 04|05|06'));
231231
}
232232

233+
public function testReverseTransformDateOnlyWithDstIssue()
234+
{
235+
$transformer = new DateTimeToLocalizedStringTransformer('Europe/Rome', 'Europe/Rome', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, 'dd/MM/yyyy');
236+
237+
$this->assertDateTimeEquals(
238+
new \DateTime('1978-05-28', new \DateTimeZone('Europe/Rome')),
239+
$transformer->reverseTransform('28/05/1978')
240+
);
241+
}
242+
243+
public function testReverseTransformDateOnlyWithDstIssueAndEscapedText()
244+
{
245+
$transformer = new DateTimeToLocalizedStringTransformer('Europe/Rome', 'Europe/Rome', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, \IntlDateFormatter::GREGORIAN, "'day': dd 'month': MM 'year': yyyy");
246+
247+
$this->assertDateTimeEquals(
248+
new \DateTime('1978-05-28', new \DateTimeZone('Europe/Rome')),
249+
$transformer->reverseTransform('day: 28 month: 05 year: 1978')
250+
);
251+
}
252+
233253
public function testReverseTransformEmpty()
234254
{
235255
$transformer = new DateTimeToLocalizedStringTransformer();

0 commit comments

Comments
 (0)
0