Skip to content

[Validator] Added a format option to the DateTime constraint. #17553

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions UPGRADE-3.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,9 @@ Yaml

* The `!!php/object` tag to indicate dumped PHP objects has been deprecated
and will be removed in Symfony 4.0. Use the `!php/object` tag instead.

Validator
---------

* The `DateTimeValidator::PATTERN` constant is deprecated and will be removed in
Symfony 4.0.
5 changes: 5 additions & 0 deletions UPGRADE-4.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,8 @@ Yaml

* The `!!php/object` tag to indicate dumped PHP objects was removed in favor of
the `!php/object` tag.

Validator
---------

* The `DateTimeValidator::PATTERN` constant was removed.
6 changes: 6 additions & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
CHANGELOG
=========

3.1.0
-----

* deprecated `DateTimeValidator::PATTERN` constant
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

must be added to the upgrade file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

* added a `format` option to the `DateTime` constraint

2.8.0
-----

Expand Down
1 change: 1 addition & 0 deletions src/Symfony/Component/Validator/Constraints/DateTime.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ class DateTime extends Constraint
self::INVALID_TIME_ERROR => 'INVALID_TIME_ERROR',
);

public $format = 'Y-m-d H:i:s';
public $message = 'This value is not a valid datetime.';
}
39 changes: 26 additions & 13 deletions src/Symfony/Component/Validator/Constraints/DateTimeValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@

/**
* @author Bernhard Schussek <bschussek@gmail.com>
* @author Diego Saint Esteben <diego@saintesteben.me>
*/
class DateTimeValidator extends DateValidator
{
/**
* @deprecated since version 3.1, to be removed in 4.0.
*/
const PATTERN = '/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/';

/**
Expand All @@ -40,7 +44,11 @@ public function validate($value, Constraint $constraint)

$value = (string) $value;

if (!preg_match(static::PATTERN, $value, $matches)) {
\DateTime::createFromFormat($constraint->format, $value);

$errors = \DateTime::getLastErrors();

if (0 < $errors['error_count']) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_FORMAT_ERROR)
Expand All @@ -49,18 +57,23 @@ public function validate($value, Constraint $constraint)
return;
}

if (!DateValidator::checkDate($matches[1], $matches[2], $matches[3])) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_DATE_ERROR)
->addViolation();
}

if (!TimeValidator::checkTime($matches[4], $matches[5], $matches[6])) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_TIME_ERROR)
->addViolation();
foreach ($errors['warnings'] as $warning) {
if ('The parsed date was invalid' === $warning) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very fragile to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, but given the return value of DateTime::getLastErrors() this is the only way to implement this feature without breaking BC. In 4.0 maybe we can get rid of the INVALID_DATE_ERROR and INVALID_TIME_ERROR codes and create a violation for each warning using the INVALID_FORMAT_ERROR code.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as we have tests that validate this behavior across multiple platforms and PHP versions I think that this should be fine.

$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_DATE_ERROR)
->addViolation();
} elseif ('The parsed time was invalid' === $warning) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_TIME_ERROR)
->addViolation();
} else {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(DateTime::INVALID_FORMAT_ERROR)
->addViolation();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should add an else clause here to catch other possible warnings.

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,32 +50,53 @@ public function testExpectsStringCompatibleType()
$this->validator->validate(new \stdClass(), new DateTime());
}

public function testDateTimeWithDefaultFormat()
{
$this->validator->validate('1995-05-10 19:33:00', new DateTime());

$this->assertNoViolation();

$this->validator->validate('1995-03-24', new DateTime());

$this->buildViolation('This value is not a valid datetime.')
->setParameter('{{ value }}', '"1995-03-24"')
->setCode(DateTime::INVALID_FORMAT_ERROR)
->assertRaised();
}

/**
* @dataProvider getValidDateTimes
*/
public function testValidDateTimes($dateTime)
public function testValidDateTimes($format, $dateTime)
{
$this->validator->validate($dateTime, new DateTime());
$constraint = new DateTime(array(
'format' => $format,
));

$this->validator->validate($dateTime, $constraint);

$this->assertNoViolation();
}

public function getValidDateTimes()
{
return array(
array('2010-01-01 01:02:03'),
array('1955-12-12 00:00:00'),
array('2030-05-31 23:59:59'),
array('Y-m-d H:i:s e', '1995-03-24 00:00:00 UTC'),
array('Y-m-d H:i:s', '2010-01-01 01:02:03'),
array('Y/m/d H:i', '2010/01/01 01:02'),
array('F d, Y', 'December 31, 1999'),
array('d-m-Y', '10-05-1995'),
);
}

/**
* @dataProvider getInvalidDateTimes
*/
public function testInvalidDateTimes($dateTime, $code)
public function testInvalidDateTimes($format, $dateTime, $code)
{
$constraint = new DateTime(array(
'message' => 'myMessage',
'format' => $format,
));

$this->validator->validate($dateTime, $constraint);
Expand All @@ -89,16 +110,16 @@ public function testInvalidDateTimes($dateTime, $code)
public function getInvalidDateTimes()
{
return array(
array('foobar', DateTime::INVALID_FORMAT_ERROR),
array('2010-01-01', DateTime::INVALID_FORMAT_ERROR),
array('00:00:00', DateTime::INVALID_FORMAT_ERROR),
array('2010-01-01 00:00', DateTime::INVALID_FORMAT_ERROR),
array('2010-13-01 00:00:00', DateTime::INVALID_DATE_ERROR),
array('2010-04-32 00:00:00', DateTime::INVALID_DATE_ERROR),
array('2010-02-29 00:00:00', DateTime::INVALID_DATE_ERROR),
array('2010-01-01 24:00:00', DateTime::INVALID_TIME_ERROR),
array('2010-01-01 00:60:00', DateTime::INVALID_TIME_ERROR),
array('2010-01-01 00:00:60', DateTime::INVALID_TIME_ERROR),
array('Y-m-d', 'foobar', DateTime::INVALID_FORMAT_ERROR),
array('H:i', '00:00:00', DateTime::INVALID_FORMAT_ERROR),
array('Y-m-d', '2010-01-01 00:00', DateTime::INVALID_FORMAT_ERROR),
array('Y-m-d e', '2010-01-01 TCU', DateTime::INVALID_FORMAT_ERROR),
array('Y-m-d H:i:s', '2010-13-01 00:00:00', DateTime::INVALID_DATE_ERROR),
array('Y-m-d H:i:s', '2010-04-32 00:00:00', DateTime::INVALID_DATE_ERROR),
array('Y-m-d H:i:s', '2010-02-29 00:00:00', DateTime::INVALID_DATE_ERROR),
array('Y-m-d H:i:s', '2010-01-01 24:00:00', DateTime::INVALID_TIME_ERROR),
array('Y-m-d H:i:s', '2010-01-01 00:60:00', DateTime::INVALID_TIME_ERROR),
array('Y-m-d H:i:s', '2010-01-01 00:00:60', DateTime::INVALID_TIME_ERROR),
);
}
}