Fix #19531 [Form] DateType fails parsing when midnight is not a valid time
This commit is contained in:
parent
aae8e3da57
commit
c951bb6e97
@ -117,20 +117,29 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||
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 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||
/**
|
||||
* 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 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -230,6 +230,26 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
|
||||
$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();
|
||||
|
Reference in New Issue
Block a user