Skip to content

Commit

Permalink
[Form] Added the option "format" to DateTimeType
Browse files Browse the repository at this point in the history
  • Loading branch information
webmozart committed Jul 10, 2012
1 parent 9eeb200 commit 7e8b622
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 51 deletions.
4 changes: 4 additions & 0 deletions src/Symfony/Component/Form/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,7 @@ CHANGELOG
of padding them automatically
* [BC BREAK] DateType defaults to the format "yyyy-MM-dd" now in order to support
the HTML 5 date field out of the box
* added the option "format" to DateTimeType
* [BC BREAK] DateTimeType defaults to the format "yyyy-MM-dd'T'HH:mm:ss" now. This
is almost identical to the pattern of the HTML 5 datetime input, but not quite,
because ICU cannot generate RFC 3339 dates (which have a timezone suffix).
60 changes: 53 additions & 7 deletions src/Symfony/Component/Form/Extension/Core/Type/DateTimeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,83 @@
namespace Symfony\Component\Form\Extension\Core\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormViewInterface;
use Symfony\Component\Form\ReversedTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DataTransformerChain;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToArrayTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToStringTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToLocalizedStringTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\ArrayToPartsTransformer;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class DateTimeType extends AbstractType
{
const DEFAULT_DATE_FORMAT = \IntlDateFormatter::MEDIUM;

const DEFAULT_TIME_FORMAT = \IntlDateFormatter::MEDIUM;

/**
* This is not quite the HTML5 format yet, because ICU lacks the
* capability of parsing and generating RFC 3339 dates, which
* are like the below pattern but with a timezone suffix. The
* timezone suffix is
*
* * "Z" for UTC
* * "(-|+)HH:mm" for other timezones (note the colon!)
*
* http://userguide.icu-project.org/formatparse/datetime#TOC-Date-Time-Format-Syntax
* http://www.w3.org/TR/html-markup/input.datetime.html
* http://tools.ietf.org/html/rfc3339
*/
const HTML5_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";

private static $acceptedFormats = array(
\IntlDateFormatter::FULL,
\IntlDateFormatter::LONG,
\IntlDateFormatter::MEDIUM,
\IntlDateFormatter::SHORT,
);

/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$parts = array('year', 'month', 'day', 'hour', 'minute');
$dateParts = array('year', 'month', 'day');
$timeParts = array('hour', 'minute');

$format = 'Y-m-d H:i';
if ($options['with_seconds']) {
$format = 'Y-m-d H:i:s';

$parts[] = 'second';
$timeParts[] = 'second';
}

$dateFormat = is_int($options['date_format']) ? $options['date_format'] : self::DEFAULT_DATE_FORMAT;
$timeFormat = self::DEFAULT_TIME_FORMAT;
$calendar = \IntlDateFormatter::GREGORIAN;
$pattern = is_string($options['format']) ? $options['format'] : null;

if (!in_array($dateFormat, self::$acceptedFormats, true)) {
throw new InvalidOptionsException('The "date_format" option must be one of the IntlDateFormatter constants (FULL, LONG, MEDIUM, SHORT) or a string representing a custom format.');
}

if (null !== $pattern && (false === strpos($pattern, 'y') || false === strpos($pattern, 'M') || false === strpos($pattern, 'd') || false === strpos($pattern, 'H') || false === strpos($pattern, 'm'))) {
throw new InvalidOptionsException(sprintf('The "format" option should contain the patterns "y", "M", "d", "H" and "m". Its current value is "%s".', $pattern));
}

if ('single_text' === $options['widget']) {
$builder->addViewTransformer(new DateTimeToStringTransformer(
$builder->addViewTransformer(new DateTimeToLocalizedStringTransformer(
$options['data_timezone'],
$options['user_timezone'],
$format
$dateFormat,
$timeFormat,
$calendar,
$pattern
));
} else {
// Only pass a subset of the options to children
Expand Down Expand Up @@ -88,7 +130,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
->addViewTransformer(new DataTransformerChain(array(
new DateTimeToArrayTransformer($options['data_timezone'], $options['user_timezone'], $parts),
new ArrayToPartsTransformer(array(
'date' => array('year', 'month', 'day'),
'date' => $dateParts,
'time' => $timeParts,
)),
)))
Expand Down Expand Up @@ -119,7 +161,10 @@ public function buildView(FormViewInterface $view, FormInterface $form, array $o
{
$view->setVar('widget', $options['widget']);

if ('single_text' === $options['widget']) {
// Change the input to a HTML5 date input if
// * the widget is set to "single_text"
// * the format matches the one expected by HTML5
if ('single_text' === $options['widget'] && self::HTML5_FORMAT === $options['format']) {
$view->setVar('type', 'datetime');
}
}
Expand Down Expand Up @@ -147,6 +192,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
'input' => 'datetime',
'data_timezone' => null,
'user_timezone' => null,
'format' => self::HTML5_FORMAT,
'date_format' => null,
'widget' => null,
'date_widget' => $dateWidget,
Expand Down
72 changes: 36 additions & 36 deletions src/Symfony/Component/Form/Tests/AbstractLayoutTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -786,14 +786,14 @@ public function testDateTime()
[@id="name_date"]
[
./select
[@id="name_date_year"]
[./option[@value="2011"][@selected="selected"]]
/following-sibling::select
[@id="name_date_month"]
[./option[@value="2"][@selected="selected"]]
/following-sibling::select
[@id="name_date_day"]
[./option[@value="3"][@selected="selected"]]
/following-sibling::select
[@id="name_date_year"]
[./option[@value="2011"][@selected="selected"]]
]
/following-sibling::div
[@id="name_time"]
Expand Down Expand Up @@ -826,13 +826,13 @@ public function testDateTimeWithEmptyValueGlobal()
[@id="name_date"]
[
./select
[@id="name_date_month"]
[@id="name_date_year"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_date_day"]
[@id="name_date_month"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_date_year"]
[@id="name_date_day"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
]
/following-sibling::div
Expand Down Expand Up @@ -866,14 +866,14 @@ public function testDateTimeWithEmptyValueOnTime()
[@id="name_date"]
[
./select
[@id="name_date_year"]
[./option[@value="2011"][@selected="selected"]]
/following-sibling::select
[@id="name_date_month"]
[./option[@value="2"][@selected="selected"]]
/following-sibling::select
[@id="name_date_day"]
[./option[@value="3"][@selected="selected"]]
/following-sibling::select
[@id="name_date_year"]
[./option[@value="2011"][@selected="selected"]]
]
/following-sibling::div
[@id="name_time"]
Expand Down Expand Up @@ -905,14 +905,14 @@ public function testDateTimeWithSeconds()
[@id="name_date"]
[
./select
[@id="name_date_year"]
[./option[@value="2011"][@selected="selected"]]
/following-sibling::select
[@id="name_date_month"]
[./option[@value="2"][@selected="selected"]]
/following-sibling::select
[@id="name_date_day"]
[./option[@value="3"][@selected="selected"]]
/following-sibling::select
[@id="name_date_year"]
[./option[@value="2011"][@selected="selected"]]
]
/following-sibling::div
[@id="name_time"]
Expand Down Expand Up @@ -948,7 +948,7 @@ public function testDateTimeSingleText()
[@type="date"]
[@id="name_date"]
[@name="name[date]"]
[@value="Feb 3, 2011"]
[@value="2011-02-03"]
/following-sibling::input
[@type="time"]
[@id="name_time"]
Expand All @@ -970,7 +970,7 @@ public function testDateTimeWithWidgetSingleText()
'/input
[@type="datetime"]
[@name="name"]
[@value="2011-02-03 04:05"]
[@value="2011-02-03T04:05:06"]
'
);
}
Expand All @@ -988,7 +988,7 @@ public function testDateTimeWithWidgetSingleTextIgnoreDateAndTimeWidgets()
'/input
[@type="datetime"]
[@name="name"]
[@value="2011-02-03 04:05"]
[@value="2011-02-03T04:05:06"]
'
);
}
Expand All @@ -1004,14 +1004,14 @@ public function testDateChoice()
'/div
[
./select
[@id="name_year"]
[./option[@value="2011"][@selected="selected"]]
/following-sibling::select
[@id="name_month"]
[./option[@value="2"][@selected="selected"]]
/following-sibling::select
[@id="name_day"]
[./option[@value="3"][@selected="selected"]]
/following-sibling::select
[@id="name_year"]
[./option[@value="2011"][@selected="selected"]]
]
[count(./select)=3]
'
Expand All @@ -1031,13 +1031,13 @@ public function testDateChoiceWithEmptyValueGlobal()
'/div
[
./select
[@id="name_month"]
[@id="name_year"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_day"]
[@id="name_month"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_year"]
[@id="name_day"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
]
[count(./select)=3]
Expand All @@ -1058,14 +1058,14 @@ public function testDateChoiceWithEmptyValueOnYear()
'/div
[
./select
[@id="name_year"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_month"]
[./option[@value="1"]]
/following-sibling::select
[@id="name_day"]
[./option[@value="1"]]
/following-sibling::select
[@id="name_year"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
]
[count(./select)=3]
'
Expand All @@ -1083,17 +1083,17 @@ public function testDateText()
'/div
[
./input
[@id="name_year"]
[@type="text"]
[@value="2011"]
/following-sibling::input
[@id="name_month"]
[@type="text"]
[@value="2"]
/following-sibling::input
[@id="name_day"]
[@type="text"]
[@value="3"]
/following-sibling::input
[@id="name_year"]
[@type="text"]
[@value="2011"]
]
[count(./input)=3]
'
Expand All @@ -1111,7 +1111,7 @@ public function testDateSingleText()
'/input
[@type="date"]
[@name="name"]
[@value="Feb 3, 2011"]
[@value="2011-02-03"]
'
);
}
Expand All @@ -1137,14 +1137,14 @@ public function testBirthDay()
'/div
[
./select
[@id="name_year"]
[./option[@value="2000"][@selected="selected"]]
/following-sibling::select
[@id="name_month"]
[./option[@value="2"][@selected="selected"]]
/following-sibling::select
[@id="name_day"]
[./option[@value="3"][@selected="selected"]]
/following-sibling::select
[@id="name_year"]
[./option[@value="2000"][@selected="selected"]]
]
[count(./select)=3]
'
Expand All @@ -1163,17 +1163,17 @@ public function testBirthDayWithEmptyValue()
'/div
[
./select
[@id="name_year"]
[./option[@value=""][.="[trans][/trans]"]]
[./option[@value="1950"][@selected="selected"]]
/following-sibling::select
[@id="name_month"]
[./option[@value=""][.="[trans][/trans]"]]
[./option[@value="1"][@selected="selected"]]
/following-sibling::select
[@id="name_day"]
[./option[@value=""][.="[trans][/trans]"]]
[./option[@value="1"][@selected="selected"]]
/following-sibling::select
[@id="name_year"]
[./option[@value=""][.="[trans][/trans]"]]
[./option[@value="1950"][@selected="selected"]]
]
[count(./select)=3]
'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,15 +163,14 @@ public function testSubmit_differentTimezonesDateTime()
'input' => 'datetime',
));

$dateTime = new \DateTime('2010-06-02 03:04:05 America/New_York');
$outputTime = new \DateTime('2010-06-02 03:04:00 Pacific/Tahiti');

$form->bind('2010-06-02 03:04:05');
$form->bind('2010-06-02T03:04:00');

$outputTime = new \DateTime('2010-06-02 03:04:00 Pacific/Tahiti');
$outputTime->setTimezone(new \DateTimeZone('America/New_York'));

$this->assertDateTimeEquals($outputTime, $form->getData());
$this->assertEquals('2010-06-02 03:04', $form->getViewData());
$this->assertEquals('2010-06-02T03:04:00', $form->getViewData());
}

public function testSubmit_stringSingleText()
Expand All @@ -183,10 +182,10 @@ public function testSubmit_stringSingleText()
'widget' => 'single_text',
));

$form->bind('2010-06-02 03:04:05');
$form->bind('2010-06-02T03:04:00');

$this->assertEquals('2010-06-02 03:04:00', $form->getData());
$this->assertEquals('2010-06-02 03:04', $form->getViewData());
$this->assertEquals('2010-06-02T03:04:00', $form->getViewData());
}

public function testSubmit_stringSingleText_withSeconds()
Expand All @@ -199,10 +198,10 @@ public function testSubmit_stringSingleText_withSeconds()
'with_seconds' => true,
));

$form->bind('2010-06-02 03:04:05');
$form->bind('2010-06-02T03:04:05');

$this->assertEquals('2010-06-02 03:04:05', $form->getData());
$this->assertEquals('2010-06-02 03:04:05', $form->getViewData());
$this->assertEquals('2010-06-02T03:04:05', $form->getViewData());
}

public function testSubmit_differentPattern()
Expand Down

0 comments on commit 7e8b622

Please sign in to comment.