Make the behavior of DateTime more explicit

We decided to make the date format validation stricter[1]. Although
that's good, it could present some challenges for some people,
considering that the DateTime rule would be more flexible.

There are many cases in which PHP can parse a date but can't output it
the same way.

[1]: 5fe4b96ebf

Signed-off-by: Henrique Moody <henriquemoody@gmail.com>
This commit is contained in:
Henrique Moody 2024-02-04 15:52:36 +01:00
parent 5b7ea866e4
commit 5ba9b31cc8
No known key found for this signature in database
GPG key ID: 221E9281655813A6
3 changed files with 52 additions and 10 deletions

View file

@ -1,16 +1,20 @@
# DateTime
- `DateTime()`
- `DateTime(string $format)`
Validates whether an input is a date/time or not. The `$format` argument should
be in accordance to PHP's [date()](http://php.net/date) function.
Validates whether an input is a date/time or not.
The `$format` argument should be in accordance to [DateTime::format()][]. See more in the [Formats](#formats) section.
When a `$format` is not given its default value is `Y-m-d H:i:s`.
```php
v::dateTime()->validate('2009-01-01'); // true
```
Also accepts strtotime values:
Also accepts [strtotime()](http://php.net/strtotime) values:
```php
v::dateTime()->validate('now'); // true
@ -31,7 +35,26 @@ v::dateTime('Y-m-d')->validate('01-01-2009'); // false
Format has no effect when validating DateTime instances.
Message template for this validator includes `{{format}}`.
Message template for this validator includes `{{sample}}`.
## Formats
Note that this rule validates whether the input **matches a given [DateTime::format()][] format** and **NOT if the input
can be parsed with a given [DateTimeImmutable::createFromFormat()][] format**. That makes the validation stricter but
offers some limitations.
The way [DateTimeImmutable::createFromFormat()][] parses an input allows for many different conversions. Overall
[DateTimeImmutable::createFromFormat()][] tend to be more lenient than [DateTime::format()][]. This might be what
you desire, and you may want to use [Callback](Callback.md) to create a custom validation.
```php
$input = '2014-04-12T23:20:50.052Z';
v::callback(fn($input) => is_string($input) && DateTime::createFromFormat(DateTime::RFC3339_EXTENDED, $input))
->validate($input); // true
v::dateTime(DateTime::RFC3339_EXTENDED)->validate($input); // false
```
## Categorization
@ -39,10 +62,11 @@ Message template for this validator includes `{{format}}`.
## Changelog
Version | Description
--------|-------------
2.2.4 | `v::dateTime('z')` is no longer supported.
2.0.0 | Created
| Version | Description |
|---------|--------------------------------------------|
| 2.3.0 | Validation became a lot stricter |
| 2.2.4 | `v::dateTime('z')` is no longer supported. |
| 2.0.0 | Created |
***
See also:
@ -53,3 +77,6 @@ See also:
- [LeapYear](LeapYear.md)
- [MinAge](MinAge.md)
- [Time](Time.md)
[DateTimeImmutable::createFromFormat()]: https://www.php.net/datetimeimmutable.createfromformat
[DateTime::format()]: https://www.php.net/datetime.format

View file

@ -16,6 +16,8 @@ use function ltrim;
use function realpath;
use function Respect\Stringifier\stringify;
use function sprintf;
use function strrchr;
use function substr;
/**
* Abstract class to create TestCases for Rules.
@ -98,7 +100,11 @@ abstract class RuleTestCase extends TestCase
{
self::assertTrue(
$rule->validate($input),
sprintf('Validation with input %s is expected to pass', stringify($input))
sprintf(
'%s should pass with %s',
substr((string) strrchr($rule::class, '\\'), 1),
stringify($rule->reportError($input)->getParams())
)
);
}
@ -109,7 +115,11 @@ abstract class RuleTestCase extends TestCase
{
self::assertFalse(
$rule->validate($input),
sprintf('Validation with input %s it not expected to pass', stringify($input))
sprintf(
'%s should not pass with %s',
substr((string) strrchr($rule::class, '\\'), 1),
stringify($rule->reportError($input)->getParams())
)
);
}
}

View file

@ -11,6 +11,7 @@ namespace Respect\Validation\Rules;
use DateTime as DateTimeMutable;
use DateTimeImmutable;
use DateTimeInterface;
use Respect\Validation\Test\RuleTestCase;
use function date_default_timezone_get;
@ -102,6 +103,8 @@ final class DateTimeTest extends RuleTestCase
[new DateTime('U'), 1464658596],
[new DateTime('h'), 6],
[new DateTime('Ym'), 202305],
[new DateTime(DateTimeInterface::RFC3339), '2018-02-23T12:00:00+00:00'],
[new DateTime(DateTimeInterface::RFC3339_EXTENDED), '2024-02-04T14:14:47.000+00:00'],
];
}
@ -123,6 +126,8 @@ final class DateTimeTest extends RuleTestCase
[new DateTime('c'), new DateTimeMutable()],
[new DateTime('c'), new DateTimeImmutable()],
[new DateTime('Y-m-d H:i:s'), '21-3-123:12:01'],
[new DateTime(DateTimeInterface::RFC3339_EXTENDED), '2005-12-30T01:02:03Z'],
[new DateTime(DateTimeInterface::RFC3339_EXTENDED), '1937-01-01T12:00:27.87+00:20'],
];
}
}