Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 7683b6f

Browse files
authored
Merge pull request #57 from programmatordev/YAPV-28-create-datetime-rule
Create DateTime rule
2 parents 5442cc1 + e03376f commit 7683b6f

File tree

10 files changed

+172
-3
lines changed

10 files changed

+172
-3
lines changed

docs/03-rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
## Date Rules
3333

34+
- [DateTime](03-rules_date-time.md)
3435
- [Timezone](03-rules_timezone.md)
3536

3637
## Choice Rules

docs/03-rules_date-time.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# DateTime
2+
3+
Validates that a given value is a valid datetime in a specific format.
4+
5+
```php
6+
DateTime(
7+
string $format = 'Y-m-d H:i:s',
8+
?string $message = null
9+
);
10+
```
11+
12+
## Basic Usage
13+
14+
```php
15+
// default "Y-m-d H:i:s"
16+
Validator::dateTime()->validate('2024-01-01 00:00:00'); // true
17+
Validator::dateTime()->validate('2024-01-01'); // false
18+
19+
// validate date
20+
Validator::dateTime(format: 'Y-m-d')->validate('2024-01-01'); // true
21+
Validator::dateTime(format: 'Y-m-d')->validate('2024-01-35'); // false
22+
23+
// validate time
24+
Validator::dateTime(format: 'H:i:s')->validate('21:00:00'); // true
25+
Validator::dateTime(format: 'H:i:s')->validate('35:00:00'); // false
26+
```
27+
28+
> [!NOTE]
29+
> An `UnexpectedValueException` will be thrown when the input value is not a `string` or an object implementing `\Stringable`.
30+
31+
## Options
32+
33+
### `format`
34+
35+
type: `string` default: `Y-m-d H:i:s`
36+
37+
Format of the datetime to be validated.
38+
Check all formatting options [here](https://www.php.net/manual/en/datetimeimmutable.createfromformat.php).
39+
40+
### `message`
41+
42+
type: `?string` default: `The {{ name }} value is not a valid datetime.`
43+
44+
Message that will be shown when the input value is not a valid datetime.
45+
46+
The following parameters are available:
47+
48+
| Parameter | Description |
49+
|----------------|---------------------------|
50+
| `{{ value }}` | The current invalid value |
51+
| `{{ name }}` | Name of the invalid value |
52+
| `{{ format }}` | The datetime format |
53+
54+
## Changelog
55+
56+
- `0.8.0` Created

docs/03-rules_regex.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Validator::regex('/[a-z]/', match: false)->validate('123'); // true
3333

3434
### `pattern`
3535

36-
type: `string`
36+
type: `string` `required`
3737

3838
Regular expression pattern to be matched against.
3939

src/ChainedValidatorInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ public function country(
3131
?string $message = null
3232
): ChainedValidatorInterface&Validator;
3333

34+
public function dateTime(
35+
string $format = 'Y-m-d H:i:s',
36+
?string $message = null
37+
): ChainedValidatorInterface&Validator;
38+
3439
public function eachKey(
3540
Validator $validator,
3641
?string $message = null
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Exception;
4+
5+
class DateTimeException extends ValidationException {}

src/Rule/DateTime.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Rule;
4+
5+
use ProgrammatorDev\Validator\Exception\DateTimeException;
6+
use ProgrammatorDev\Validator\Exception\UnexpectedTypeException;
7+
8+
class DateTime extends AbstractRule implements RuleInterface
9+
{
10+
private string $message = 'The {{ name }} value is not a valid datetime.';
11+
12+
public function __construct(
13+
private readonly string $format = 'Y-m-d H:i:s',
14+
?string $message = null
15+
)
16+
{
17+
$this->message = $message ?? $this->message;
18+
}
19+
20+
public function assert(mixed $value, ?string $name = null): void
21+
{
22+
if (!\is_scalar($value) && !$value instanceof \Stringable) {
23+
throw new UnexpectedTypeException('string|\Stringable', get_debug_type($value));
24+
}
25+
26+
$value = (string) $value;
27+
28+
\DateTimeImmutable::createFromFormat($this->format, $value);
29+
30+
$errors = \DateTimeImmutable::getLastErrors();
31+
32+
if ($errors !== false && ($errors['error_count'] > 0 || $errors['warning_count'] > 0)) {
33+
throw new DateTimeException(
34+
message: $this->message,
35+
parameters: [
36+
'name' => $name,
37+
'value' => $value,
38+
'format' => $this->format
39+
]
40+
);
41+
}
42+
}
43+
}

src/Rule/Type.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public function assert(mixed $value, ?string $name = null): void
9393
}
9494

9595
if (!isset(self::TYPE_FUNCTIONS[$constraint]) && !\class_exists($constraint) && !\interface_exists($constraint)) {
96-
throw new UnexpectedOptionException('constraint type', self::TYPE_FUNCTIONS, $constraint);
96+
throw new UnexpectedOptionException('constraint type', \array_keys(self::TYPE_FUNCTIONS), $constraint);
9797
}
9898
}
9999

