-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdatetime.py
More file actions
149 lines (113 loc) · 4.54 KB
/
Copy pathdatetime.py
File metadata and controls
149 lines (113 loc) · 4.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
@namespace("datetime")
# A small, opt-in subset of Python's datetime module: just date arithmetic
# (`PyDate`/`PyTimeDelta`), not wall-clock time. Proleptic-Gregorian day
# arithmetic (the same algorithm CPython's own pure-Python reference
# implementation uses) is pure integer math — no native call needed for any
# of this. `datetime.now()`/`today()` are NOT attempted: reading the actual
# wall clock needs the same kind of per-target native-call research
# `math.py`'s functions and `random.py`'s RNG needed, which hasn't been
# done for time — a deliberate scope cut, not an oversight.
#
# Not named `Date`/`DateTime`/`TimeDelta`: `Date`/`DateTime` collide with
# native BCL/platform types by the same mechanism `random.py`'s `Random`
# and `decimal.py`'s `Decimal` already confirmed breaks Toffee builds.
# `PyDate`/`PyTimeDelta` sidestep it preemptively. There's also no
# `@classmethod` (no decorators in this language slice), so CPython's
# `date.fromordinal(...)` becomes the module-level function
# `dateFromOrdinal(...)` instead of an alternate constructor.
#
# Every value is stored and compared through a single day-ordinal (day 1 =
# January 1, year 1, matching CPython's own `date.toordinal()` convention
# exactly, including `weekday()`'s Monday=0 result), so `__add__`/`__sub__`/
# comparisons are all just integer arithmetic on that ordinal — dunders are
# explicit-call-only, as everywhere else in this project.
def _isLeap(year: int) -> bool:
if year % 4 != 0:
return False
if year % 100 != 0:
return True
return year % 400 == 0
def _daysInMonth(year: int, month: int) -> int:
if month == 2:
if _isLeap(year):
return 29
return 28
if month == 4 or month == 6 or month == 9 or month == 11:
return 30
return 31
def _daysBeforeMonth(year: int, month: int) -> int:
total: int = 0
m: int = 1
while m < month:
total += _daysInMonth(year, m)
m += 1
return total
def _daysBeforeYear(year: int) -> int:
y: int = year - 1
return y * 365 + y / 4 - y / 100 + y / 400
def _ymdToOrdinal(year: int, month: int, day: int) -> int:
return _daysBeforeYear(year) + _daysBeforeMonth(year, month) + day
def _ordinalToYmd(ordinal: int) -> tuple[int, int, int]:
year: int = 1
remaining: int = ordinal
while True:
daysInYear: int = 365
if _isLeap(year):
daysInYear = 366
if remaining <= daysInYear:
break
remaining -= daysInYear
year += 1
month: int = 1
while True:
dim: int = _daysInMonth(year, month)
if remaining <= dim:
break
remaining -= dim
month += 1
return (year, month, remaining)
class PyTimeDelta:
days: int
def __init__(self, days: int):
self.days = days
def __add__(self, other: PyTimeDelta) -> PyTimeDelta:
return PyTimeDelta(self.days + other.days)
def __sub__(self, other: PyTimeDelta) -> PyTimeDelta:
return PyTimeDelta(self.days - other.days)
def __neg__(self) -> PyTimeDelta:
return PyTimeDelta(-self.days)
def __eq__(self, other: PyTimeDelta) -> bool:
return self.days == other.days
def __lt__(self, other: PyTimeDelta) -> bool:
return self.days < other.days
def __le__(self, other: PyTimeDelta) -> bool:
return self.days <= other.days
class PyDate:
year: int
month: int
day: int
def __init__(self, year: int, month: int, day: int):
self.year = year
self.month = month
self.day = day
def toordinal(self) -> int:
return _ymdToOrdinal(self.year, self.month, self.day)
def weekday(self) -> int:
return (self.toordinal() - 1) % 7
def isoweekday(self) -> int:
return self.weekday() + 1
def __add__(self, delta: PyTimeDelta) -> PyDate:
return dateFromOrdinal(self.toordinal() + delta.days)
def __sub__(self, delta: PyTimeDelta) -> PyDate:
return dateFromOrdinal(self.toordinal() - delta.days)
def __sub__(self, other: PyDate) -> PyTimeDelta:
return PyTimeDelta(self.toordinal() - other.toordinal())
def __eq__(self, other: PyDate) -> bool:
return self.toordinal() == other.toordinal()
def __lt__(self, other: PyDate) -> bool:
return self.toordinal() < other.toordinal()
def __le__(self, other: PyDate) -> bool:
return self.toordinal() <= other.toordinal()
def dateFromOrdinal(ordinal: int) -> PyDate:
parts: tuple[int, int, int] = _ordinalToYmd(ordinal)
return PyDate(parts[0], parts[1], parts[2])