diff --git a/UPGRADE-4.4.md b/UPGRADE-4.4.md index 416603f3df84..93854c80739e 100644 --- a/UPGRADE-4.4.md +++ b/UPGRADE-4.4.md @@ -72,6 +72,8 @@ Filesystem Form ---- + * Using different values for the "model_timezone" and "view_timezone" options of the `TimeType` without configuring a + reference date is deprecated. * Using `int` or `float` as data for the `NumberType` when the `input` option is set to `string` is deprecated. FrameworkBundle diff --git a/UPGRADE-5.0.md b/UPGRADE-5.0.md index a02d2e302e06..7ae6f5d7f578 100644 --- a/UPGRADE-5.0.md +++ b/UPGRADE-5.0.md @@ -152,6 +152,8 @@ Finder Form ---- + * Removed support for using different values for the "model_timezone" and "view_timezone" options of the `TimeType` + without configuring a reference date. * Removed support for using `int` or `float` as data for the `NumberType` when the `input` option is set to `string`. * Removed support for using the `format` option of `DateType` and `DateTimeType` when the `html5` option is enabled. * Using names for buttons that do not start with a letter, a digit, or an underscore leads to an exception. diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index f6bd9c35d5a1..122a37e89fc8 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -4,6 +4,8 @@ CHANGELOG 4.4.0 ----- + * using different values for the "model_timezone" and "view_timezone" options of the `TimeType` without configuring a + reference date is deprecated * preferred choices are repeated in the list of all choices * deprecated using `int` or `float` as data for the `NumberType` when the `input` option is set to `string` * The type guesser guesses the HTML accept attribute when a mime type is configured in the File or Image constraint. diff --git a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php index 00600f8487b1..51efcb43a950 100644 --- a/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php +++ b/src/Symfony/Component/Form/Extension/Core/DataTransformer/DateTimeToArrayTransformer.php @@ -24,6 +24,7 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer private $pad; private $fields; + private $referenceDate; /** * @param string $inputTimezone The input timezone @@ -31,7 +32,7 @@ class DateTimeToArrayTransformer extends BaseDateTimeTransformer * @param array $fields The date fields * @param bool $pad Whether to use padding */ - public function __construct(string $inputTimezone = null, string $outputTimezone = null, array $fields = null, bool $pad = false) + public function __construct(string $inputTimezone = null, string $outputTimezone = null, array $fields = null, bool $pad = false, \DateTimeInterface $referenceDate = null) { parent::__construct($inputTimezone, $outputTimezone); @@ -41,6 +42,7 @@ public function __construct(string $inputTimezone = null, string $outputTimezone $this->fields = $fields; $this->pad = $pad; + $this->referenceDate = $referenceDate ?: new \DateTimeImmutable('1970-01-01 00:00:00'); } /** @@ -165,12 +167,12 @@ public function reverseTransform($value) try { $dateTime = new \DateTime(sprintf( '%s-%s-%s %s:%s:%s', - empty($value['year']) ? '1970' : $value['year'], - empty($value['month']) ? '1' : $value['month'], - empty($value['day']) ? '1' : $value['day'], - empty($value['hour']) ? '0' : $value['hour'], - empty($value['minute']) ? '0' : $value['minute'], - empty($value['second']) ? '0' : $value['second'] + empty($value['year']) ? $this->referenceDate->format('Y') : $value['year'], + empty($value['month']) ? $this->referenceDate->format('m') : $value['month'], + empty($value['day']) ? $this->referenceDate->format('d') : $value['day'], + empty($value['hour']) ? $this->referenceDate->format('H') : $value['hour'], + empty($value['minute']) ? $this->referenceDate->format('i') : $value['minute'], + empty($value['second']) ? $this->referenceDate->format('s') : $value['second'] ), new \DateTimeZone($this->outputTimezone) ); diff --git a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php index fb46274e31ab..8d4ef4181b2d 100644 --- a/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php +++ b/src/Symfony/Component/Form/Extension/Core/Type/TimeType.php @@ -45,6 +45,10 @@ public function buildForm(FormBuilderInterface $builder, array $options) throw new InvalidConfigurationException('You can not disable minutes if you have enabled seconds.'); } + if (null !== $options['reference_date'] && $options['reference_date']->getTimezone()->getName() !== $options['model_timezone']) { + throw new InvalidConfigurationException(sprintf('The configured "model_timezone" (%s) must match the timezone of the "reference_date" (%s).', $options['model_timezone'], $options['reference_date']->getTimezone()->getName())); + } + if ($options['with_minutes']) { $format .= ':i'; $parts[] = 'minute'; @@ -56,8 +60,6 @@ public function buildForm(FormBuilderInterface $builder, array $options) } if ('single_text' === $options['widget']) { - $builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format)); - // handle seconds ignored by user's browser when with_seconds enabled // https://codereview.chromium.org/450533009/ if ($options['with_seconds']) { @@ -68,6 +70,20 @@ public function buildForm(FormBuilderInterface $builder, array $options) } }); } + + if (null !== $options['reference_date']) { + $format = 'Y-m-d '.$format; + + $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) { + $data = $event->getData(); + + if (preg_match('/^\d{2}:\d{2}(:\d{2})?$/', $data)) { + $event->setData($options['reference_date']->format('Y-m-d ').$data); + } + }); + } + + $builder->addViewTransformer(new DateTimeToStringTransformer($options['model_timezone'], $options['view_timezone'], $format)); } else { $hourOptions = $minuteOptions = $secondOptions = [ 'error_bubbling' => true, @@ -157,7 +173,7 @@ public function buildForm(FormBuilderInterface $builder, array $options) $builder->add('second', self::$widgets[$options['widget']], $secondOptions); } - $builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'])); + $builder->addViewTransformer(new DateTimeToArrayTransformer($options['model_timezone'], $options['view_timezone'], $parts, 'text' === $options['widget'], $options['reference_date'])); } if ('datetime_immutable' === $options['input']) { @@ -262,6 +278,7 @@ public function configureOptions(OptionsResolver $resolver) 'with_seconds' => false, 'model_timezone' => null, 'view_timezone' => null, + 'reference_date' => null, 'placeholder' => $placeholderDefault, 'html5' => true, // Don't modify \DateTime classes by reference, we treat @@ -280,6 +297,14 @@ public function configureOptions(OptionsResolver $resolver) 'choice_translation_domain' => false, ]); + $resolver->setDeprecated('model_timezone', function (Options $options, $modelTimezone): string { + if (null !== $modelTimezone && $options['view_timezone'] !== $modelTimezone && null === $options['reference_date']) { + return sprintf('Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is deprecated since Symfony 4.4.'); + } + + return ''; + }); + $resolver->setNormalizer('placeholder', $placeholderNormalizer); $resolver->setNormalizer('choice_translation_domain', $choiceTranslationDomainNormalizer); @@ -300,6 +325,7 @@ public function configureOptions(OptionsResolver $resolver) $resolver->setAllowedTypes('minutes', 'array'); $resolver->setAllowedTypes('seconds', 'array'); $resolver->setAllowedTypes('input_format', 'string'); + $resolver->setAllowedTypes('reference_date', ['null', \DateTimeInterface::class]); } /** diff --git a/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php b/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php index 16434073a548..7be042fbb884 100644 --- a/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php +++ b/src/Symfony/Component/Form/Tests/Command/DebugCommandTest.php @@ -45,7 +45,8 @@ public function testDebugDeprecatedDefaults() Built-in form types (Symfony\Component\Form\Extension\Core\Type) ---------------------------------------------------------------- - BirthdayType, DateTimeType, DateType, IntegerType, TimezoneType + BirthdayType, DateTimeType, DateType, IntegerType, TimeType + TimezoneType Service form types ------------------ diff --git a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php index 308207f2b32c..e5d7fa9a7c26 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Core/Type/TimeTypeTest.php @@ -276,6 +276,57 @@ public function testSubmitWithSecondsAndBrowserOmissionSeconds() $this->assertEquals('03:04:00', $form->getViewData()); } + public function testSubmitDifferentTimezones() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'Europe/Berlin', + 'input' => 'datetime', + 'with_seconds' => true, + 'reference_date' => new \DateTimeImmutable('2019-01-01', new \DateTimeZone('UTC')), + ]); + $form->submit([ + 'hour' => '16', + 'minute' => '9', + 'second' => '10', + ]); + + $this->assertSame('15:09:10', $form->getData()->format('H:i:s')); + } + + public function testSubmitDifferentTimezonesDuringDaylightSavingTime() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'Europe/Berlin', + 'input' => 'datetime', + 'with_seconds' => true, + 'reference_date' => new \DateTimeImmutable('2019-07-12', new \DateTimeZone('UTC')), + ]); + $form->submit([ + 'hour' => '16', + 'minute' => '9', + 'second' => '10', + ]); + + $this->assertSame('14:09:10', $form->getData()->format('H:i:s')); + } + + public function testSubmitDifferentTimezonesDuringDaylightSavingTimeUsingSingleTextWidget() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'Europe/Berlin', + 'input' => 'datetime', + 'with_seconds' => true, + 'reference_date' => new \DateTimeImmutable('2019-07-12', new \DateTimeZone('UTC')), + 'widget' => 'single_text', + ]); + $form->submit('16:09:10'); + + $this->assertSame('14:09:10', $form->getData()->format('H:i:s')); + } + public function testSetDataWithoutMinutes() { $form = $this->factory->create(static::TESTED_TYPE, null, [ @@ -311,6 +362,7 @@ public function testSetDataDifferentTimezones() 'view_timezone' => 'Asia/Hong_Kong', 'input' => 'string', 'with_seconds' => true, + 'reference_date' => new \DateTimeImmutable('2013-01-01 00:00:00', new \DateTimeZone('America/New_York')), ]); $dateTime = new \DateTime('2013-01-01 12:04:05'); @@ -337,6 +389,7 @@ public function testSetDataDifferentTimezonesDateTime() 'view_timezone' => 'Asia/Hong_Kong', 'input' => 'datetime', 'with_seconds' => true, + 'reference_date' => new \DateTimeImmutable('now', new \DateTimeZone('America/New_York')), ]); $dateTime = new \DateTime('12:04:05'); @@ -357,6 +410,39 @@ public function testSetDataDifferentTimezonesDateTime() $this->assertEquals($displayedData, $form->getViewData()); } + public function testSetDataDifferentTimezonesDuringDaylightSavingTime() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'Europe/Berlin', + 'input' => 'datetime', + 'with_seconds' => true, + 'reference_date' => new \DateTimeImmutable('2019-07-12', new \DateTimeZone('UTC')), + ]); + + $form->setData(new \DateTime('2019-07-24 14:09:10', new \DateTimeZone('UTC'))); + + $this->assertSame(['hour' => '16', 'minute' => '9', 'second' => '10'], $form->getViewData()); + } + + /** + * @group legacy + * @expectedDeprecation Using different values for the "model_timezone" and "view_timezone" options without configuring a reference date is deprecated since Symfony 4.4. + */ + public function testSetDataDifferentTimezonesWithoutReferenceDate() + { + $form = $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'Europe/Berlin', + 'input' => 'datetime', + 'with_seconds' => true, + ]); + + $form->setData(new \DateTime('2019-07-24 14:09:10', new \DateTimeZone('UTC'))); + + $this->assertSame(['hour' => '16', 'minute' => '9', 'second' => '10'], $form->getViewData()); + } + public function testHoursOption() { $form = $this->factory->create(static::TESTED_TYPE, null, [ @@ -762,6 +848,18 @@ public function testThrowExceptionIfSecondsIsInvalid() ]); } + /** + * @expectedException \Symfony\Component\Form\Exception\InvalidConfigurationException + */ + public function testReferenceDateTimezoneMustMatchModelTimezone() + { + $this->factory->create(static::TESTED_TYPE, null, [ + 'model_timezone' => 'UTC', + 'view_timezone' => 'Europe/Berlin', + 'reference_date' => new \DateTimeImmutable('now', new \DateTimeZone('Europe/Berlin')), + ]); + } + public function testPassDefaultChoiceTranslationDomain() { $form = $this->factory->create(static::TESTED_TYPE); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy