[Form] Fixed reverse transformation of values in DateTimeToStringTransformer

The parts not given in the format are reset to the corresponding values of
the UNIX base timestamp. For example, when parsing with the format "Y-m-d",
parsing

    "2012-05-18"

now results in the date

    "2012-05-18 00:00:00 UTC"

instead of

    "2012-05-18 12:58:27 UTC"

as before, where the time part corresponded to the local server time.

Another example: When parsing with the format "H:i:s", parsing

    "12:58:27"

now results in

    "1970-01-01 12:58:27 UTC"

instead of

    "2012-12-13 12:58:27 UTC"

as before, where again the date part corresponded to the local server time.

This behavior is now consistent with DateTimeToArrayTransformer and
DateTimeToLocalizedStringTransformer.
This commit is contained in:
Bernhard Schussek 2012-12-13 18:32:34 +01:00
parent 722c19bca5
commit b20c5ca2ef
5 changed files with 145 additions and 157 deletions

View File

@ -32,12 +32,12 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
*
* @see BaseDateTimeTransformer::formats for available format options
*
* @param string $inputTimezone The name of the input timezone
* @param string $outputTimezone The name of the output timezone
* @param integer $dateFormat The date format
* @param integer $timeFormat The time format
* @param \IntlDateFormatter $calendar An \IntlDateFormatter instance
* @param string $pattern A pattern to pass to \IntlDateFormatter
* @param string $inputTimezone The name of the input timezone
* @param string $outputTimezone The name of the output timezone
* @param integer $dateFormat The date format
* @param integer $timeFormat The time format
* @param integer $calendar One of the \IntlDateFormatter calendar constants
* @param string $pattern A pattern to pass to \IntlDateFormatter
*
* @throws UnexpectedTypeException If a format is not supported
* @throws UnexpectedTypeException if a timezone is not a string

View File

