8000 [Form] Fixed reverse transformation of values in DateTimeToStringTran… · symfony/symfony@b20c5ca · GitHub
[go: up one dir, main page]

Skip to content

Commit b20c5ca

Browse files
committed
[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.
1 parent 722c19b commit b20c5ca

File tree

5 files changed

+145
-157
lines changed

5 files changed

+145
-157
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@ class DateTimeToLocalizedStringTransformer extends BaseDateTimeTransformer
3232
*
3333
* @see BaseDateTimeTransformer::formats for available format options
3434
*
35-
* @param string $inputTimezone The name of the input timezone
36-
* @param string $outputTimezone The name of the output timezone
37-
* @param integer $dateFormat The date format
38-
* @param integer $timeFormat The time format
39-
* @param \IntlDateFormatter $calendar An \IntlDateFormatter instance
40-
* @param string $pattern A pattern to pass to \IntlDateFormatter
35+
* @param string $inputTimezone The name of the input timezone
36+
* @param string $outputTimezone The name of the output timezone
37+
* @param integer $dateFormat The date format
38+
* @param integer $timeFormat The time format
39+
* @param integer $calendar One of the \IntlDateFormatter calendar constants
40+
* @param string $pattern A pattern to pass to \IntlDateFormatter
4141
*
4242
* @throws UnexpectedTypeException If a format is not supported
4343
* @throws UnexpectedTypeException if a timezone is not a string

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

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,22 @@
2222
*/
2323
class DateTimeToStringTransformer extends BaseDateTimeTransformer
2424
{
25-
private $format;
25+
/**
26+
* Format used for generating strings
27+
* @var string
28+
*/
29+
private $generateFormat;
30+
31+
/**
32+
* Format used for parsing strings
33+
*
34+
* Different than the {@link $generateFormat} because formats for parsing
35+
* support additional characters in PHP that are not supported for
36+
* generating strings.
37+
*
38+
* @var string
39+
*/
40+
private $parseFormat;
2641

2742
/**
2843
* Transforms a \DateTime instance to a string
@@ -39,14 +54,26 @@ public function __construct($inputTimezone = null, $outputTimezone = null, $form
3954
{
4055
parent::__construct($inputTimezone, $outputTimezone);
4156

42-
$this->format = $format;
57+
$this->generateFormat = $this->parseFormat = $format;
58+
59+
// See http://php.net/manual/en/datetime.createfromformat.php
60+
// The character "|" in the format makes sure that the parts of a date
61+
// that are *not* specified in the format are reset to the corresponding
62+
// values from 1970-01-01 00:00:00 instead of the current time.
63+
// Without "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 12:32:47",
64+
// where the time corresponds to the current server time.
65+
// With "|" and "Y-m-d", "2010-02-03" becomes "2010-02-03 00:00:00",
66+
// which is at least deterministic and thus used here.
67+
if (false === strpos($this->parseFormat, '|')) {
68+
$this->parseFormat .= '|';
69+
}
4370
}
4471

4572
/**
4673
* Transforms a DateTime object into a date string with the configured format
4774
* and timezone
4875
*
49-
* @param DateTime $value A DateTime object
76+
* @param \DateTime $value A DateTime object
5077
*
5178
* @return string A value as produced by PHP's date() function
5279
*
@@ -70,15 +97,15 @@ public function transform($value)
7097
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
7198
}
7299

73-
return $value->format($this->format);
100+
return $value->format($this->generateFormat);
74101
}
75102

76103
/**
77104
* Transforms a date string in the configured timezone into a DateTime object.
78105
*
79106
* @param string $value A value as produced by PHP's date() function
80107
*
81-
* @return \DateTime An instance of \DateTime
108+
* @return \DateTime An instance of \DateTime
82109
*
83110
* @throws UnexpectedTypeException if the given value is not a string
84111
* @throws TransformationFailedException if the date could not be parsed
@@ -95,20 +122,25 @@ public function reverseTransform($value)
95122
}
96123

97124
try {
98-
$dateTime = new \DateTime($value, new \DateTimeZone($this->outputTimezone));
125+
$outputTz = new \DateTimeZone($this->outputTimezone);
126+
$dateTime = \DateTime::createFromFormat($this->parseFormat, $value, $outputTz);
127+
99128
$lastErrors = \DateTime::getLastErrors();
100-
if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) {
101-
throw new \UnexpectedValueException(implode(', ', array_merge(array_values($lastErrors['warnings']), array_values($lastErrors['errors']))));
102-
}
103129

104-
// Force value to be in same format as given to transform
105-
if ($value !== $dateTime->format($this->format)) {
106-
$dateTime = new \DateTime($dateTime->format($this->format), new \DateTimeZone($this->outputTimezone));
130+
if (0 < $lastErrors['warning_count'] || 0 < $lastErrors['error_count']) {
131+
throw new TransformationFailedException(
132+
implode(', ', array_merge(
133+
array_values($lastErrors['warnings']),
134+
array_values($lastErrors['errors'])
135+
))
136+
);
107137
}
108138

109139
if ($this->inputTimezone !== $this->outputTimezone) {
110140
$dateTime->setTimeZone(new \DateTimeZone($this->inputTimezone));
111141
}
142+
} catch (TransformationFailedException $e) {
143+
throw $e;
112144
} catch (\Exception $e) {
113145
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
114146
}

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

Lines changed: 69 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ protected function tearDown()
3434
$this->dateTimeWithoutSeconds = null;
3535
}
3636

37-
public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE)
37+
public static function assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false)
3838
{
3939
if ($expected instanceof \DateTime && $actual instanceof \DateTime) {
4040
$expected = $expected->format('c');
@@ -44,54 +44,59 @@ public static function assertEquals($expected, $actual, $message = '', $delta =
4444
parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase);
4545
}
4646

47-
public function testTransformShortDate()
48-
{
49-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::SHORT);
50-
$this->assertEquals('03.02.10 04:05', $transformer->transform($this->dateTime));
51-
}
52-
53-
public function testTransformMediumDate()
54-
{
55-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::MEDIUM);
56-
57-
$this->assertEquals('03.02.2010 04:05', $transformer->transform($this->dateTime));
58-
}
59-
60-
public function testTransformLongDate()
61-
{
62-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::LONG);
63-
64-
$this->assertEquals('03. Februar 2010 04:05', $transformer->transform($this->dateTime));
65-
}
66-
67-
public function testTransformFullDate()
68-
{
69-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL);
70-
71-
$this->assertEquals('Mittwoch, 03. Februar 2010 04:05', $transformer->transform($this->dateTime));
47+
public function dataProvider()
48+
{
49+
return array(
50+
array(\IntlDateFormatter::SHORT, null, null, '03.02.10 04:05', '2010-02-03 04:05:00 UTC'),
51+
array(\IntlDateFormatter::MEDIUM, null, null, '03.02.2010 04:05', '2010-02-03 04:05:00 UTC'),
52+
array(\IntlDateFormatter::LONG, null, null, '03. Februar 2010 04:05', '2010-02-03 04:05:00 UTC'),
53+
array(\IntlDateFormatter::FULL, null, null, 'Mittwoch, 03. Februar 2010 04:05', '2010-02-03 04:05:00 UTC'),
54+
array(\IntlDateFormatter::SHORT, \IntlDateFormatter::NONE, null, '03.02.10', '2010-02-03 00:00:00 UTC'),
55+
array(\IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE, null, '03.02.2010', '2010-02-03 00:00:00 UTC'),
56+
array(\IntlDateFormatter::LONG, \IntlDateFormatter::NONE, null, '03. Februar 2010', '2010-02-03 00:00:00 UTC'),
57+
array(\IntlDateFormatter::FULL, \IntlDateFormatter::NONE, null, 'Mittwoch, 03. Februar 2010', '2010-02-03 00:00:00 UTC'),
58+
array(null, \IntlDateFormatter::SHORT, null, '03.02.2010 04:05', '2010-02-03 04:05:00 UTC'),
59+
array(null, \IntlDateFormatter::MEDIUM, null, '03.02.2010 04:05:06', '2010-02-03 04:05:06 UTC'),
60+
array(null, \IntlDateFormatter::LONG, null,
61+
'03.02.2010 04:05:06 GMT' . ($this->isLowerThanIcuVersion('4.8') ? '+00:00' : ''),
62+
'2010-02-03 04:05:06 UTC'),
63+
// see below for extra test case for time format FULL
64+
array(\IntlDateFormatter::NONE, \IntlDateFormatter::SHORT, null, '04:05', '1970-01-01 04:05:00 UTC'),
65+
array(\IntlDateFormatter::NONE, \IntlDateFormatter::MEDIUM, null, '04:05:06', '1970-01-01 04:05:06 UTC'),
66+
array(\IntlDateFormatter::NONE, \IntlDateFormatter::LONG, null,
67+
'04:05:06 GMT' . ($this->isLowerThanIcuVersion('4.8') ? '+00:00' : ''),
68+
'1970-01-01 04:05:06 UTC'),
69+
array(null, null, 'yyyy-MM-dd HH:mm:00', '2010-02-03 04:05:00', '2010-02-03 04:05:00 UTC'),
70+
array(null, null, 'yyyy-MM-dd HH:mm', '2010-02-03 04:05', '2010-02-03 04:05:00 UTC'),
71+
array(null, null, 'yyyy-MM-dd HH', '2010-02-03 04', '2010-02-03 04:00:00 UTC'),
72+
array(null, null, 'yyyy-MM-dd', '2010-02-03', '2010-02-03 00:00:00 UTC'),
73+
array(null, null, 'yyyy-MM', '2010-02', '2010-02-01 00:00:00 UTC'),
74+
array(null, null, 'yyyy', '2010', '2010-01-01 00:00:00 UTC'),
75+
array(null, null, 'dd-MM-yyyy', '03-02-2010', '2010-02-03 00:00:00 UTC'),
76+
array(null, null, 'HH:mm:ss', '04:05:06', '1970-01-01 04:05:06 UTC'),
77+
array(null, null, 'HH:mm:00', '04:05:00', '1970-01-01 04:05:00 UTC'),
78+
array(null, null, 'HH:mm', '04:05', '1970-01-01 04:05:00 UTC'),
79+
array(null, null, 'HH', '04', '1970-01-01 04:00:00 UTC'),
80+
);
7281
}
7382

74-
public function testTransformShortTime()
75-
{
76-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::SHORT);
77-
78-
$this->assertEquals('03.02.2010 04:05', $transformer->transform($this->dateTime));
79-
}
80-
81-
public function testTransformMediumTime()
82-
{
83-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::MEDIUM);
84-
85-
$this->assertEquals('03.02.2010 04:05:06', $transformer->transform($this->dateTime));
86-
}
87-
88-
public function testTransformLongTime()
83+
/**
84+
* @dataProvider dataProvider
85+
*/
86+
public function testTransform($dateFormat, $timeFormat, $pattern, $output, $input)
8987
{
90-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::LONG);
88+
$transformer = new DateTimeToLocalizedStringTransformer(
89+
'UTC',
90+
'UTC',
91+
$dateFormat,
92+
$timeFormat,
93+
\IntlDateFormatter::GREGORIAN,
94+
$pattern
95+
);
9196

92-
$expected = $this->isLowerThanIcuVersion('4.8') ? '03.02.2010 04:05:06 GMT+00:00' : '03.02.2010 04:05:06 GMT';
97+
$input = new \DateTime($input);
9398

94-
$this->assertEquals($expected, $transformer->transform($this->dateTime));
99+
$this->assertEquals($output, $transformer->transform($input));
95100
}
96101

97102
public function testTransformFullTime()
@@ -143,7 +148,7 @@ public function testTransform_differentPatterns()
143148
}
144149

