Skip to content

Commit 2be0be3

Browse files
authored
Merge pull request #2506 from kubernetes-client/copilot/fix-parse-rfc3339-error
Fix parse_rfc3339 AttributeError when timezone regex fails to match
2 parents e81db31 + 41681d2 commit 2be0be3

2 files changed

Lines changed: 30 additions & 2 deletions

File tree

kubernetes/base/config/dateutil.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,15 @@ def parse_rfc3339(s):
7171
us = int(MICROSEC_PER_SEC * partial_sec)
7272

7373
tz = UTC
74-
if groups[7] is not None and groups[7] != 'Z' and groups[7] != 'z':
75-
tz_groups = _re_timezone.search(groups[7]).groups()
74+
if groups[7] is not None and groups[7] not in ('Z', 'z', ' '):
75+
tz_match = _re_timezone.search(groups[7])
76+
if tz_match is None:
77+
raise ValueError(
78+
f"Invalid timezone format in RFC3339 string {s!r}: "
79+
f"timezone part {groups[7]!r} does not match expected "
80+
f"format (±HH:MM)"
81+
)
82+
tz_groups = tz_match.groups()
7683
hour = int(tz_groups[1])
7784
minute = 0
7885
if tz_groups[0] == "-":

kubernetes/base/config/dateutil_test.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,24 @@ def test_parse_rfc3339_error_message_clarity(self):
102102
self.assertIn("Invalid RFC3339", error_msg)
103103
self.assertIn("YYYY-MM-DD", error_msg)
104104
self.assertIn("expected", error_msg)
105+
106+
def test_parse_rfc3339_handles_none_from_timezone_regex(self):
107+
"""Test parse_rfc3339 handles timezone regex returning None.
108+
109+
This test addresses the GitHub issue where parse_rfc3339 was
110+
calling .groups() on None when the timezone regex failed to match,
111+
causing: 'NoneType' object has no attribute 'groups'
112+
113+
The fix adds a check to ensure _re_timezone.search() result is
114+
not None before calling .groups(), and provides a clear error
115+
message.
116+
"""
117+
# The main RFC3339 regex allows space in timezone position: [zZ ]
118+
# If a space ends up in groups[7], it should be handled gracefully
119+
# Since the current code uses strip(), trailing spaces are removed,
120+
# but the fix ensures robustness for any edge case
121+
122+
# Test that space in timezone is treated as UTC (like Z/z)
123+
actual = parse_rfc3339("2017-07-25 04:44:21")
124+
expected = datetime(2017, 7, 25, 4, 44, 21, 0, UTC)
125+
self.assertEqual(expected, actual)

0 commit comments

Comments
 (0)