Document parse_isotime() UTC behavior for naive timestamps

The parse_isotime() function treats datetime strings without timezone
designators as UTC rather than returning naive datetime objects as
specified by ISO 8601. This behavior exists for historical reasons
and backward compatibility.

Update the docstring to clearly document this behavior and point users
to alternatives (datetime.fromisoformat() or iso8601 library directly)
for ISO 8601 compliant parsing of naive datetime strings.

Add test to verify the documented behavior matches actual behavior.

Closes-Bug: #1638124

Assisted-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Change-Id: Ib80e86d08cbe62217120d2aa191b0c65e4ef434f
Signed-off-by: Pavlo Kostianov <pkostian@redhat.com>
This commit is contained in:
Pavlo Kostianov
2026-02-16 15:23:52 +00:00
parent 8d5f30a1a7
commit dbba34eeb1
3 changed files with 61 additions and 1 deletions

View File

@@ -62,6 +62,26 @@ class TimeUtilsTest(test_base.BaseTestCase):
)
self.assertEqual(skynet_self_aware_time_ms_utc, expect)
def test_parse_isotime_naive_treated_as_utc(self):
"""Verify naive timestamps are treated as UTC (documented behavior)."""
# Naive string without timezone should be treated as UTC
result = timeutils.parse_isotime('2012-02-14T20:53:07')
self.assertIsNotNone(result.tzinfo)
assert result.tzinfo is not None # for type checker
self.assertEqual(result.tzinfo.tzname(None), 'UTC')
# Explicit Z should also be UTC
result_z = timeutils.parse_isotime('2012-02-14T20:53:07Z')
assert result_z.tzinfo is not None # for type checker
self.assertEqual(result_z.tzinfo.tzname(None), 'UTC')
# Explicit offset should be respected
result_offset = timeutils.parse_isotime('2012-02-14T20:53:07+05:30')
self.assertIsNotNone(result_offset.tzinfo)
assert result_offset.tzinfo is not None # for type checker
offset = result_offset.tzinfo.utcoffset(None)
self.assertEqual(offset, datetime.timedelta(hours=5, minutes=30))
def test_parse_strtime(self):
perfect_time_format = self.skynet_self_aware_time_perfect_str
expect = timeutils.parse_strtime(perfect_time_format)

View File

@@ -41,7 +41,36 @@ now = time.monotonic
def parse_isotime(timestr: str) -> datetime.datetime:
"""Parse time from ISO 8601 format."""
"""Parse time from ISO 8601 format.
:param timestr: ISO 8601 formatted datetime string
:returns: A timezone-aware datetime.datetime instance
:raises ValueError: When the string cannot be parsed as ISO 8601
.. note::
For historical reasons, datetime strings without explicit timezone
designators (Z, +HH:MM, -HH:MM) are treated as UTC timestamps rather
than naive/local time as specified by ISO 8601. This behavior is
preserved for backward compatibility.
For ISO 8601 compliant parsing that returns naive datetime objects
when no timezone is specified, consider using
``datetime.datetime.fromisoformat()`` (Python 3.7+) or the ``iso8601``
library directly with ``default_timezone=None``.
Examples:
>>> # Strings without timezone designators are treated as UTC
>>> parse_isotime('2012-02-14T20:53:07')
datetime.datetime(2012, 2, 14, 20, 53, 7, tzinfo=<UTC>)
>>> # Explicit timezone designators are respected
>>> parse_isotime('2012-02-14T20:53:07Z')
datetime.datetime(2012, 2, 14, 20, 53, 7, tzinfo=<UTC>)
>>> parse_isotime('2012-02-14T20:53:07+05:30')
datetime.datetime(2012, 2, 14, 20, 53, 7, tzinfo=<+05:30>)
"""
try:
return iso8601.parse_date(timestr)
except iso8601.ParseError as e:

View File

@@ -0,0 +1,11 @@
---
fixes:
- |
`Bug #1638124 <https://bugs.launchpad.net/oslo.utils/+bug/1638124>`_:
The ``parse_isotime()`` function's docstring now clearly documents that
datetime strings without explicit timezone designators (Z, +HH:MM, -HH:MM)
are treated as UTC timestamps rather than naive/local time as specified
by ISO 8601. This behavior exists for historical reasons and backward
compatibility. The documentation now points users to alternatives
(``datetime.fromisoformat()`` or the ``iso8601`` library directly with
``default_timezone=None``) for ISO 8601 compliant parsing.