merged branch bschussek/issue3162 (PR #4839)

Commits
-------

ded6c03 [Form] DateTimeType now handles RFC 3339 dates as provided by HTML5
7e8b622 [Form] Added the option "format" to DateTimeType
9eeb200 [Form] Changed the default format of DateType to "yyyy-MM-dd" to support HTML 5 out of the box
d621a76 [Form] Improved DateTimeType code

Discussion
----------

[Form] Changed DateType and DateTimeType to support HTML5 by default

Bug fix: no
Feature addition: yes
Backwards compatibility break: yes
Symfony2 tests pass: yes
Fixes the following tickets: #2849, #3162
Todo: -

This PR changes DateType and DateTimeType to support HTML5 by default when setting the option "widget" to "single_text".

Also, the option "format" was added to DateTimeType.

---------------------------------------------------------------------------

by stof at 2012-07-10T15:38:44Z

This loos OK to me

---------------------------------------------------------------------------

by MDrollette at 2012-07-10T16:36:26Z

@stof typo: "looks" #meta-stoffed
This commit is contained in:
Fabien Potencier 2012-07-11 09:09:08 +02:00
commit 75dfc8fe97
8 changed files with 453 additions and 79 deletions

View File

@ -149,3 +149,8 @@ CHANGELOG
* fixed: the "data" option supersedes default values from the model
* changed DateType to refer to the "format" option for calculating the year and day choices instead
of padding them automatically
* [BC BREAK] DateType defaults to the format "yyyy-MM-dd" now if the widget is
"single_text", in order to support the HTML 5 date field out of the box
* added the option "format" to DateTimeType
* [BC BREAK] DateTimeType now outputs RFC 3339 dates by default, as generated and
consumed by HTML5 browsers, if the widget is "single_text"

View File

@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Core\DataTransformer;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
use Symfony\Component\Form\Exception\TransformationFailedException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class DateTimeToRfc3339Transformer extends BaseDateTimeTransformer
{
/**
* {@inheritDoc}
*/
public function transform($dateTime)
{
if (null === $dateTime) {
return '';
}
if (!$dateTime instanceof \DateTime) {
throw new UnexpectedTypeException($dateTime, '\DateTime');
}
if ($this->inputTimezone !== $this->outputTimezone) {
$dateTime = clone $dateTime;
$dateTime->setTimezone(new \DateTimeZone($this->outputTimezone));
}
return preg_replace('/\+00:00$/', 'Z', $dateTime->format('c'));
}
/**
* {@inheritDoc}
*/
public function reverseTransform($rfc3339)
{
if (!is_string($rfc3339)) {
throw new UnexpectedTypeException($rfc3339, 'string');
}
if ('' === $rfc3339) {
return null;
}
$dateTime = new \DateTime($rfc3339);
if ($this->outputTimezone !== $this->inputTimezone) {
try {
$dateTime->setTimezone(new \DateTimeZone($this->inputTimezone));
} catch (\Exception $e) {
throw new TransformationFailedException($e->getMessage(), $e->getCode(), $e);
}
}
if (preg_match('/(\d{4})-(\d{2})-(\d{2})/', $rfc3339, $matches)) {
if (!checkdate($matches[2], $matches[3], $matches[1])) {
throw new TransformationFailedException(sprintf(
'The date "%s-%s-%s" is not a valid date.',
$matches[1],
$matches[2],
$matches[3]
));
}
}
return $dateTime;
}
}

View File

@ -12,6 +12,7 @@
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;
@ -19,31 +20,93 @@ 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\DateTimeToRfc3339Transformer;
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
*
* An ICU ticket was created:
* http://icu-project.org/trac/ticket/9421
*
* To temporarily circumvent this issue, DateTimeToRfc3339Transformer is used
* when the format matches this constant.
*
* ("ZZZZZZ" is not recognized by ICU and used here to differentiate this
* pattern from custom patterns).
*/
const HTML5_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZZZZZZ";
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 letters "y", "M", "d", "H" and "m". Its current value is "%s".', $pattern));
}
if ('single_text' === $options['widget']) {
$builder->addViewTransformer(new DateTimeToStringTransformer($options['data_timezone'], $options['user_timezone'], $format));
if (self::HTML5_FORMAT === $pattern) {
$builder->addViewTransformer(new DateTimeToRfc3339Transformer(
$options['data_timezone'],
$options['user_timezone']
));
} else {
$builder->addViewTransformer(new DateTimeToLocalizedStringTransformer(
$options['data_timezone'],
$options['user_timezone'],
$dateFormat,
$timeFormat,
$calendar,
$pattern
));
}
} else {
// Only pass a subset of the options to children
$dateOptions = array_intersect_key($options, array_flip(array(
@ -54,6 +117,7 @@ class DateTimeType extends AbstractType
'required',
'translation_domain',
)));
$timeOptions = array_intersect_key($options, array_flip(array(
'hours',
'minutes',
@ -64,21 +128,15 @@ class DateTimeType extends AbstractType
'translation_domain',
)));
// If `widget` is set, overwrite widget options from `date` and `time`
if (isset($options['widget'])) {
$dateOptions['widget'] = $options['widget'];
$timeOptions['widget'] = $options['widget'];
} else {
if (isset($options['date_widget'])) {
$dateOptions['widget'] = $options['date_widget'];
}
if (isset($options['time_widget'])) {
$timeOptions['widget'] = $options['time_widget'];
}
if (null !== $options['date_widget']) {
$dateOptions['widget'] = $options['date_widget'];
}
if (isset($options['date_format'])) {
if (null !== $options['time_widget']) {
$timeOptions['widget'] = $options['time_widget'];
}
if (null !== $options['date_format']) {
$dateOptions['format'] = $options['date_format'];
}
@ -89,7 +147,7 @@ class DateTimeType extends AbstractType
->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,
)),
)))
@ -120,7 +178,10 @@ class DateTimeType extends AbstractType
{
$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');
}
}
@ -134,27 +195,29 @@ class DateTimeType extends AbstractType
return $options['widget'] !== 'single_text';
};
// Defaults to the value of "widget"
$dateWidget = function (Options $options) {
return $options['widget'];
};
// Defaults to the value of "widget"
$timeWidget = function (Options $options) {
return $options['widget'];
};
$resolver->setDefaults(array(
'input' => 'datetime',
'data_timezone' => null,
'user_timezone' => null,
'date_widget' => null,
'format' => self::HTML5_FORMAT,
'date_format' => null,
'time_widget' => null,
/* Defaults for date field */
'years' => range(date('Y') - 5, date('Y') + 5),
'months' => range(1, 12),
'days' => range(1, 31),
/* Defaults for time field */
'hours' => range(0, 23),
'minutes' => range(0, 59),
'seconds' => range(0, 59),
'widget' => null,
'date_widget' => $dateWidget,
'time_widget' => $timeWidget,
'with_seconds' => false,
// Don't modify \DateTime classes by reference, we treat
// them like immutable value objects
'by_reference' => false,
// This will overwrite "widget" child options
'widget' => null,
// If initialized with a \DateTime object, FormType initializes
// this option to "\DateTime". Since the internal, normalized
// representation is not \DateTime, but an array, we need to unset
@ -167,6 +230,12 @@ class DateTimeType extends AbstractType
// set in DateType and TimeType
$resolver->setOptional(array(
'empty_value',
'years',
'months',
'days',
'hours',
'minutes',
'seconds',
));
$resolver->setAllowedValues(array(

View File

@ -29,6 +29,8 @@ class DateType extends AbstractType
{
const DEFAULT_FORMAT = \IntlDateFormatter::MEDIUM;
const HTML5_FORMAT = 'yyyy-MM-dd';
private static $acceptedFormats = array(
\IntlDateFormatter::FULL,
\IntlDateFormatter::LONG,
@ -51,7 +53,7 @@ class DateType extends AbstractType
}
if (null !== $pattern && (false === strpos($pattern, 'y') || false === strpos($pattern, 'M') || false === strpos($pattern, 'd'))) {
throw new InvalidOptionsException(sprintf('The "format" option should contain the patterns "y", "M" and "d". Its current value is "%s".', $pattern));
throw new InvalidOptionsException(sprintf('The "format" option should contain the letters "y", "M" and "d". Its current value is "%s".', $pattern));
}
if ('single_text' === $options['widget']) {
@ -130,7 +132,10 @@ class DateType extends AbstractType
{
$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', 'date');
}
@ -186,7 +191,7 @@ class DateType extends AbstractType
'days' => range(1, 31),
'widget' => 'choice',
'input' => 'datetime',
'format' => self::DEFAULT_FORMAT,
'format' => self::HTML5_FORMAT,
'data_timezone' => null,
'user_timezone' => null,
'empty_value' => $emptyValue,

View File

@ -786,14 +786,14 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@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"]
@ -826,14 +826,14 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@id="name_date"]
[
./select
[@id="name_date_year"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_date_month"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_date_day"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_date_year"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
]
/following-sibling::div
[@id="name_time"]
@ -866,14 +866,14 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@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"]
@ -905,14 +905,14 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@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"]
@ -948,7 +948,7 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@type="date"]
[@id="name_date"]
[@name="name[date]"]
[@value="Feb 3, 2011"]
[@value="2011-02-03"]
/following-sibling::input
[@type="time"]
[@id="name_time"]
@ -970,7 +970,7 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
'/input
[@type="datetime"]
[@name="name"]
[@value="2011-02-03 04:05"]
[@value="2011-02-03T04:05:06+01:00"]
'
);
}
@ -988,7 +988,7 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
'/input
[@type="datetime"]
[@name="name"]
[@value="2011-02-03 04:05"]
[@value="2011-02-03T04:05:06+01:00"]
'
);
}
@ -1004,14 +1004,14 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
'/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]
'
@ -1031,14 +1031,14 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
'/div
[
./select
[@id="name_year"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_month"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_day"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
/following-sibling::select
[@id="name_year"]
[./option[@value=""][.="[trans]Change&Me[/trans]"]]
]
[count(./select)=3]
'
@ -1058,14 +1058,14 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
'/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]
'
@ -1083,6 +1083,10 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
'/div
[
./input
[@id="name_year"]
[@type="text"]
[@value="2011"]
/following-sibling::input
[@id="name_month"]
[@type="text"]
[@value="2"]
@ -1090,10 +1094,6 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@id="name_day"]
[@type="text"]
[@value="3"]
/following-sibling::input
[@id="name_year"]
[@type="text"]
[@value="2011"]
]
[count(./input)=3]
'
@ -1111,7 +1111,7 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
'/input
[@type="date"]
[@name="name"]
[@value="Feb 3, 2011"]
[@value="2011-02-03"]
'
);
}
@ -1137,14 +1137,14 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
'/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]
'
@ -1163,6 +1163,10 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
'/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"]]
@ -1170,10 +1174,6 @@ abstract class AbstractLayoutTest extends \PHPUnit_Framework_TestCase
[@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]
'

