Skip to content

Commit

Permalink
Make the behavior of DateTime more explicit
Browse files Browse the repository at this point in the history
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]: 5fe4b96

Signed-off-by: Henrique Moody <[email protected]>
  • Loading branch information
henriquemoody committed Feb 4, 2024
1 parent 5b7ea86 commit 5ba9b31
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 10 deletions.
43 changes: 35 additions & 8 deletions docs/rules/DateTime.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -31,18 +35,38 @@ 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

- Date and Time

## 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:
Expand All @@ -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
14 changes: 12 additions & 2 deletions tests/library/RuleTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use function realpath;
use function Respect\Stringifier\stringify;
use function sprintf;
use function strrchr;
use function substr;

/**
* Abstract class to create TestCases for Rules.
Expand Down Expand Up @@ -98,7 +100,11 @@ public static function assertValidInput(Validatable $rule, $input): void
{
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())
)
);
}

Expand All @@ -109,7 +115,11 @@ public static function assertInvalidInput(Validatable $rule, $input): void
{
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())
)
);
}
}
5 changes: 5 additions & 0 deletions tests/unit/Rules/DateTimeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use DateTime as DateTimeMutable;
use DateTimeImmutable;
use DateTimeInterface;
use Respect\Validation\Test\RuleTestCase;

use function date_default_timezone_get;
Expand Down Expand Up @@ -102,6 +103,8 @@ public static function providerForValidInput(): array
[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'],
];
}

Expand All @@ -123,6 +126,8 @@ public static function providerForInvalidInput(): array
[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'],
];
}
}

0 comments on commit 5ba9b31

Please sign in to comment.