Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7068cfe

Browse files
committed
use a reference date to handle times during DST
1 parent 5db58f6 commit 7068cfe

File tree

7 files changed

+128
-11
lines changed

7 files changed

+128
-11
lines changed

UPGRADE-4.4.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ Filesystem
7272
Form
7373
----
7474

75+
* Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is
76+
deprecated.
7577
* Using `int` or `float` as data for the `NumberType` when the `input` option is set to `string` is deprecated.
7678

7779
FrameworkBundle

UPGRADE-5.0.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ Finder
152152
Form
153153
----
154154

155+
* Removed support for using different values for the "model_timezone" and "view_timezone" options without configuring
156+
a reference date.
155157
* Removed support for using `int` or `float` as data for the `NumberType` when the `input` option is set to `string`.
156158
* Removed support for using the `format` option of `DateType` and `DateTimeType` when the `html5` option is enabled.
157159
* Using names for buttons that do not start with a letter, a digit, or an underscore leads to an exception.

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ CHANGELOG
44
4.4.0
55
-----
66

7+
* using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is
8+
deprecated
79
* preferred choices are repeated in the list of all choices
810
* deprecated using `int` or `float` as data for the `NumberType` when the `input` option is set to `string`
911
* The type guesser guesses the HTML accept attribute when a mime type is configured in the File or Image constraint.

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer
2424
private $pad;
2525

2626
private $fields;
27+
private $referenceDate;
2728

