Skip to content

Commit 847aa7c

Browse files
committed
added DateTime::validate()
1 parent f06c25d commit 847aa7c

File tree

3 files changed

+138
-72
lines changed

3 files changed

+138
-72
lines changed

src/Utils/DateTime.php

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,8 @@ public static function fromParts(
7373
float $second = 0.0,
7474
): static
7575
{
76-
$s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second);
77-
if (
78-
!checkdate($month, $day, $year)
79-
|| $hour < 0
80-
|| $hour > 23
81-
|| $minute < 0
82-
|| $minute > 59
83-
|| $second < 0
84-
|| $second >= 60
85-
) {
86-
throw new Nette\InvalidArgumentException("Invalid date '$s'");
87-
}
88-
89-
return new static($s);
76+
self::validate($year, $month, $day, $hour, $minute, $second);
77+
return new static(sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second));
9078
}
9179

9280

@@ -95,7 +83,7 @@ public static function fromParts(
9583
*/
9684
public static function createFromFormat(
9785
string $format,
98-
string $time,
86+
string $datetime,
9987
string|\DateTimeZone|null $timezone = null,
10088
): static|false
10189
{
@@ -106,11 +94,39 @@ public static function createFromFormat(
10694
$timezone = new \DateTimeZone($timezone);
10795
}
10896

109-
$date = parent::createFromFormat($format, $time, $timezone);
97+
$date = parent::createFromFormat($format, $datetime, $timezone);
11098
return $date ? static::from($date) : false;
11199
}
112100

113101

102+
/**
103+
* Throws an exception if the date and time are not valid.
104+
*/
105+
public static function validate(
106+
int $year = 1,
107+
int $month = 1,
108+
int $day = 1,
109+
int $hour = 0,
110+
int $minute = 0,
111+
float $second = 0,
112+
int $microsecond = 0,
113+
): void
114+
{
115+
$microsecond2 = round($second * 1_000_000) % 1_000_000 + $microsecond;
116+
match (true) {
117+
$month < 1 || $month > 12 => throw new Nette\InvalidArgumentException("Month value ($month) is out of range."),
118+
$day < 1 || $day > 31 => throw new Nette\InvalidArgumentException("Day value ($day) is out of range."),
119+
$hour < 0 || $hour > 23 => throw new Nette\InvalidArgumentException("Hour value ($hour) is out of range."),
120+
$minute < 0 || $minute > 59 => throw new Nette\InvalidArgumentException("Minute value ($minute) is out of range."),
121+
$second < 0 || $second >= 60 => throw new Nette\InvalidArgumentException("Second value ($second) is out of range."),
122+
$microsecond < 0 || $microsecond >= 1_000_000 => throw new Nette\InvalidArgumentException("Microsecond value ($microsecond) is out of range."),
123+
$microsecond2 >= 1_000_000 => throw new Nette\InvalidArgumentException("Combination of second and microsecond ($microsecond2) is out of range."),
124+
!checkdate($month, $day, $year) => throw new Nette\InvalidArgumentException('The date ' . sprintf('%04d-%02d-%02d', $year, $month, $day) . ' is not valid.'),
125+
default => null,
126+
};
127+
}
128+
129+
114130
/**
115131
* Returns JSON representation in ISO 8601 (used by JavaScript).
116132
*/

tests/Utils/DateTime.check.phpt

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\Utils\DateTime::check().
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\Utils\DateTime;
10+
use Tester\Assert;
11+
12+
require __DIR__ . '/../bootstrap.php';
13+
14+
15+
date_default_timezone_set('Europe/Prague');
16+
17+
Assert::exception(
18+
fn() => DateTime::validate(1985, 2, 29),
19+
Nette\InvalidArgumentException::class,
20+
'The date 1985-02-29 is not valid.',
21+
);
22+
23+
Assert::exception(
24+
fn() => DateTime::validate(0, 12, 9),
25+
Nette\InvalidArgumentException::class,
26+
'The date 0000-12-09 is not valid.',
27+
);
28+
29+
Assert::exception(
30+
fn() => DateTime::validate(1985, 0, 9),
31+
Nette\InvalidArgumentException::class,
32+
'Month value (0) is out of range.',
33+
);
34+
35+
Assert::exception(
36+
fn() => DateTime::validate(1985, 13, 9),
37+
Nette\InvalidArgumentException::class,
38+
'Month value (13) is out of range.',
39+
);
40+
41+
Assert::exception(
42+
fn() => DateTime::validate(1985, 12, 0),
43+
Nette\InvalidArgumentException::class,
44+
'Day value (0) is out of range.',
45+
);
46+
47+
Assert::exception(
48+
fn() => DateTime::validate(1985, 12, 32),
49+
Nette\InvalidArgumentException::class,
50+
'Day value (32) is out of range.',
51+
);
52+
53+
Assert::exception(
54+
fn() => DateTime::validate(hour: -1),
55+
Nette\InvalidArgumentException::class,
56+
'Hour value (-1) is out of range.',
57+
);
58+
59+
Assert::exception(
60+
fn() => DateTime::validate(hour: 60),
61+
Nette\InvalidArgumentException::class,
62+
'Hour value (60) is out of range.',
63+
);
64+
65+
Assert::exception(
66+
fn() => DateTime::validate(minute: -1),
67+
Nette\InvalidArgumentException::class,
68+
'Minute value (-1) is out of range.',
69+
);
70+
71+
Assert::exception(
72+
fn() => DateTime::validate(minute: 60),
73+
Nette\InvalidArgumentException::class,
74+
'Minute value (60) is out of range.',
75+
);
76+
77+
Assert::exception(
78+
fn() => DateTime::validate(second: -1),
79+
Nette\InvalidArgumentException::class,
80+
'Second value (-1) is out of range.',
81+
);
82+
83+
Assert::exception(
84+
fn() => DateTime::validate(second: 60),
85+
Nette\InvalidArgumentException::class,
86+
'Second value (60) is out of range.',
87+
);
88+
89+
Assert::exception(
90+
fn() => DateTime::validate(microsecond: -1),
91+
Nette\InvalidArgumentException::class,
92+
'Microsecond value (-1) is out of range.',
93+
);
94+
95+
Assert::exception(
96+
fn() => DateTime::validate(microsecond: 1_000_000),
97+
Nette\InvalidArgumentException::class,
98+
'Microsecond value (1000000) is out of range.',
99+
);
100+
101+
Assert::exception(
102+
fn() => DateTime::validate(second: 1.5, microsecond: 500_000),
103+
Nette\InvalidArgumentException::class,
104+
'Combination of second and microsecond (1000000) is out of range.',
105+
);

tests/Utils/DateTime.fromParts.phpt

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -26,60 +26,5 @@ Assert::same('1985-12-09 11:22:59.123000', DateTime::fromParts(1985, 12, 9, 11,
2626
Assert::exception(
2727
fn() => DateTime::fromParts(1985, 2, 29),
2828
Nette\InvalidArgumentException::class,
29-
"Invalid date '1985-02-29 00:00:0.00000'",
30-
);
31-
32-
Assert::exception(
33-
fn() => DateTime::fromParts(0, 12, 9),
34-
Nette\InvalidArgumentException::class,
35-
);
36-
37-
Assert::exception(
38-
fn() => DateTime::fromParts(1985, 0, 9),
39-
Nette\InvalidArgumentException::class,
40-
);
41-
42-
Assert::exception(
43-
fn() => DateTime::fromParts(1985, 13, 9),
44-
Nette\InvalidArgumentException::class,
45-
);
46-
47-
Assert::exception(
48-
fn() => DateTime::fromParts(1985, 12, 0),
49-
Nette\InvalidArgumentException::class,
50-
);
51-
52-
Assert::exception(
53-
fn() => DateTime::fromParts(1985, 12, 32),
54-
Nette\InvalidArgumentException::class,
55-
);
56-
57-
Assert::exception(
58-
fn() => DateTime::fromParts(1985, 12, 9, -1),
59-
Nette\InvalidArgumentException::class,
60-
);
61-
62-
Assert::exception(
63-
fn() => DateTime::fromParts(1985, 12, 9, 60),
64-
Nette\InvalidArgumentException::class,
65-
);
66-
67-
Assert::exception(
68-
fn() => DateTime::fromParts(1985, 12, 9, 0, -1),
69-
Nette\InvalidArgumentException::class,
70-
);
71-
72-
Assert::exception(
73-
fn() => DateTime::fromParts(1985, 12, 9, 0, 60),
74-
Nette\InvalidArgumentException::class,
75-
);
76-
77-
Assert::exception(
78-
fn() => DateTime::fromParts(1985, 12, 9, 0, 0, -1),
79-
Nette\InvalidArgumentException::class,
80-
);
81-
82-
Assert::exception(
83-
fn() => DateTime::fromParts(1985, 12, 9, 0, 0, 60),
84-
Nette\InvalidArgumentException::class,
29+
'The date 1985-02-29 is not valid.',
8530
);

0 commit comments

Comments
 (0)