Skip to content

Commit f7f7ee6

Browse files
committed
Improve regex for xs:time and xs:dateTime; allow up to twelve decimal seconds, and prohibit the hour 24 and decimal seconds ending with 0
1 parent 61ec392 commit f7f7ee6

File tree

4 files changed

+46
-5
lines changed

4 files changed

+46
-5
lines changed

src/XML/Assert/DateTimeTrait.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ trait DateTimeTrait
3030
* * ss is a two-integer-digit numeral that represents the whole seconds;
3131
* * '.' s+ (if present) represents the fractional seconds;
3232
* * zzzzzz (if present) represents the timezone (as described below).
33+
*
34+
* Except for trailing fractional zero digits in the seconds representation, '24:00:00' time representations,
35+
* and timezone (for timezoned values), the mapping from literals to values is one-to-one.
36+
* Where there is more than one possible representation, the canonical representation is as follows:
37+
38+
* The 2-digit numeral representing the hour must not be '24';
39+
* The fractional second string, if present, must not end in '0';
40+
* for timezoned values, the timezone must be represented with 'Z' (All timezoned dateTime values are UTC.).
41+
42+
43+
* Note: we're restricting decimal seconds to 12, although strictly the standards allow an infite number.
3344
*/
3445
private static string $datetime_regex = '/^
3546
-?
@@ -49,10 +60,10 @@ trait DateTimeTrait
4960
(02-(0[1-9]|(1|2)[0-9]))
5061
)
5162
T
52-
([0-1][0-9]|2[0-4])
63+
([0-1][0-9]|2[0-3])
5364
:(0[0-9]|[1-5][0-9])
5465
:(0[0-9]|[1-5][0-9])
55-
(\.[0-9]{0,6})?
66+
(\.[0-9]{0,11}[1-9])?
5667
(
5768
[+-]([0-1][0-9]|2[0-4])
5869
:(0[0-9]|[1-5][0-9])

src/XML/Assert/TimeTrait.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,35 @@
1111
*/
1212
trait TimeTrait
1313
{
14+
/**
15+
* The ·lexical space· of time consists of finite-length sequences of characters of the form:
16+
* hh ':' mm ':' ss ('.' s+)? (zzzzzz)?, where
17+
*
18+
* * hh is a two-digit numeral that represents the hour; '24' is permitted if the minutes and seconds represented
19+
* are zero, and the dateTime value so represented is the first instant of the following day
20+
* (the hour property of a dateTime object in the ·value space· cannot have a value greater than 23);
21+
* * ':' is a separator between parts of the time-of-day portion;
22+
* * the second mm is a two-digit numeral that represents the minute;
23+
* * ss is a two-integer-digit numeral that represents the whole seconds;
24+
* * '.' s+ (if present) represents the fractional seconds;
25+
* * zzzzzz (if present) represents the timezone (as described below).
26+
*
27+
* Except for trailing fractional zero digits in the seconds representation, '24:00:00' time representations,
28+
* and timezone (for timezoned values), the mapping from literals to values is one-to-one.
29+
* Where there is more than one possible representation, the canonical representation is as follows:
30+
*
31+
* The 2-digit numeral representing the hour must not be '24';
32+
* The fractional second string, if present, must not end in '0';
33+
* for timezoned values, the timezone must be represented with 'Z' (All timezoned dateTime values are UTC.).
34+
*
35+
* Note: we're restricting decimal seconds to 12, although strictly the standards allow an infite number.
36+
*/
1437
private static string $time_regex = '/^
15-
([0-1][0-9]|2[0-4])
38+
([0-1][0-9]|2[0-3])
1639
:
1740
(0[0-9]|[1-5][0-9])
1841
:(0[0-9]|[1-5][0-9])
19-
(\.[0-9]{0,6})?
42+
(\.[0-9]{0,11}[1-9])?
2043
([+-]([0-1][0-9]|2[0-4]):(0[0-9]|[1-5][0-9])|Z)?
2144
$/Dx';
2245

tests/XML/Assert/DateTimeTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static function provideValidDateTime(): array
4848
'valid with negative value' => [true, '-2001-10-26T21:32:52'],
4949
'valid with subseconds' => [true, '2001-10-26T21:32:52.12679'],
5050
'valid with more than four digit year' => [true, '-22001-10-26T21:32:52+02:00'],
51-
'valid with sub-seconds' => [true, '2001-10-26T21:32:52.12679'],
51+
'valid with up to twelve sub-seconds' => [true, '2001-10-26T21:32:52.126798764382'],
5252
];
5353
}
5454

@@ -62,9 +62,12 @@ public static function provideInvalidDateTime(): array
6262
'missing time' => [false, '2001-10-26'],
6363
'missing second' => [false, '2001-10-26T21:32'],
6464
'hour out of range' => [false, '2001-10-26T25:32:52+02:00'],
65+
'hour twenty-four' => [false, '2001-10-26T24:32:52+02:00'],
6566
'year 0000' => [false, '0000-10-26T25:32:52+02:00'],
6667
'prefixed zero' => [false, '02001-10-26T25:32:52+02:00'],
6768
'wrong format' => [false, '01-10-26T21:32'],
69+
'too many sub-seconds' => [false, '2001-10-26T21:32:52.1267987643821'],
70+
'sub-seconds ending with zero' => [false, '2001-10-26T21:32:52.12670'],
6871
];
6972
}
7073
}

tests/XML/Assert/TimeTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public static function provideValidTime(): array
4646
'valid time with Zulu timezone' => [true, '19:32:52Z'],
4747
'valid time with 00:00 timezone' => [true, '19:32:52+00:00'],
4848
'valid time with sub-seconds' => [true, '21:32:52.12679'],
49+
'valid with up to twelve sub-seconds' => [true, '21:32:52.126798764382'],
4950
];
5051
}
5152

@@ -58,7 +59,10 @@ public static function provideInvalidTime(): array
5859
return [
5960
'invalid negative hour' => [false, '-10:00:00'],
6061
'invalid hour out of range' => [false, '25:25:10'],
62+
'invalid hour twenty-four' => [false, '24:25:10'],
6163
'invalid invalid format' => [false, '1:20:10'],
64+
'too many sub-seconds' => [false, '21:32:52.1267987643821'],
65+
'sub-seconds ending with zero' => [false, '21:32:52.12670'],
6266
];
6367
}
6468
}

0 commit comments

Comments
 (0)