145150
/**
146-
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
151+
* @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
147152
*/
148153
public function testTransformRequiresValidDateTime()
149154
{
@@ -162,53 +167,23 @@ public function testTransformWrapsIntlErrors()
162167
//$transformer->transform(1.5);
163168
}
164169

165-
public function testReverseTransformShortDate()
166-
{
167-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::SHORT);
168-
169-
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('03.02.10 04:05'));
170-
}
171-
172-
public function testReverseTransformMediumDate()
173-
{
174-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::MEDIUM);
175-
176-
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('03.02.2010 04:05'));
177-
}
178-
179-
public function testReverseTransformLongDate()
180-
{
181-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::LONG);
182-
183-
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('03. Februar 2010 04:05'));
184-
}
185-
186-
public function testReverseTransformFullDate()
187-
{
188-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', \IntlDateFormatter::FULL);
189-
190-
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('Mittwoch, 03. Februar 2010 04:05'));
191-
}
192-
193-
public function testReverseTransformShortTime()
194-
{
195-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::SHORT);
196-
197-
$this->assertDateTimeEquals($this->dateTimeWithoutSeconds, $transformer->reverseTransform('03.02.2010 04:05'));
198-
}
199-
200-
public function testReverseTransformMediumTime()
170+
/**
171+
* @dataProvider dataProvider
172+
*/
173+
public function testReverseTransform($dateFormat, $timeFormat, $pattern, $input, $output)
201174
{
202-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::MEDIUM);
203-
204-
$this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('03.02.2010 04:05:06'));
205-
}
175+
$transformer = new DateTimeToLocalizedStringTransformer(
176+
'UTC',
177+
'UTC',
178+
$dateFormat,
179+
$timeFormat,
180+
\IntlDateFormatter::GREGORIAN,
181+
$pattern
182+
);
206183

