Skip to content

Commit c2f767a

Browse files
authored
Merge pull request #18 from glyph/datetime-sub-different-tzinfo
adjust `DateTime.__sub__` to allow differing tzinfo implementations
2 parents 731fdd6 + 12901c2 commit c2f767a

5 files changed

Lines changed: 62 additions & 4 deletions

File tree

expected_mypy.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,18 @@ tryit.py:53: note: Got:
4444
tryit.py:53: note: def timetz(self) -> Time[timezone]
4545
tryit.py:53: note: tzinfo: expected "None", got "timezone"
4646
tryit.py:56: error: Incompatible types in assignment (expression has type "NaiveTime", variable has type "DateTime[timezone]") [assignment]
47-
Found 10 errors in 1 file (checked 1 source file)
47+
tryit.py:58: error: Unsupported operand types for - ("NaiveDateTime" and "AwareDateTime") [operator]
48+
tryit.py:58: note: Following member(s) of "AwareDateTime" have conflicts:
49+
tryit.py:58: note: Expected:
50+
tryit.py:58: note: def timetz(self) -> Time[None]
51+
tryit.py:58: note: Got:
52+
tryit.py:58: note: def timetz(self) -> Time[tzinfo]
53+
tryit.py:58: note: tzinfo: expected "None", got "tzinfo"
54+
tryit.py:59: error: Unsupported operand types for - ("AwareDateTime" and "NaiveDateTime") [operator]
55+
tryit.py:59: note: Following member(s) of "NaiveDateTime" have conflicts:
56+
tryit.py:59: note: Expected:
57+
tryit.py:59: note: def timetz(self) -> Time[tzinfo]
58+
tryit.py:59: note: Got:
59+
tryit.py:59: note: def timetz(self) -> Time[None]
60+
tryit.py:59: note: tzinfo: expected "tzinfo", got "None"
61+
Found 12 errors in 1 file (checked 1 source file)

expected_mypy_37.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,18 @@ tryit.py:53: note: Got:
3030
tryit.py:53: note: def timetz(self) -> Time[timezone]
3131
tryit.py:53: note: tzinfo: expected "None", got "timezone"
3232
tryit.py:56: error: Incompatible types in assignment (expression has type "NaiveTime", variable has type "DateTime[timezone]") [assignment]
33-
Found 8 errors in 1 file (checked 1 source file)
33+
tryit.py:58: error: Unsupported operand types for - ("NaiveDateTime" and "AwareDateTime") [operator]
34+
tryit.py:58: note: Following member(s) of "AwareDateTime" have conflicts:
35+
tryit.py:58: note: Expected:
36+
tryit.py:58: note: def timetz(self) -> Time[None]
37+
tryit.py:58: note: Got:
38+
tryit.py:58: note: def timetz(self) -> Time[tzinfo]
39+
tryit.py:58: note: tzinfo: expected "None", got "tzinfo"
40+
tryit.py:59: error: Unsupported operand types for - ("AwareDateTime" and "NaiveDateTime") [operator]
41+
tryit.py:59: note: Following member(s) of "NaiveDateTime" have conflicts:
42+
tryit.py:59: note: Expected:
43+
tryit.py:59: note: def timetz(self) -> Time[tzinfo]
44+
tryit.py:59: note: Got:
45+
tryit.py:59: note: def timetz(self) -> Time[None]
46+
tryit.py:59: note: tzinfo: expected "tzinfo", got "None"
47+
Found 10 errors in 1 file (checked 1 source file)

src/datetype/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,10 @@ def __gt__(self: Self, __other: Self) -> bool: ...
456456
def __sub__(self: Self, __other: _timedelta) -> Self: ...
457457

458458
@overload
459-
def __sub__(self: DTSelf, __other: DTSelf) -> _timedelta: ...
459+
def __sub__(self: DateTime[None], __other: DateTime[None]) -> _timedelta: ...
460+
461+
@overload
462+
def __sub__(self: DateTime[_tzinfo], __other: DateTime[_tzinfo]) -> _timedelta: ...
460463

461464
def __add__(self: Self, __other: _timedelta) -> Self: ...
462465

src/datetype/test/test_datetype.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@
44
from sys import version_info
55
from unittest import TestCase, skipIf
66

7-
from datetype import AwareDateTime, NaiveDateTime, NaiveTime, Time, aware, naive
7+
from datetype import (
8+
AwareDateTime,
9+
NaiveDateTime,
10+
NaiveTime,
11+
Time,
12+
aware,
13+
naive,
14+
DateTime,
15+
)
816

917
TEST_DATA = (Path(__file__) / "..").resolve()
1018
while not (TEST_DATA / ".git").is_dir():
@@ -87,3 +95,17 @@ def test_none_aware(self) -> None:
8795
awareified = aware(stddt)
8896
self.assertIs(awareified.tzinfo, zi)
8997
self.assertEqual(awareified.tzinfo.dst(stddt), timedelta(0))
98+
99+
@skipIf(version_info < (3, 9), "ZoneInfo")
100+
def test_differing_zone_subtract(self) -> None:
101+
from zoneinfo import ZoneInfo
102+
103+
zi = ZoneInfo("US/Pacific")
104+
stddt = datetime(2025, 2, 13, 15, 35, 13, 574354, tzinfo=zi)
105+
inutc = stddt.astimezone(timezone.utc)
106+
107+
dtzi: DateTime[ZoneInfo] = aware(stddt, ZoneInfo)
108+
dttz: DateTime[timezone] = aware(inutc, timezone)
109+
110+
self.assertEqual(dtzi - dttz, timedelta(0))
111+
self.assertEqual(dttz - dtzi, timedelta(0))

tryit.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,8 @@
5454
is_aware: DateTime[timezone] = a.astimezone(None)
5555

5656
not_aware_method_time: DateTime[timezone] = a.time() # nope; actually naive
57+
58+
x - y # error; naive & aware are incompatible
59+
y - x # also error
60+
y - is_aware # ok
61+
is_aware - y # ok

0 commit comments

Comments
 (0)