Skip to content

Commit 60ed797

Browse files
authored
Merge pull request #347 from 15r10nk/fix-timezone
fix: datetime with timezone
2 parents a0c0064 + a8ddaa1 commit 60ed797

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!--
2+
A new scriv changelog fragment.
3+
4+
Uncomment the section that is right (remove the HTML comment wrapper).
5+
For top level release notes, leave all the headers commented out.
6+
-->
7+
8+
<!--
9+
### Removed
10+
11+
- A bullet item for the Removed category.
12+
13+
-->
14+
<!--
15+
### Added
16+
17+
- A bullet item for the Added category.
18+
19+
-->
20+
<!--
21+
### Changed
22+
23+
- A bullet item for the Changed category.
24+
25+
-->
26+
<!--
27+
### Deprecated
28+
29+
- A bullet item for the Deprecated category.
30+
31+
-->
32+
33+
### Fixed
34+
35+
- Fixed handling of `datetime` and `time` objects with timezone information. The `tzinfo` parameter is now properly included in snapshots, and `timezone.utc` is represented with the correct import (`from datetime import timezone`).
36+
37+
<!--
38+
### Security
39+
40+
- A bullet item for the Security category.
41+
42+
-->

src/inline_snapshot/plugin/_default_plugin.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ def builtin_function_handler(self, value, builder: Builder):
7676
if isinstance(value, BuiltinFunctionType):
7777
return builder.create_code(value.__name__)
7878

79+
@customize
80+
def timezone_handler(self, value, builder: Builder):
81+
if isinstance(value, datetime.timezone):
82+
# Handle timezone.utc specially - it's a constant, not a constructor call
83+
if value == datetime.timezone.utc:
84+
return builder.create_code(
85+
"timezone.utc",
86+
imports=[ImportFrom("datetime", "timezone")],
87+
)
88+
89+
# For other timezone objects, use the constructor
90+
offset = value.utcoffset(None)
91+
tzname = value.tzname(None)
92+
return builder.create_call(datetime.timezone, [offset, tzname])
93+
7994
@customize
8095
def datetime_handler(self, value, builder: Builder):
8196

@@ -88,6 +103,7 @@ def datetime_handler(self, value, builder: Builder):
88103
"minute": builder.with_default(value.minute, 0),
89104
"second": builder.with_default(value.second, 0),
90105
"microsecond": builder.with_default(value.microsecond, 0),
106+
"tzinfo": builder.with_default(value.tzinfo, None),
91107
},
92108
)
93109

@@ -105,6 +121,7 @@ def datetime_handler(self, value, builder: Builder):
105121
"minute": builder.with_default(value.minute, 0),
106122
"second": builder.with_default(value.second, 0),
107123
"microsecond": builder.with_default(value.microsecond, 0),
124+
"tzinfo": builder.with_default(value.tzinfo, None),
108125
},
109126
)
110127

tests/test_datetime_timezone.py

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
from inline_snapshot import snapshot
2+
from inline_snapshot.testing import Example
3+
4+
5+
def test_datetime_with_timezone():
6+
Example(
7+
{
8+
"test_something.py": """\
9+
from datetime import datetime, timezone
10+
from inline_snapshot import snapshot
11+
12+
13+
def test_datetime():
14+
dt = datetime(2026, 2, 16, 5, 0, tzinfo=timezone.utc)
15+
assert dt == snapshot()
16+
""",
17+
}
18+
).run_inline(
19+
["--inline-snapshot=create"],
20+
changed_files=snapshot(
21+
{
22+
"test_something.py": """\
23+
from datetime import datetime, timezone
24+
from inline_snapshot import snapshot
25+
26+
27+
def test_datetime():
28+
dt = datetime(2026, 2, 16, 5, 0, tzinfo=timezone.utc)
29+
assert dt == snapshot(datetime(2026, 2, 16, hour=5, tzinfo=timezone.utc))
30+
"""
31+
}
32+
),
33+
).run_inline()
34+
35+
36+
def test_time_with_timezone():
37+
Example(
38+
{
39+
"test_something.py": """\
40+
from datetime import time, timezone
41+
from inline_snapshot import snapshot
42+
43+
44+
def test_time():
45+
t = time(12, 30, tzinfo=timezone.utc)
46+
assert t == snapshot()
47+
""",
48+
}
49+
).run_inline(
50+
["--inline-snapshot=create"],
51+
changed_files=snapshot(
52+
{
53+
"test_something.py": """\
54+
from datetime import time, timezone
55+
from inline_snapshot import snapshot
56+
57+
58+
def test_time():
59+
t = time(12, 30, tzinfo=timezone.utc)
60+
assert t == snapshot(time(hour=12, minute=30, tzinfo=timezone.utc))
61+
"""
62+
}
63+
),
64+
).run_inline()
65+
66+
67+
def test_custom_timezone_with_offset_only():
68+
Example(
69+
{
70+
"test_something.py": """\
71+
from datetime import datetime, timedelta, timezone
72+
from inline_snapshot import snapshot
73+
74+
75+
def test_custom_tz():
76+
tz = timezone(timedelta(hours=5))
77+
dt = datetime(2026, 2, 16, 5, 0, tzinfo=tz)
78+
assert dt == snapshot()
79+
""",
80+
}
81+
).run_inline(
82+
["--inline-snapshot=create"],
83+
changed_files=snapshot(
84+
{
85+
"test_something.py": """\
86+
from datetime import datetime, timedelta, timezone
87+
from inline_snapshot import snapshot
88+
89+
90+
def test_custom_tz():
91+
tz = timezone(timedelta(hours=5))
92+
dt = datetime(2026, 2, 16, 5, 0, tzinfo=tz)
93+
assert dt == snapshot(
94+
datetime(
95+
2026, 2, 16, hour=5, tzinfo=timezone(timedelta(seconds=18000), "UTC+05:00")
96+
)
97+
)
98+
"""
99+
}
100+
),
101+
).run_inline()
102+
103+
104+
def test_custom_timezone_with_name():
105+
Example(
106+
{
107+
"test_something.py": """\
108+
from datetime import datetime, timedelta, timezone
109+
from inline_snapshot import snapshot
110+
111+
112+
def test_custom_tz_named():
113+
tz = timezone(timedelta(hours=-5), "EST")
114+
dt = datetime(2026, 2, 16, 5, 0, tzinfo=tz)
115+
assert dt == snapshot()
116+
""",
117+
}
118+
).run_inline(
119+
["--inline-snapshot=create"],
120+
changed_files=snapshot(
121+
{
122+
"test_something.py": """\
123+
from datetime import datetime, timedelta, timezone
124+
from inline_snapshot import snapshot
125+
126+
127+
def test_custom_tz_named():
128+
tz = timezone(timedelta(hours=-5), "EST")
129+
dt = datetime(2026, 2, 16, 5, 0, tzinfo=tz)
130+
assert dt == snapshot(
131+
datetime(
132+
2026,
133+
2,
134+
16,
135+
hour=5,
136+
tzinfo=timezone(timedelta(days=-1, seconds=68400), "EST"),
137+
)
138+
)
139+
"""
140+
}
141+
),
142+
).run_inline()

0 commit comments

Comments
 (0)