207-
public function testReverseTransformLongTime()
208-
{
209-
$transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::LONG);
184+
$output = new \DateTime($output);
210185

211-
$this->assertDateTimeEquals($this->dateTime, $transformer->reverseTransform('03.02.2010 04:05:06 GMT+00:00'));
186+
$this->assertEquals($output, $transformer->reverseTransform($input));
212187
}
213188

214189
public function testReverseTransformFullTime()
@@ -256,7 +231,7 @@ public function testReverseTransform_empty()
256231
}
257232

258233
/**
259-
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
234+
* @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
260235
*/
261236
public function testReverseTransformRequiresString()
262237
{
@@ -265,7 +240,7 @@ public function testReverseTransformRequiresString()
265240
}
266241

267242
/**
268-
* @expectedException Symfony\Component\Form\Exception\TransformationFailedException
243+
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
269244
*/
270245
public function testReverseTransformWrapsIntlErrors()
271246
{
@@ -274,23 +249,23 @@ public function testReverseTransformWrapsIntlErrors()
274249
}
275250

276251
/**
277-
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
252+
* @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
278253
*/
279254
public function testValidateDateFormatOption()
280255
{
281256
new DateTimeToLocalizedStringTransformer(null, null, 'foobar');
282257
}
283258

284259
/**
285-
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
260+
* @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
286261
*/
287262
public function testValidateTimeFormatOption()
288263
{
289264
new DateTimeToLocalizedStringTransformer(null, null, null, 'foobar');
290265
}
291266

292267
/**
293-
* @expectedException Symfony\Component\Form\Exception\TransformationFailedException
268+
* @expectedException \Symfony\Component\Form\Exception\TransformationFailedException
294269
*/
295270
public function testReverseTransformWithNonExistingDate()
296271
{

0 commit comments

Comments
 (0)
0