From a039d6d4b96648066bc564d8af5f38021a20a270 Mon Sep 17 00:00:00 2001 From: Henrique Moody Date: Wed, 29 Aug 2018 21:04:17 +0200 Subject: [PATCH] Fix wrong behavior of "Date" rule The validation was considering "99" as a valid month and neither date_parse_from_format() [1] or DateTime::createFromFormat() [2] would complain about that. The solution for that was to use checkdate() [3] to verify whether the date is valid or not. Also, an extra step was added to the validation that a date that only contains a day would return false since a day without a month and year is impossible to validate. Apparently, there is no problem while validation when it comes to time, therefore nothing needed to be added for this validation. [1]: http://php.net/date_parse_from_format [2]: http://php.net/datetime.createFromFormat [3]: http://php.net/checkdate Signed-off-by: Henrique Moody --- library/Rules/Date.php | 33 ++++++++++++++++++++- tests/unit/Rules/DateTest.php | 55 ++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/library/Rules/Date.php b/library/Rules/Date.php index eb47d16d..342e1d43 100644 --- a/library/Rules/Date.php +++ b/library/Rules/Date.php @@ -49,8 +49,39 @@ class Date extends AbstractRule $this->format = $exceptionalFormats[$this->format]; } - $info = date_parse_from_format($this->format, $inputString); + return $this->isValidForFormatProvided($input); + } + private function isValidForFormatProvided($input) + { + $info = date_parse_from_format($this->format, $input); + if (!$this->isParsable($info)) { + return false; + } + + if ($this->hasDateFormat()) { + return $this->hasValidDate($info); + } + + return true; + } + + private function isParsable(array $info) + { return ($info['error_count'] === 0 && $info['warning_count'] === 0); } + + private function hasDateFormat() + { + return preg_match('/[djSFmMnYy]/', $this->format) > 0; + } + + private function hasValidDate(array $info) + { + if ($info['day']) { + return checkdate((int) $info['month'], $info['day'], (int) $info['year']); + } + + return checkdate($info['month'] ?: 1, $info['day'] ?: 1, $info['year'] ?: 1); + } } diff --git a/tests/unit/Rules/DateTest.php b/tests/unit/Rules/DateTest.php index 0b895592..b0262a6e 100644 --- a/tests/unit/Rules/DateTest.php +++ b/tests/unit/Rules/DateTest.php @@ -19,7 +19,7 @@ use DateTimeImmutable; * @covers Respect\Validation\Rules\Date * @covers Respect\Validation\Exceptions\DateException */ -class DateTest extends \PHPUnit_Framework_TestCase +class DateTest extends RuleTestCase { protected $dateValidator; @@ -150,4 +150,57 @@ class DateTest extends \PHPUnit_Framework_TestCase ['UTC', 'z', 320], ]; } + + /** + * {@inheritdoc} + */ + public function providerForValidInput() + { + return [ + [new Date(), 'now'], + [new Date(), 'today'], + [new Date(), 'tomorrow'], + [new Date(), 'yesterday'], + [new Date(), '+1 day'], + [new Date(), 'next Thursday'], + [new Date(), '+1 week 2 days 4 hours 2 seconds'], + [new Date(), 2018], + [new Date(), new DateTime()], + [new Date('Y-m-d'), '2009-09-09'], + [new Date('d/m/Y'), '23/05/1987'], + [new Date('c'), '2004-02-12T15:19:21+00:00'], + [new Date('r'), 'Thu, 29 Dec 2005 01:02:03 +0000'], + [new Date('U'), 1464658596], + [new Date('h'), 6], + [new Date('z'), 320], + [new Date('Ym'), 202302], + [new Date('m'), 12], + [new Date('Y'), 2000], + ]; + } + + /** + * {@inheritdoc} + */ + public function providerForInvalidInput() + { + return [ + [new Date(), 'not-a-date'], + [new Date(), []], + [new Date(), true], + [new Date(), false], + [new Date(), null], + [new Date(), ''], + [new Date('Y-m-d'), '2009-12-00'], + [new Date('Y-m-d'), '2018-02-29'], + [new Date('h'), 24], + [new Date(), '2014-99'], + [new Date('d'), 1], + [new Date('Y-m'), '2014-99'], + [new Date('m'), '99'], + [new Date('H'), '24'], + [new Date('i'), '60'], + [new Date('s'), '60'], + ]; + } }