View File

@ -0,0 +1,122 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Tests\Extension\Core\DataTransformer;
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToRfc3339Transformer;
class DateTimeToRfc3339TransformerTest extends DateTimeTestCase
{
protected $dateTime;
protected $dateTimeWithoutSeconds;
protected function setUp()
{
parent::setUp();
$this->dateTime = new \DateTime('2010-02-03 04:05:06 UTC');
$this->dateTimeWithoutSeconds = new \DateTime('2010-02-03 04:05:00 UTC');
}
protected function tearDown()
{
$this->dateTime = null;
$this->dateTimeWithoutSeconds = null;
}
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');
$actual = $actual->format('c');
}
parent::assertEquals($expected, $actual, $message, $delta, $maxDepth, $canonicalize, $ignoreCase);
}
public function allProvider()
{
return array(
array('UTC', 'UTC', '2010-02-03 04:05:06 UTC', '2010-02-03T04:05:06Z'),
array('UTC', 'UTC', null, ''),
array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:06 America/New_York', '2010-02-03T17:05:06+08:00'),
array('America/New_York', 'Asia/Hong_Kong', null, ''),
array('UTC', 'Asia/Hong_Kong', '2010-02-03 04:05:06 UTC', '2010-02-03T12:05:06+08:00'),
array('America/New_York', 'UTC', '2010-02-03 04:05:06 America/New_York', '2010-02-03T09:05:06Z'),
);
}
public function transformProvider()
{
return $this->allProvider();
}
public function reverseTransformProvider()
{
return array_merge($this->allProvider(), array(
// format without seconds, as appears in some browsers
array('UTC', 'UTC', '2010-02-03 04:05:00 UTC', '2010-02-03T04:05Z'),
array('America/New_York', 'Asia/Hong_Kong', '2010-02-03 04:05:00 America/New_York', '2010-02-03T17:05+08:00'),
));
}
/**
* @dataProvider transformProvider
*/
public function testTransform($fromTz, $toTz, $from, $to)
{
$transformer = new DateTimeToRfc3339Transformer($fromTz, $toTz);
$this->assertSame($to, $transformer->transform(null !== $from ? new \DateTime($from) : null));
}
/**
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
*/
public function testTransformRequiresValidDateTime()
{
$transformer = new DateTimeToRfc3339Transformer();
$transformer->transform('2010-01-01');
}
/**
* @dataProvider reverseTransformProvider
*/
public function testReverseTransform($toTz, $fromTz, $to, $from)
{
$transformer = new DateTimeToRfc3339Transformer($toTz, $fromTz);
if (null !== $to) {
$this->assertDateTimeEquals(new \DateTime($to), $transformer->reverseTransform($from));
} else {
$this->assertSame($to, $transformer->reverseTransform($from));
}
}
/**
* @expectedException Symfony\Component\Form\Exception\UnexpectedTypeException
*/
public function testReverseTransformRequiresString()
{
$transformer = new DateTimeToRfc3339Transformer();
$transformer->reverseTransform(12345);
}
/**
* @expectedException Symfony\Component\Form\Exception\TransformationFailedException
*/
public function testReverseTransformWithNonExistingDate()
{
$transformer = new DateTimeToRfc3339Transformer('UTC', 'UTC');
var_dump($transformer->reverseTransform('2010-04-31T04:05Z'));
}
}