@ -22,7 +22,22 @@ use Symfony\Component\Form\Exception\UnexpectedTypeException;
*/
class DateTimeToStringTransformer extends BaseDateTimeTransformer
{
private $format;
/**
* Format used for generating strings
* @var string
*/
private $generateFormat;
/**
* Format used for parsing strings
*
* Different than the {@link $generateFormat} because formats for parsing
* support additional characters in PHP that are not supported for
* generating strings.
*
* @var string
*/
private $parseFormat;
/**
* Transforms a \DateTime instance to a string
@ -39,14 +54,26 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer
{
parent::__construct($inputTimezone, $outputTimezone);
$this->format = $format;
$this->generateFormat = $this->parseFormat = $format;
// See http://php.net/manual/en/datetime.createfromformat.php
// The character "|" in the format makes sure that the parts of a date
// that are *not* specified in the format are reset to the corresponding
// values from 1970-01-01 00:00:00 instead of the current time.
// Without "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 12:32:47",
// where the time corresponds to the current server time.
// With "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 00:00:00",
// which is at least deterministic and thus used here.
if (false === strpos($this->parseFormat, '|')) {
$this->parseFormat .= '|';
}
}
/**
* Transforms a DateTime object into a date string with the configured format
* and timezone
*
* @param DateTime $value A DateTime object
* @param \DateTime $value A DateTime object
*
* @return string A value as produced by PHP's date() function
*
@ -70,7 +97,7 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
}
return $value->format($this->format);
return $value->format($this->generateFormat);
}
/**
@ -78,7 +105,7 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer
*
* @param string $value A value as produced by PHP's date() function
*
* @return \DateTime An instance of \DateTime
* @return \DateTime An instance of \DateTime
*
* @throws UnexpectedTypeException if the given value is not a string
* @throws TransformationFailedException if the date could not be parsed
@ -95,20 +122,25 @@ class DateTimeToStringTransformer extends BaseDateTimeTransformer
}
try {
$dateTime = new \DateTime($value, new \DateTimeZone($this->outputTimezone));
$lastErrors = \DateTime::getLastErrors();
if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) {
throw new \UnexpectedValueException(implode(', ', array_merge(array_values($lastErrors['warnings']), array_values($lastErrors['errors']))));
}
$outputTz = new \DateTimeZone($this->outputTimezone);
$dateTime = \DateTime::createFromFormat($this->parseFormat, $value, $outputTz);
// Force value to be in same format as given to transform
if ($value !== $dateTime->format($this->format)) {
$dateTime = new \DateTime($dateTime->format($this->format), new \DateTimeZone($this->outputTimezone));
$lastErrors = \DateTime::getLastErrors();
if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) {
throw new TransformationFailedException(
implode(', ', array_merge(
array_values($lastErrors['warnings']),
array_values($lastErrors['errors'])
))
);
}
if ($this->inputTimezone !== $this->outputTimezone) {
$dateTime->setTimeZone(new \DateTimeZone($this->inputTimezone));
}
} catch (TransformationFailedException $e) {
throw $e;
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
}

View File

@ -34,7 +34,7 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
$this->dateTimeWithoutSeconds = null;
}
public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE)
public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
{
if ($expected instanceof \DateTime && $actual instanceof \DateTime) {
$expected = $expected->format('c');
@ -44,54 +44,59 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase);
}
public function testTransformShortDate()
public function dataProvider()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::SHORT);
$this->assertEquals('03.02.10 04:05', $transformer->transform($this->dateTime));
return array(
array(\IntlDateFormatter::SHORT, null, null, '03.02.10 04:05', '2010-02-03 04:05:00 UTC'),
array(\IntlDateFormatter::MEDIUM, null, null, '03.02.2010 04:05', '2010-02-03 04:05:00 UTC'),
array(\IntlDateFormatter::LONG, null, null, '03. Februar 2010 04:05', '2010-02-03 04:05:00 UTC'),
array(\IntlDateFormatter::FULL, null, null, 'Mittwoch, 03. Februar 2010 04:05', '2010-02-03 04:05:00 UTC'),
array(\IntlDateFormatter::SHORT, \IntlDateFormatter::NONE, null, '03.02.10', '2010-02-03 00:00:00 UTC'),
array(\IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE, null, '03.02.2010', '2010-02-03 00:00:00 UTC'),
array(\IntlDateFormatter::LONG, \IntlDateFormatter::NONE, null, '03. Februar 2010', '2010-02-03 00:00:00 UTC'),
array(\IntlDateFormatter::FULL, \IntlDateFormatter::NONE, null, 'Mittwoch, 03. Februar 2010', '2010-02-03 00:00:00 UTC'),
array(null, \IntlDateFormatter::SHORT, null, '03.02.2010 04:05', '2010-02-03 04:05:00 UTC'),
array(null, \IntlDateFormatter::MEDIUM, null, '03.02.2010 04:05:06', '2010-02-03 04:05:06 UTC'),
array(null, \IntlDateFormatter::LONG, null,
'03.02.2010 04:05:06 GMT' . ($this->isLowerThanIcuVersion('4.8') ? '+00:00' : ''),
'2010-02-03 04:05:06 UTC'),
// see below for extra test case for time format FULL
array(\IntlDateFormatter::NONE, \IntlDateFormatter::SHORT, null, '04:05', '1970-01-01 04:05:00 UTC'),
array(\IntlDateFormatter::NONE, \IntlDateFormatter::MEDIUM, null, '04:05:06', '1970-01-01 04:05:06 UTC'),
array(\IntlDateFormatter::NONE, \IntlDateFormatter::LONG, null,
'04:05:06 GMT' . ($this->isLowerThanIcuVersion('4.8') ? '+00:00' : ''),
'1970-01-01 04:05:06 UTC'),
array(null, null, 'yyyy-MM-dd HH:mm:00', '2010-02-03 04:05:00', '2010-02-03 04:05:00 UTC'),
array(null, null, 'yyyy-MM-dd HH:mm', '2010-02-03 04:05', '2010-02-03 04:05:00 UTC'),
array(null, null, 'yyyy-MM-dd HH', '2010-02-03 04', '2010-02-03 04:00:00 UTC'),
array(null, null, 'yyyy-MM-dd', '2010-02-03', '2010-02-03 00:00:00 UTC'),
array(null, null, 'yyyy-MM', '2010-02', '2010-02-01 00:00:00 UTC'),
array(null, null, 'yyyy', '2010', '2010-01-01 00:00:00 UTC'),
array(null, null, 'dd-MM-yyyy', '03-02-2010', '2010-02-03 00:00:00 UTC'),
array(null, null, 'HH:mm:ss', '04:05:06', '1970-01-01 04:05:06 UTC'),
array(null, null, 'HH:mm:00', '04:05:00', '1970-01-01 04:05:00 UTC'),
array(null, null, 'HH:mm', '04:05', '1970-01-01 04:05:00 UTC'),
array(null, null, 'HH', '04', '1970-01-01 04:00:00 UTC'),
);
}
public function testTransformMediumDate()
/**
* @dataProvider dataProvider
*/
public function testTransform($dateFormat, $timeFormat, $pattern, $output, $input)
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::MEDIUM);
$transformer = new DateTimeToLocalizedStringTransformer(
'UTC',
'UTC',
$dateFormat,
$timeFormat,
\IntlDateFormatter::GREGORIAN,
$pattern
);
$this->assertEquals('03.02.2010 04:05', $transformer->transform($this->dateTime));
}
$input = new \DateTime($input);
public function testTransformLongDate()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::LONG);
$this->assertEquals('03. Februar 2010 04:05', $transformer->transform($this->dateTime));
}
public function testTransformFullDate()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL);
$this->assertEquals('Mittwoch, 03. Februar 2010 04:05', $transformer->transform($this->dateTime));
}
public function testTransformShortTime()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::SHORT);
$this->assertEquals('03.02.2010 04:05', $transformer->transform($this->dateTime));
}
public function testTransformMediumTime()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::MEDIUM);
$this->assertEquals('03.02.2010 04:05:06', $transformer->transform($this->dateTime));
}
public function testTransformLongTime()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::LONG);
$expected = $this->isLowerThanIcuVersion('4.8') ? '03.02.2010 04:05:06 GMT+00:00' : '03.02.2010 04:05:06 GMT';
$this->assertEquals($expected, $transformer->transform($this->dateTime));
$this->assertEquals($output, $transformer->transform($input));
}
public function testTransformFullTime()
@ -143,7 +148,7 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
}
/**
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
* @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
*/
public function testTransformRequiresValidDateTime()
{
@ -162,53 +167,23 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
//$transformer->transform(1.5);
}
public function testReverseTransformShortDate()
/**
* @dataProvider dataProvider
*/
public function testReverseTransform($dateFormat, $timeFormat, $pattern, $input, $output)
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::SHORT);
$transformer = new DateTimeToLocalizedStringTransformer(
'UTC',
'UTC',
$dateFormat,
$timeFormat,
\IntlDateFormatter::GREGORIAN,
$pattern
);
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('03.02.10 04:05'));
}
$output = new \DateTime($output);
public function testReverseTransformMediumDate()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::MEDIUM);
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('03.02.2010 04:05'));
}
public function testReverseTransformLongDate()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::LONG);
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('03. Februar 2010 04:05'));
}
public function testReverseTransformFullDate()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL);
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('Mittwoch, 03. Februar 2010 04:05'));
}
public function testReverseTransformShortTime()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::SHORT);
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('03.02.2010 04:05'));
}
public function testReverseTransformMediumTime()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::MEDIUM);
$this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('03.02.2010 04:05:06'));
}
public function testReverseTransformLongTime()
{
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::LONG);
$this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('03.02.2010 04:05:06 GMT+00:00'));
$this->assertEquals($output, $transformer->reverseTransform($input));
}
public function testReverseTransformFullTime()
@ -256,7 +231,7 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
}
/**
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
* @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
*/
public function testReverseTransformRequiresString()
{
@ -265,7 +240,7 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
}
/**
* @expectedException Symfony\Component\Form\Exception\TransformationFailedException
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testReverseTransformWrapsIntlErrors()
{
@ -274,7 +249,7 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
}
/**
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
* @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
*/
public function testValidateDateFormatOption()
{
@ -282,7 +257,7 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
}
/**
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
* @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
*/
public function testValidateTimeFormatOption()
{
@ -290,7 +265,7 @@ class DateTimeToLocalizedStringTransformerTest extends DateTimeTestCase
}
/**
* @expectedException Symfony\Component\Form\Exception\TransformationFailedException
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testReverseTransformWithNonExistingDate()
{

View File

@ -15,30 +15,34 @@ use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransf
class DateTimeToStringTransformerTest extends DateTimeTestCase
{
public function testTransform()
public function dataProvider()
{
$transformer = new DateTimeToStringTransformer('UTC', 'UTC', 'Y-m-d H:i:s');
$input = new \DateTime('2010-02-03 04:05:06 UTC');
$output = clone $input;
$output->setTimezone(new \DateTimeZone('UTC'));
$output = $output->format('Y-m-d H:i:s');
$this->assertEquals($output, $transformer->transform($input));
return array(
array('Y-m-d H:i:s', '2010-02-03 04:05:06', '2010-02-03 04:05:06 UTC'),
array('Y-m-d H:i:00', '2010-02-03 04:05:00', '2010-02-03 04:05:00 UTC'),
array('Y-m-d H:i', '2010-02-03 04:05', '2010-02-03 04:05:00 UTC'),
array('Y-m-d H', '2010-02-03 04', '2010-02-03 04:00:00 UTC'),
array('Y-m-d', '2010-02-03', '2010-02-03 00:00:00 UTC'),
array('Y-m', '2010-02', '2010-02-01 00:00:00 UTC'),
array('Y', '2010', '2010-01-01 00:00:00 UTC'),
array('d-m-Y', '03-02-2010', '2010-02-03 00:00:00 UTC'),
array('H:i:s', '04:05:06', '1970-01-01 04:05:06 UTC'),
array('H:i:00', '04:05:00', '1970-01-01 04:05:00 UTC'),
array('H:i', '04:05', '1970-01-01 04:05:00 UTC'),
array('H', '04', '1970-01-01 04:00:00 UTC'),
);
}
/**
* @dataProvider getFormatAndDateTime
* @dataProvider dataProvider
*/
public function testTransformRandomFormat($format, $datetime)
public function testTransform($format, $output, $input)
{
$transformer = new DateTimeToStringTransformer('UTC', 'UTC', $format);
$input = new \DateTime($datetime);
$output = clone $input;
$output->setTimezone(new \DateTimeZone('UTC'));
$input = new \DateTime($input);
$this->assertEquals($output->format($format), $transformer->transform($input));
$this->assertEquals($output, $transformer->transform($input));
}
public function testTransform_empty()
@ -68,39 +72,16 @@ class DateTimeToStringTransformerTest extends DateTimeTestCase
$transformer->transform('1234');
}
public function testReverseTransform()
{
$reverseTransformer = new DateTimeToStringTransformer('UTC', 'UTC', 'Y-m-d H:i:s');
$output = new \DateTime('2010-02-03 04:05:06 UTC');
$input = $output->format('Y-m-d H:i:s');
$this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input));
}
/**
* @dataProvider getFormatAndDateTime
* @dataProvider dataProvider
*/
public function testReverseTransformRandomFormat($format, $datetime)
public function testReverseTransform($format, $input, $output)
{
$reverseTransformer = new DateTimeToStringTransformer('UTC', 'UTC', $format);
$dateTime = new \DateTime($datetime);
$input = $dateTime->format($format);
$output = new \DateTime($output);
$this->assertDateTimeEquals($dateTime, $reverseTransformer->reverseTransform($input));
}
public function getFormatAndDateTime()
{
return array(
array('Y-m-d H:i:s', '2010-02-03 04:05:06 UTC'),
array('Y-m-d H:i:00', '2010-02-03 04:05:00 UTC'),
array('Y-m-d', '2010-02-03 UTC'),
array('d-m-Y', '03-02-2010 UTC'),
array('H:i:s', '04:05:06 UTC'),
array('H:i:00', '04:05:00 UTC'),
);
$this->assertDateTimeEquals($output, $reverseTransformer->reverseTransform($input));
}
public function testReverseTransform_empty()

View File

@ -105,9 +105,9 @@ class TimeTypeTest extends LocalizedTestCase
'widget' => 'single_text',
));
$form->bind('03:04:05');
$form->bind('03:04');
$this->assertEquals(new \DateTime('03:04:00 UTC'), $form->getData());
$this->assertEquals(new \DateTime('1970-01-01 03:04:00 UTC'), $form->getData());
$this->assertEquals('03:04', $form->getViewData());
}
@ -162,7 +162,7 @@ class TimeTypeTest extends LocalizedTestCase
'widget' => 'single_text',
));
$form->bind('03:04:05');
$form->bind('03:04');
$this->assertEquals('03:04:00', $form->getData());
$this->assertEquals('03:04', $form->getViewData());