diff --git a/tests/test_items.py b/tests/test_items.py index bca3b51..040690a 100644 --- a/tests/test_items.py +++ b/tests/test_items.py @@ -844,6 +844,23 @@ def test_datetimes_behave_like_datetimes_fold() -> None: assert k.fold == 1 +def test_time_with_tzinfo_renders_as_local_time() -> None: + # TOML local times have no offset/timezone; a tz-aware time must + # render without its offset, otherwise the output is not valid TOML + # and this library's own parser cannot read it back. The offset is + # not representable in the format, so a round-trip yields the local + # (naive) time. + tz = timezone(timedelta(hours=2)) + i = item(time(7, 32, tzinfo=tz)) + assert i.as_string() == "07:32:00" + assert parse(f"t = {i.as_string()}")["t"] == time(7, 32) + + # replace() keeps the same rendering rule. + j = i.replace(hour=13) + assert j.as_string() == "13:32:00" + assert parse(f"t = {j.as_string()}")["t"] == time(13, 32) + + def test_strings_behave_like_strs() -> None: i = item("foo") diff --git a/tomlkit/items.py b/tomlkit/items.py index fb4661c..37eecc7 100644 --- a/tomlkit/items.py +++ b/tomlkit/items.py @@ -206,7 +206,7 @@ def item(value: Any, _parent: Item | None = None, _sort_keys: bool = False) -> I value.microsecond, value.tzinfo, Trivia(), - value.isoformat(), + _format_time(value), fold=value.fold, ) else: @@ -234,6 +234,18 @@ def item(value: Any, _parent: Item | None = None, _sort_keys: bool = False) -> I raise ConvertError(f"Unable to convert an object of {type(value)} to a TOML item") +def _format_time(value: time) -> str: + # TOML local times carry no offset or timezone (the spec's examples + # are plain ``07:32:00``); keeping the offset in the rendered raw + # value would produce a literal this library's own parser rejects. + # Built from the fields (not ``isoformat``/``replace``) so it stays + # correct even when called on a ``Time`` from ``Time._new``. + s = f"{value.hour:02d}:{value.minute:02d}:{value.second:02d}" + if value.microsecond: + s += f".{value.microsecond:06d}".rstrip("0") + return s + + class StringType(Enum): # Single Line Basic SLB = '"' @@ -1320,7 +1332,7 @@ def replace(self, *args: object, **kwargs: object) -> Time: return self._new(super().replace(*args, **kwargs)) # type: ignore[arg-type] def _new(self, result: time) -> Time: - raw = result.isoformat() + raw = _format_time(result) return Time( result.hour,