2829
/**
2930
* @param string $inputTimezone The input timezone
3031
* @param string $outputTimezone The output timezone
3132
* @param array $fields The date fields
3233
* @param bool $pad Whether to use padding
3334
*/
34-
public function __construct(string $inputTimezone = null, string $outputTimezone = null, array $fields = null, bool $pad = false)
35+
public function __construct(string $inputTimezone = null, string $outputTimezone = null, array $fields = null, bool $pad = false, \DateTimeInterface $referenceDate = null)
3536
{
3637
parent::__construct($inputTimezone, $outputTimezone);
3738

@@ -41,6 +42,7 @@ public function __construct(string $inputTimezone = null, string $outputTimezone
4142

4243
$this->fields = $fields;
4344
$this->pad = $pad;
45+
$this->referenceDate = $referenceDate ?: new \DateTime('1970-01-01 00:00:00');
4446
}
4547

4648
/**
@@ -165,12 +167,12 @@ public function reverseTransform($value)
165167
try {
166168
$dateTime = new \DateTime(sprintf(
167169
'%s-%s-%s %s:%s:%s',
168-
empty($value['year']) ? '1970' : $value['year'],
169-
empty($value['month']) ? '1' : $value['month'],
170-
empty($value['day']) ? '1' : $value['day'],
171-
empty($value['hour']) ? '0' : $value['hour'],
172-
empty($value['minute']) ? '0' : $value['minute'],
173-
empty($value['second']) ? '0' : $value['second']
170+
empty($value['year']) ? $this->referenceDate->format('Y') : $value['year'],
171+
empty($value['month']) ? $this->referenceDate->format('m') : $value['month'],
172+
empty($value['day']) ? $this->referenceDate->format('d') : $value['day'],
173+
empty($value['hour']) ? $this->referenceDate->format('H') : $value['hour'],
174+
empty($value['minute']) ? $this->referenceDate->format('i') : $value['minute'],
175+
empty($value['second']) ? $this->referenceDate->format('s') : $value['second']
174176
),
175177
new \DateTimeZone($this->outputTimezone)
176178
);

src/Symfony/Component/Form/Extension/Core/Type/TimeType.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ public function buildForm(FormBuilderInterface $builder, array $options)
5656
}
5757

5858
if ('single_text' === $options['widget']) {
59-
$builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format));
60-
6159
// handle seconds ignored by user's browser when with_seconds enabled
6260
// https://codereview.chromium.org/450533009/
6361
if ($options['with_seconds']) {
@@ -68,6 +66,20 @@ public function buildForm(FormBuilderInterface $builder, array $options)
6866
}
6967
});
7068
}
69+
70+
if (null !== $options['reference_date']) {
71+
$format = 'Y-m-d '.$format;
72+
73+
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) {
74+
$data = $event->getData();
75+
76+
if (preg_match('/^\d{2}:\d{2}(:\d{2})?$/', $data)) {
77+
$event->setData($options['reference_date']->format('Y-m-d ').$data);
78+
}
79+
});
80+
}
81+
82+
$builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format));
7183
} else {
7284
$hourOptions = $minuteOptions = $secondOptions = [
7385
'error_bubbling' => true,
@@ -157,7 +169,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
157169
$builder->add('second', self::$widgets[$options['widget']], $secondOptions);
158170
}
159171

160-
$builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget']));
172+
$builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'], $options['reference_date']));
161173
}
162174

163175
if ('datetime_immutable' === $options['input']) {
@@ -262,6 +274,7 @@ public function configureOptions(OptionsResolver $resolver)
262274
'with_seconds' => false,
263275
'model_timezone' => null,
264276
'view_timezone' => null,
277+
'reference_date' => null,
265278
'placeholder' => $placeholderDefault,
266279
'html5' => true,
267280
// Don't modify \DateTime classes by reference, we treat
@@ -280,6 +293,14 @@ public function configureOptions(OptionsResolver $resolver)
280293
'choice_translation_domain' => false,
281294
]);
282295

296+
$resolver->setDeprecated('model_timezone', function (Options $options, $modelTimezone): string {
297+
if (null !== $modelTimezone && $options['view_timezone'] !== $modelTimezone && null === $options['reference_date']) {
298+
return sprintf('Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is deprecated since Symfony 4.4.');
299+
}
300+
301+
return '';
302+
});
303+
283304
$resolver->setNormalizer('placeholder', $placeholderNormalizer);
284305
$resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer);
285306

@@ -300,6 +321,7 @@ public function configureOptions(OptionsResolver $resolver)
300321
$resolver->setAllowedTypes('minutes', 'array');
301322
$resolver->setAllowedTypes('seconds', 'array');
302323
$resolver->setAllowedTypes('input_format', 'string');
324+
$resolver->setAllowedTypes('reference_date', ['null', \DateTimeInterface::class]);
303325
}
304326

305327
/**

src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public function testDebugDeprecatedDefaults()
4545
Built-in form types (Symfony\Component\Form\Extension\Core\Type)
4646
----------------------------------------------------------------
4747
48-
BirthdayType, DateTimeType, DateType, IntegerType, TimezoneType
48+
BirthdayType, DateTimeType, DateType, IntegerType, TimeType
49+
TimezoneType
4950
5051
Service form types
5152
------------------

src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,57 @@ public function testSubmitWithSecondsAndBrowserOmissionSeconds()
276276
$this->assertEquals('03:04:00', $form->getViewData());
277277
}
278278

279+
public function testSubmitDifferentTimezones()
280+
{
281+
$form = $this->factory->create(static::TESTED_TYPE, null, [
282+
'model_timezone' => 'UTC',
283+
'view_timezone' => 'Europe/Berlin',
284+
'input' => 'datetime',
285+
'with_seconds' => true,
286+
'reference_date' => new \DateTime('2019-01-01', new \DateTimeZone('UTC')),
287+
]);
288+
$form->submit([
289+
'hour' => '16',
290+
'minute' => '9',
291+
'second' => '10',
292+
]);
293+
294+
$this->assertSame('15:09:10', $form->getData()->format('H:i:s'));
295+
}
296+
297+
public function testSubmitDifferentTimezonesDuringDaylightSavingTime()
298+
{
299+
$form = $this->factory->create(static::TESTED_TYPE, null, [
300+
'model_timezone' => 'UTC',
301+
'view_timezone' => 'Europe/Berlin',
302+
'input' => 'datetime',
303+
'with_seconds' => true,
304+
'reference_date' => new \DateTime('2019-07-12', new \DateTimeZone('UTC')),
305+
]);
306+
$form->submit([
307+
'hour' => '16',
308+
'minute' => '9',
309+
'second' => '10',
310+
]);
311+
312+
$this->assertSame('14:09:10', $form->getData()->format('H:i:s'));
313+
}
314+
315+
public function testSubmitDifferentTimezonesDuringDaylightSavingTimeUsingSingleTextWidget()
316+
{
317+
$form = $this->factory->create(static::TESTED_TYPE, null, [
318+
'model_timezone' => 'UTC',
319+
'view_timezone' => 'Europe/Berlin',
320+
'input' => 'datetime',
321+
'with_seconds' => true,
322+
'reference_date' => new \DateTime('2019-07-12', new \DateTimeZone('UTC')),
323+
'widget' => 'single_text',
324+
]);
325+
$form->submit('16:09:10');
326+
327+
$this->assertSame('14:09:10', $form->getData()->format('H:i:s'));
328+
}
329+
279330
public function testSetDataWithoutMinutes()
280331
{
281332
$form = $this->factory->create(static::TESTED_TYPE, null, [
@@ -311,6 +362,7 @@ public function testSetDataDifferentTimezones()
311362
'view_timezone' => 'Asia/Hong_Kong',
312363
'input' => 'string',
313364
'with_seconds' => true,
365+
'reference_date' => new \DateTime('2013-01-01 00:00:00', new \DateTimeZone('America/New_York')),
314366
]);
315367

316368
$dateTime = new \DateTime('2013-01-01 12:04:05');
@@ -337,6 +389,7 @@ public function testSetDataDifferentTimezonesDateTime()
337389
'view_timezone' => 'Asia/Hong_Kong',
338390
'input' => 'datetime',
339391
'with_seconds' => true,
392+
'reference_date' => new \DateTime('now', new \DateTimeZone('America/New_York')),
340393
]);
341394

342395
$dateTime = new \DateTime('12:04:05');
@@ -357,6 +410,39 @@ public function testSetDataDifferentTimezonesDateTime()
357410
$this->assertEquals($displayedData, $form->getViewData());
358411
}
359412

413+
public function testSetDataDifferentTimezonesDuringDaylightSavingTime()
414+
{
415+
$form = $this->factory->create(static::TESTED_TYPE, null, [
416+
'model_timezone' => 'UTC',
417+
'view_timezone' => 'Europe/Berlin',
418+
'input' => 'datetime',
419+
'with_seconds' => true,
420+
'reference_date' => new \DateTime('2019-07-12', new \DateTimeZone('UTC')),
421+
]);
422+
423+
$form->setData(new \DateTime('2019-07-24 14:09:10', new \DateTimeZone('UTC')));
424+
425+
$this->assertSame(['hour' => '16', 'minute' => '9', 'second' => '10'], $form->getViewData());
426+
}
427+
428+
/**
429+
* @group legacy
430+
* @expectedDeprecation Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is deprecated since Symfony 4.4.
431+
*/
432+
public function testSetDataDifferentTimezonesWithoutReferenceDate()
433+
{
434+
$form = $this->factory->create(static::TESTED_TYPE, null, [
435+
'model_timezone' => 'UTC',
436+
'view_timezone' => 'Europe/Berlin',
437+
'input' => 'datetime',
438+
'with_seconds' => true,
439+
]);
440+
441+
$form->setData(new \DateTime('2019-07-24 14:09:10', new \DateTimeZone('UTC')));
442+
443+
$this->assertSame(['hour' => '16', 'minute' => '9', 'second' => '10'], $form->getViewData());
444+
}
445+
360446
public function testHoursOption()
361447
{
362448
$form = $this->factory->create(static::TESTED_TYPE, null, [

0 commit comments

Comments
 (0)