Skip to content

Commit 9eb2125

Browse files
authored
TOML 1.1: Make seconds optional in Date-Time and Time (#203)
1 parent 12314bd commit 9eb2125

File tree

6 files changed

+20
-24
lines changed

6 files changed

+20
-24
lines changed

src/tomli/_re.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414

1515
from ._types import ParseFloat
1616

17-
# E.g.
18-
# - 00:32:00.999999
19-
# - 00:32:00
20-
_TIME_RE_STR: Final = (
21-
r"([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])(?:\.([0-9]{1,6})[0-9]*)?"
22-
)
17+
_TIME_RE_STR: Final = r"""
18+
([01][0-9]|2[0-3]) # hours
19+
:([0-5][0-9]) # minutes
20+
(?:
21+
:([0-5][0-9]) # optional seconds
22+
(?:\.([0-9]{1,6})[0-9]*)? # optional fractions of a second
23+
)?
24+
"""
2325

2426
RE_NUMBER: Final = re.compile(
2527
r"""
@@ -40,7 +42,7 @@
4042
""",
4143
flags=re.VERBOSE,
4244
)
43-
RE_LOCALTIME: Final = re.compile(_TIME_RE_STR)
45+
RE_LOCALTIME: Final = re.compile(_TIME_RE_STR, flags=re.VERBOSE)
4446
RE_DATETIME: Final = re.compile(
4547
rf"""
4648
([0-9]{{4}})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01]) # date, e.g. 1988-10-27
@@ -76,7 +78,8 @@ def match_to_datetime(match: re.Match[str]) -> datetime | date:
7678
year, month, day = int(year_str), int(month_str), int(day_str)
7779
if hour_str is None:
7880
return date(year, month, day)
79-
hour, minute, sec = int(hour_str), int(minute_str), int(sec_str)
81+
hour, minute = int(hour_str), int(minute_str)
82+
sec = int(sec_str) if sec_str else 0
8083
micros = int(micros_str.ljust(6, "0")) if micros_str else 0
8184
if offset_sign_str:
8285
tz: tzinfo | None = cached_tz(
@@ -105,8 +108,9 @@ def cached_tz(hour_str: str, minute_str: str, sign_str: str) -> timezone:
105108

106109
def match_to_localtime(match: re.Match[str]) -> time:
107110
hour_str, minute_str, sec_str, micros_str = match.groups()
111+
sec = int(sec_str) if sec_str else 0
108112
micros = int(micros_str.ljust(6, "0")) if micros_str else 0
109-
return time(int(hour_str), int(minute_str), int(sec_str), micros)
113+
return time(int(hour_str), int(minute_str), sec, micros)
110114

111115

112116
def match_to_number(match: re.Match[str], parse_float: ParseFloat) -> Any:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"local-dt": {"type":"datetime-local","value":"1988-10-27t01:01:01"},
3+
"local-dt-no-seconds": {"type":"datetime-local","value":"2025-04-18t20:05:00"},
34
"zulu-dt": {"type":"datetime","value":"1988-10-27t01:01:01z"}
45
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
local-dt=1988-10-27t01:01:01
2+
local-dt-no-seconds=2025-04-18T20:05
23
zulu-dt=1988-10-27t01:01:01z
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
{"t":
2-
{"type":"time-local","value":"00:00:00.999999"}}
2+
{"type":"time-local","value":"00:00:00.999999"},
3+
"t2":
4+
{"type":"time-local","value":"00:00:00"}}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
t=00:00:00.99999999999999
1+
t=00:00:00.99999999999999
2+
t2=00:00

tests/test_data.py

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,6 @@ def test_valid(self):
4141
for valid, expected in zip(VALID_FILES, VALID_FILES_EXPECTED):
4242
with self.subTest(msg=valid.stem):
4343
toml_str = valid.read_bytes().decode()
44-
45-
# xfail on TOML 1.1 compatibility tests that Tomli isn't
46-
# compatible with yet.
47-
if valid.stem in {
48-
"no-seconds",
49-
"common-34",
50-
"common-31",
51-
"common-29",
52-
}:
53-
with self.assertRaises(tomllib.TOMLDecodeError):
54-
tomllib.loads(toml_str)
55-
continue
56-
5744
actual = tomllib.loads(toml_str)
5845
actual = burntsushi.convert(actual) # type: ignore[no-untyped-call]
5946
expected = burntsushi.normalize(expected)

0 commit comments

Comments
 (0)