View File

@ -163,15 +163,14 @@ class DateTimeTypeTest extends LocalizedTestCase
'input' => 'datetime',
));
$dateTime = new \DateTime('2010-06-02 03:04:05 America/New_York');
$form->bind('2010-06-02 03:04:05');
$outputTime = new \DateTime('2010-06-02 03:04:00 Pacific/Tahiti');
$form->bind('2010-06-02T03:04:00-10:00');
$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-10:00', $form->getViewData());
}
public function testSubmit_stringSingleText()
@ -183,10 +182,10 @@ class DateTimeTypeTest extends LocalizedTestCase
'widget' => 'single_text',
));
$form->bind('2010-06-02 03:04:05');
$form->bind('2010-06-02T03:04:00Z');
$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:00Z', $form->getViewData());
}
public function testSubmit_stringSingleText_withSeconds()
@ -199,10 +198,10 @@ class DateTimeTypeTest extends LocalizedTestCase
'with_seconds' => true,
));
$form->bind('2010-06-02 03:04:05');
$form->bind('2010-06-02T03:04:05Z');
$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:05Z', $form->getViewData());
}
public function testSubmit_differentPattern()
@ -356,4 +355,35 @@ class DateTimeTypeTest extends LocalizedTestCase
$this->assertNull($view->get('time')->get('minute')->getVar('empty_value'));
$this->assertSame('Empty second', $view->get('time')->get('second')->getVar('empty_value'));
}
public function testPassHtml5TypeIfSingleTextAndHtml5Format()
{
$form = $this->factory->create('datetime', null, array(
'widget' => 'single_text',
));
$view = $form->createView();
$this->assertSame('datetime', $view->getVar('type'));
}
public function testDontPassHtml5TypeIfNotHtml5Format()
{
$form = $this->factory->create('datetime', null, array(
'widget' => 'single_text',
'format' => 'yyyy-MM-dd HH:mm',
));
$view = $form->createView();
$this->assertNull($view->getVar('datetime'));
}
public function testDontPassHtml5TypeIfNotSingleText()
{
$form = $this->factory->create('datetime', null, array(
'widget' => 'text',
));
$view = $form->createView();
$this->assertNull($view->getVar('type'));
}
}