src/StaticValidatorInterface.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ public static function country(
3030
?string $message = null
3131
): ChainedValidatorInterface&Validator;
3232

33+
public static function dateTime(
34+
string $format = 'Y-m-d H:i:s',
35+
?string $message = null
36+
): ChainedValidatorInterface&Validator;
37+
3338
public static function eachKey(
3439
Validator $validator,
3540
?string $message = null

tests/DateTimeTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace ProgrammatorDev\Validator\Test;
4+
5+
use ProgrammatorDev\Validator\Exception\DateTimeException;
6+
use ProgrammatorDev\Validator\Rule\DateTime;
7+
use ProgrammatorDev\Validator\Test\Util\TestRuleFailureConditionTrait;
8+
use ProgrammatorDev\Validator\Test\Util\TestRuleMessageOptionTrait;
9+
use ProgrammatorDev\Validator\Test\Util\TestRuleSuccessConditionTrait;
10+
use ProgrammatorDev\Validator\Test\Util\TestRuleUnexpectedValueTrait;
11+
12+
class DateTimeTest extends AbstractTest
13+
{
14+
use TestRuleUnexpectedValueTrait;
15+
use TestRuleFailureConditionTrait;
16+
use TestRuleSuccessConditionTrait;
17+
use TestRuleMessageOptionTrait;
18+
19+
public static function provideRuleUnexpectedValueData(): \Generator
20+
{
21+
$unexpectedTypeMessage = '/Expected value of type "string|\Stringable", "(.*)" given./';
22+
23+
yield 'invalid value type' => [new DateTime(), ['2024-01-01 00:00:00'], $unexpectedTypeMessage];
24+
}
25+
26+
public static function provideRuleFailureConditionData(): \Generator
27+
{
28+
$exception = DateTimeException::class;
29+
$message = '/The (.*) value is not a valid datetime./';
30+
31+
yield 'invalid format' => [new DateTime(format: 'invalid'), '2024-01-01 00:00:00', $exception, $message];
32+
yield 'invalid datetime' => [new DateTime(), '2024-01-01', $exception, $message];
33+
yield 'invalid overflow date' => [new DateTime(format: 'Y-m-d'), '2024-01-35', $exception, $message];
34+
yield 'invalid overflow time' => [new DateTime(format: 'H:i:s'), '35:00:00', $exception, $message];
35+
}
36+
37+
public static function provideRuleSuccessConditionData(): \Generator
38+
{
39+
yield 'datetime' => [new DateTime(), '2024-01-01 00:00:00'];
40+
yield 'date' => [new DateTime(format: 'Y-m-d'), '2024-01-01'];
41+
yield 'time' => [new DateTime(format: 'H:i:s'), '21:00:00'];
42+
}
43+
44+
public static function provideRuleMessageOptionData(): \Generator
45+
{
46+
yield 'message' => [
47+
new DateTime(
48+
message: 'The {{ name }} datetime does not match the format {{ format }}.'
49+
),
50+
'2024-01-01',
51+
'The test datetime does not match the format "Y-m-d H:i:s".'
52+
];
53+
}
54+
}

tests/RegexTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class RegexTest extends AbstractTest
1919
public static function provideRuleUnexpectedValueData(): \Generator
2020
{
2121
$unexpectedPatternMessage = '/Invalid regular expression pattern./';
22-
$unexpectedTypeMessage = '/Expected value of type "array|\Stringable", "(.*)" given./';
22+
$unexpectedTypeMessage = '/Expected value of type "string|\Stringable", "(.*)" given./';
2323

2424
yield 'invalid pattern' => [new Regex('invalid'), 'abc', $unexpectedPatternMessage];
2525
yield 'invalid value type' => [new Regex('/[a-z]/'), ['abc'], $unexpectedTypeMessage];

0 commit comments

Comments
 (0)