|
9 | 9 |
|
10 | 10 | * Absent input (``None``) -> ``None``. |
11 | 11 | * Well-formed Server form -> UTC-aware ``datetime``. |
12 | | -* Well-formed Cloud form -> aware ``datetime`` with the on-the-wire offset |
13 | | - preserved (**not** normalised to UTC). |
| 12 | +* Well-formed Cloud form -> UTC-aware ``datetime``; the wire's numeric offset |
| 13 | + is converted to UTC on parse so every returned ``datetime`` carries the same |
| 14 | + ``tzinfo`` regardless of which wire form arrived. |
14 | 15 | * Unparseable non-empty input -> ``None``. A malformed server response should |
15 | 16 | not crash a page-through of unrelated data. This matches the pre-Cloud |
16 | 17 | behaviour. |
@@ -60,14 +61,21 @@ def test_parse_datetime_server_z_form(): |
60 | 61 | assert timedelta(0) == result.utcoffset() |
61 | 62 |
|
62 | 63 |
|
63 | | -def test_parse_datetime_cloud_offset_form_preserved(): |
| 64 | +def test_parse_datetime_cloud_offset_form_normalised_to_utc(): |
64 | 65 | result = parse_datetime("2026-08-29T16:55:00-0700") |
65 | 66 | assert result is not None |
66 | | - # Cloud offsets are deliberately kept -- a future .replace(tzinfo=utc) |
67 | | - # after strptime would silently shift the instant. Lock that in. |
68 | | - assert timedelta(hours=-7) == result.utcoffset() |
| 67 | + # The Cloud branch normalises to UTC via astimezone(utc) so every parsed |
| 68 | + # datetime carries the same tzinfo regardless of wire form. A future |
| 69 | + # regression that drops the astimezone would leave tzinfo == timezone(-7:00) |
| 70 | + # and break callers doing .strftime("...Z") or == against a UTC constant. |
| 71 | + assert timedelta(0) == result.utcoffset() |
| 72 | + assert result.tzinfo is utc |
| 73 | + # 16:55 -07:00 == 23:55 UTC on the same wall date. |
69 | 74 | assert 2026 == result.year |
70 | | - assert 16 == result.hour |
| 75 | + assert 8 == result.month |
| 76 | + assert 29 == result.day |
| 77 | + assert 23 == result.hour |
| 78 | + assert 55 == result.minute |
71 | 79 |
|
72 | 80 |
|
73 | 81 | def test_parse_datetime_cloud_offset_with_colon(): |
@@ -129,7 +137,10 @@ def test_property_is_datetime_accepts_valid_cloud_string(): |
129 | 137 | holder = _DateHolder() |
130 | 138 | holder.created_at = "2026-08-29T16:55:00-0700" |
131 | 139 | assert holder._value is not None |
132 | | - assert timedelta(hours=-7) == holder._value.utcoffset() |
| 140 | + # parse_datetime normalises Cloud offsets to UTC on the read side, and the |
| 141 | + # property decorator funnels through parse_datetime for string input. |
| 142 | + assert timedelta(0) == holder._value.utcoffset() |
| 143 | + assert holder._value.tzinfo is utc |
133 | 144 |
|
134 | 145 |
|
135 | 146 | def test_property_is_datetime_accepts_datetime_instance(): |
|
0 commit comments