View File

@ -42,9 +42,25 @@ class DateTypeTest extends LocalizedTestCase
));
}
public function testSubmitFromSingleTextDateTimeWithDefaultFormat()
{
$form = $this->factory->create('date', null, array(
'data_timezone' => 'UTC',
'user_timezone' => 'UTC',
'widget' => 'single_text',
'input' => 'datetime',
));
$form->bind('2010-06-02');
$this->assertDateTimeEquals(new \DateTime('2010-06-02 UTC'), $form->getData());
$this->assertEquals('2010-06-02', $form->getViewData());
}
public function testSubmitFromSingleTextDateTime()
{
$form = $this->factory->create('date', null, array(
'format' => \IntlDateFormatter::MEDIUM,
'data_timezone' => 'UTC',
'user_timezone' => 'UTC',
'widget' => 'single_text',
@ -60,6 +76,7 @@ class DateTypeTest extends LocalizedTestCase
public function testSubmitFromSingleTextString()
{
$form = $this->factory->create('date', null, array(
'format' => \IntlDateFormatter::MEDIUM,
'data_timezone' => 'UTC',
'user_timezone' => 'UTC',
'widget' => 'single_text',
@ -75,6 +92,7 @@ class DateTypeTest extends LocalizedTestCase
public function testSubmitFromSingleTextTimestamp()
{
$form = $this->factory->create('date', null, array(
'format' => \IntlDateFormatter::MEDIUM,
'data_timezone' => 'UTC',
'user_timezone' => 'UTC',
'widget' => 'single_text',
@ -92,6 +110,7 @@ class DateTypeTest extends LocalizedTestCase
public function testSubmitFromSingleTextRaw()
{
$form = $this->factory->create('date', null, array(
'format' => \IntlDateFormatter::MEDIUM,
'data_timezone' => 'UTC',
'user_timezone' => 'UTC',
'widget' => 'single_text',
@ -296,6 +315,7 @@ class DateTypeTest extends LocalizedTestCase
public function testSetData_differentTimezones()
{
$form = $this->factory->create('date', null, array(
'format' => \IntlDateFormatter::MEDIUM,
'data_timezone' => 'America/New_York',
'user_timezone' => 'Pacific/Tahiti',
'input' => 'string',
@ -310,6 +330,7 @@ class DateTypeTest extends LocalizedTestCase
public function testSetData_differentTimezonesDateTime()
{
$form = $this->factory->create('date', null, array(
'format' => \IntlDateFormatter::MEDIUM,
'data_timezone' => 'America/New_York',
'user_timezone' => 'Pacific/Tahiti',
'input' => 'datetime',
@ -488,6 +509,17 @@ class DateTypeTest extends LocalizedTestCase
$form = $this->factory->create('date');
$view = $form->createView();
$this->assertSame('{{ year }}-{{ month }}-{{ day }}', $view->getVar('date_pattern'));
}
public function testPassDatePatternToViewDifferentFormat()
{
$form = $this->factory->create('date', null, array(
'format' => \IntlDateFormatter::MEDIUM,
));
$view = $form->createView();
$this->assertSame('{{ day }}.{{ month }}.{{ year }}', $view->getVar('date_pattern'));
}
@ -623,4 +655,35 @@ class DateTypeTest extends LocalizedTestCase
$this->assertNull($view->get('month')->getVar('empty_value'));
$this->assertSame('Empty day', $view->get('day')->getVar('empty_value'));
}
public function testPassHtml5TypeIfSingleTextAndHtml5Format()
{
$form = $this->factory->create('date', null, array(
'widget' => 'single_text',
));
$view = $form->createView();
$this->assertSame('date', $view->getVar('type'));
}
public function testDontPassHtml5TypeIfNotHtml5Format()
{
$form = $this->factory->create('date', null, array(
'widget' => 'single_text',
'format' => \IntlDateFormatter::MEDIUM,
));
$view = $form->createView();
$this->assertNull($view->getVar('type'));
}
public function testDontPassHtml5TypeIfNotSingleText()
{
$form = $this->factory->create('date', null, array(
'widget' => 'text',
));
$view = $form->createView();
$this->assertNull($view->getVar('type'));
}
}