crosshair-tool 0.0.99__cp312-cp312-macosx_10_13_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. _crosshair_tracers.cpython-312-darwin.so +0 -0
  2. crosshair/__init__.py +42 -0
  3. crosshair/__main__.py +8 -0
  4. crosshair/_mark_stacks.h +790 -0
  5. crosshair/_preliminaries_test.py +18 -0
  6. crosshair/_tracers.h +94 -0
  7. crosshair/_tracers_pycompat.h +522 -0
  8. crosshair/_tracers_test.py +138 -0
  9. crosshair/abcstring.py +245 -0
  10. crosshair/auditwall.py +190 -0
  11. crosshair/auditwall_test.py +77 -0
  12. crosshair/codeconfig.py +113 -0
  13. crosshair/codeconfig_test.py +117 -0
  14. crosshair/condition_parser.py +1237 -0
  15. crosshair/condition_parser_test.py +497 -0
  16. crosshair/conftest.py +30 -0
  17. crosshair/copyext.py +155 -0
  18. crosshair/copyext_test.py +84 -0
  19. crosshair/core.py +1763 -0
  20. crosshair/core_and_libs.py +149 -0
  21. crosshair/core_regestered_types_test.py +82 -0
  22. crosshair/core_test.py +1316 -0
  23. crosshair/diff_behavior.py +314 -0
  24. crosshair/diff_behavior_test.py +261 -0
  25. crosshair/dynamic_typing.py +346 -0
  26. crosshair/dynamic_typing_test.py +210 -0
  27. crosshair/enforce.py +282 -0
  28. crosshair/enforce_test.py +182 -0
  29. crosshair/examples/PEP316/__init__.py +1 -0
  30. crosshair/examples/PEP316/bugs_detected/__init__.py +0 -0
  31. crosshair/examples/PEP316/bugs_detected/getattr_magic.py +16 -0
  32. crosshair/examples/PEP316/bugs_detected/hash_consistent_with_equals.py +31 -0
  33. crosshair/examples/PEP316/bugs_detected/shopping_cart.py +24 -0
  34. crosshair/examples/PEP316/bugs_detected/showcase.py +39 -0
  35. crosshair/examples/PEP316/correct_code/__init__.py +0 -0
  36. crosshair/examples/PEP316/correct_code/arith.py +60 -0
  37. crosshair/examples/PEP316/correct_code/chess.py +77 -0
  38. crosshair/examples/PEP316/correct_code/nesting_inference.py +17 -0
  39. crosshair/examples/PEP316/correct_code/numpy_examples.py +132 -0
  40. crosshair/examples/PEP316/correct_code/rolling_average.py +35 -0
  41. crosshair/examples/PEP316/correct_code/showcase.py +104 -0
  42. crosshair/examples/__init__.py +0 -0
  43. crosshair/examples/check_examples_test.py +146 -0
  44. crosshair/examples/deal/__init__.py +1 -0
  45. crosshair/examples/icontract/__init__.py +1 -0
  46. crosshair/examples/icontract/bugs_detected/__init__.py +0 -0
  47. crosshair/examples/icontract/bugs_detected/showcase.py +41 -0
  48. crosshair/examples/icontract/bugs_detected/wrong_sign.py +8 -0
  49. crosshair/examples/icontract/correct_code/__init__.py +0 -0
  50. crosshair/examples/icontract/correct_code/arith.py +51 -0
  51. crosshair/examples/icontract/correct_code/showcase.py +94 -0
  52. crosshair/fnutil.py +391 -0
  53. crosshair/fnutil_test.py +75 -0
  54. crosshair/fuzz_core_test.py +516 -0
  55. crosshair/libimpl/__init__.py +0 -0
  56. crosshair/libimpl/arraylib.py +161 -0
  57. crosshair/libimpl/binascii_ch_test.py +30 -0
  58. crosshair/libimpl/binascii_test.py +67 -0
  59. crosshair/libimpl/binasciilib.py +150 -0
  60. crosshair/libimpl/bisectlib_test.py +23 -0
  61. crosshair/libimpl/builtinslib.py +5228 -0
  62. crosshair/libimpl/builtinslib_ch_test.py +1191 -0
  63. crosshair/libimpl/builtinslib_test.py +3735 -0
  64. crosshair/libimpl/codecslib.py +86 -0
  65. crosshair/libimpl/codecslib_test.py +86 -0
  66. crosshair/libimpl/collectionslib.py +264 -0
  67. crosshair/libimpl/collectionslib_ch_test.py +252 -0
  68. crosshair/libimpl/collectionslib_test.py +332 -0
  69. crosshair/libimpl/copylib.py +23 -0
  70. crosshair/libimpl/copylib_test.py +18 -0
  71. crosshair/libimpl/datetimelib.py +2559 -0
  72. crosshair/libimpl/datetimelib_ch_test.py +354 -0
  73. crosshair/libimpl/datetimelib_test.py +112 -0
  74. crosshair/libimpl/decimallib.py +5257 -0
  75. crosshair/libimpl/decimallib_ch_test.py +78 -0
  76. crosshair/libimpl/decimallib_test.py +76 -0
  77. crosshair/libimpl/encodings/__init__.py +23 -0
  78. crosshair/libimpl/encodings/_encutil.py +187 -0
  79. crosshair/libimpl/encodings/ascii.py +44 -0
  80. crosshair/libimpl/encodings/latin_1.py +40 -0
  81. crosshair/libimpl/encodings/utf_8.py +93 -0
  82. crosshair/libimpl/encodings_ch_test.py +83 -0
  83. crosshair/libimpl/fractionlib.py +16 -0
  84. crosshair/libimpl/fractionlib_test.py +80 -0
  85. crosshair/libimpl/functoolslib.py +34 -0
  86. crosshair/libimpl/functoolslib_test.py +56 -0
  87. crosshair/libimpl/hashliblib.py +30 -0
  88. crosshair/libimpl/hashliblib_test.py +18 -0
  89. crosshair/libimpl/heapqlib.py +47 -0
  90. crosshair/libimpl/heapqlib_test.py +21 -0
  91. crosshair/libimpl/importliblib.py +18 -0
  92. crosshair/libimpl/importliblib_test.py +38 -0
  93. crosshair/libimpl/iolib.py +216 -0
  94. crosshair/libimpl/iolib_ch_test.py +128 -0
  95. crosshair/libimpl/iolib_test.py +19 -0
  96. crosshair/libimpl/ipaddresslib.py +8 -0
  97. crosshair/libimpl/itertoolslib.py +44 -0
  98. crosshair/libimpl/itertoolslib_test.py +44 -0
  99. crosshair/libimpl/jsonlib.py +984 -0
  100. crosshair/libimpl/jsonlib_ch_test.py +42 -0
  101. crosshair/libimpl/jsonlib_test.py +51 -0
  102. crosshair/libimpl/mathlib.py +179 -0
  103. crosshair/libimpl/mathlib_ch_test.py +44 -0
  104. crosshair/libimpl/mathlib_test.py +67 -0
  105. crosshair/libimpl/oslib.py +7 -0
  106. crosshair/libimpl/pathliblib_test.py +10 -0
  107. crosshair/libimpl/randomlib.py +178 -0
  108. crosshair/libimpl/randomlib_test.py +120 -0
  109. crosshair/libimpl/relib.py +846 -0
  110. crosshair/libimpl/relib_ch_test.py +169 -0
  111. crosshair/libimpl/relib_test.py +493 -0
  112. crosshair/libimpl/timelib.py +72 -0
  113. crosshair/libimpl/timelib_test.py +82 -0
  114. crosshair/libimpl/typeslib.py +15 -0
  115. crosshair/libimpl/typeslib_test.py +36 -0
  116. crosshair/libimpl/unicodedatalib.py +75 -0
  117. crosshair/libimpl/unicodedatalib_test.py +42 -0
  118. crosshair/libimpl/urlliblib.py +23 -0
  119. crosshair/libimpl/urlliblib_test.py +19 -0
  120. crosshair/libimpl/weakreflib.py +13 -0
  121. crosshair/libimpl/weakreflib_test.py +69 -0
  122. crosshair/libimpl/zliblib.py +15 -0
  123. crosshair/libimpl/zliblib_test.py +13 -0
  124. crosshair/lsp_server.py +261 -0
  125. crosshair/lsp_server_test.py +30 -0
  126. crosshair/main.py +973 -0
  127. crosshair/main_test.py +543 -0
  128. crosshair/objectproxy.py +376 -0
  129. crosshair/objectproxy_test.py +41 -0
  130. crosshair/opcode_intercept.py +601 -0
  131. crosshair/opcode_intercept_test.py +304 -0
  132. crosshair/options.py +218 -0
  133. crosshair/options_test.py +10 -0
  134. crosshair/patch_equivalence_test.py +75 -0
  135. crosshair/path_cover.py +209 -0
  136. crosshair/path_cover_test.py +138 -0
  137. crosshair/path_search.py +161 -0
  138. crosshair/path_search_test.py +52 -0
  139. crosshair/pathing_oracle.py +271 -0
  140. crosshair/pathing_oracle_test.py +21 -0
  141. crosshair/pure_importer.py +27 -0
  142. crosshair/pure_importer_test.py +16 -0
  143. crosshair/py.typed +0 -0
  144. crosshair/register_contract.py +273 -0
  145. crosshair/register_contract_test.py +190 -0
  146. crosshair/simplestructs.py +1165 -0
  147. crosshair/simplestructs_test.py +283 -0
  148. crosshair/smtlib.py +24 -0
  149. crosshair/smtlib_test.py +14 -0
  150. crosshair/statespace.py +1199 -0
  151. crosshair/statespace_test.py +108 -0
  152. crosshair/stubs_parser.py +352 -0
  153. crosshair/stubs_parser_test.py +43 -0
  154. crosshair/test_util.py +329 -0
  155. crosshair/test_util_test.py +26 -0
  156. crosshair/tools/__init__.py +0 -0
  157. crosshair/tools/check_help_in_doc.py +264 -0
  158. crosshair/tools/check_init_and_setup_coincide.py +119 -0
  159. crosshair/tools/generate_demo_table.py +127 -0
  160. crosshair/tracers.py +544 -0
  161. crosshair/tracers_test.py +154 -0
  162. crosshair/type_repo.py +151 -0
  163. crosshair/unicode_categories.py +589 -0
  164. crosshair/unicode_categories_test.py +27 -0
  165. crosshair/util.py +741 -0
  166. crosshair/util_test.py +173 -0
  167. crosshair/watcher.py +307 -0
  168. crosshair/watcher_test.py +107 -0
  169. crosshair/z3util.py +76 -0
  170. crosshair/z3util_test.py +11 -0
  171. crosshair_tool-0.0.99.dist-info/METADATA +144 -0
  172. crosshair_tool-0.0.99.dist-info/RECORD +176 -0
  173. crosshair_tool-0.0.99.dist-info/WHEEL +6 -0
  174. crosshair_tool-0.0.99.dist-info/entry_points.txt +3 -0
  175. crosshair_tool-0.0.99.dist-info/licenses/LICENSE +93 -0
  176. crosshair_tool-0.0.99.dist-info/top_level.txt +2 -0
@@ -0,0 +1,2559 @@
1
+ #
2
+ # This file includes a modified version of CPython's pure python datetime
3
+ # implementation from:
4
+ # https://github.com/python/cpython/blob/v3.10.2/Lib/datetime.py
5
+ #
6
+ # The shared source code is licensed under the PSF license and is
7
+ # copyright © 2001-2022 Python Software Foundation; All Rights Reserved
8
+ #
9
+ # See the "LICENSE" file for complete license details on CrossHair.
10
+ #
11
+
12
+ # NOTE: At least some of this code could be rewritten to be more
13
+ # symbolic-friendly. Since much is fork-lifted from CPython, do not
14
+ # assume the coding decisions made here are very intentional or
15
+ # optimal.
16
+
17
+
18
+ import math as _math
19
+ import sys
20
+ import time as _time
21
+ from datetime import date as real_date
22
+ from datetime import datetime as real_datetime
23
+ from datetime import time as real_time
24
+ from datetime import timedelta as real_timedelta
25
+ from datetime import timezone as real_timezone
26
+ from datetime import tzinfo as real_tzinfo
27
+ from enum import Enum
28
+ from typing import Any, Optional, Tuple, Union
29
+
30
+ from crosshair import (
31
+ IgnoreAttempt,
32
+ ResumedTracing,
33
+ realize,
34
+ register_patch,
35
+ register_type,
36
+ )
37
+ from crosshair.core import SymbolicFactory
38
+ from crosshair.libimpl.builtinslib import make_bounded_int, smt_or
39
+ from crosshair.statespace import context_statespace
40
+ from crosshair.tracers import NoTracing
41
+
42
+
43
+ def _cmp(x, y):
44
+ return 0 if x == y else 1 if x > y else -1
45
+
46
+
47
+ MINYEAR = 1
48
+ MAXYEAR = 9999
49
+ _MAXORDINAL = 3652059 # date.max.toordinal()
50
+
51
+
52
+ # -1 is a placeholder for indexing purposes.
53
+ _DAYS_IN_MONTH = [-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
54
+
55
+ _DAYS_BEFORE_MONTH = [-1] # -1 is a placeholder for indexing purposes.
56
+ dbm = 0
57
+ for dim in _DAYS_IN_MONTH[1:]:
58
+ _DAYS_BEFORE_MONTH.append(dbm)
59
+ dbm += dim
60
+ del dbm, dim
61
+
62
+
63
+ def _is_leap(year):
64
+ """year -> 1 if leap year, else 0."""
65
+ return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
66
+
67
+
68
+ import z3 # type: ignore
69
+
70
+
71
+ def _smt_is_leap(smt_year):
72
+ """year -> 1 if leap year, else 0."""
73
+ return context_statespace().smt_fork(
74
+ z3.And(smt_year % 4 == 0, z3.Or(smt_year % 100 != 0, smt_year % 400 == 0)),
75
+ "is leap year",
76
+ probability_true=0.25,
77
+ )
78
+
79
+
80
+ def _days_before_year(year):
81
+ """year -> number of days before January 1st of year."""
82
+ y = year - 1
83
+ return y * 365 + y // 4 - y // 100 + y // 400
84
+
85
+
86
+ def _days_in_month(year, month):
87
+ """year, month -> number of days in that month in that year."""
88
+ # Avoid _DAYS_IN_MONTH so that we don't realize the month
89
+ assert 1 <= month <= 12, month
90
+ if month >= 8:
91
+ return 31 if month % 2 == 0 else 30
92
+ else:
93
+ if month == 2:
94
+ return 29 if _is_leap(year) else 28
95
+ else:
96
+ return 30 if month % 2 == 0 else 31
97
+
98
+
99
+ def _smt_days_in_month(smt_year, smt_month, smt_day):
100
+ """constraint smt_day to match the year and month."""
101
+ feb_days = 29 if _smt_is_leap(smt_year) else 28
102
+ return z3.Or(
103
+ z3.And(
104
+ smt_day <= feb_days,
105
+ smt_month == 2,
106
+ ),
107
+ z3.And(
108
+ smt_day <= 30,
109
+ z3.Or(
110
+ smt_month == 4,
111
+ smt_month == 6,
112
+ smt_month == 9,
113
+ smt_month == 11,
114
+ ),
115
+ ),
116
+ z3.And(
117
+ smt_day <= 31,
118
+ z3.Or(
119
+ smt_month == 1,
120
+ smt_month == 3,
121
+ smt_month == 5,
122
+ smt_month == 7,
123
+ smt_month == 8,
124
+ smt_month == 10,
125
+ smt_month == 12,
126
+ ),
127
+ ),
128
+ )
129
+
130
+
131
+ def _days_before_month(year, month):
132
+ """year, month -> number of days in year preceding first day of month."""
133
+ assert 1 <= month <= 12, "month must be in 1..12"
134
+ return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))
135
+
136
+
137
+ def _ymd2ord(year, month, day):
138
+ """year, month, day -> ordinal, considering 01-Jan-0001 as day 1."""
139
+ assert 1 <= month <= 12, "month must be in 1..12"
140
+ dim = _days_in_month(year, month)
141
+ assert 1 <= day <= dim, "day must be in 1..%d" % dim
142
+ return _days_before_year(year) + _days_before_month(year, month) + day
143
+
144
+
145
+ _DI400Y = _days_before_year(401) # number of days in 400 years
146
+ _DI100Y = _days_before_year(101) # " " " " 100 "
147
+ _DI4Y = _days_before_year(5) # " " " " 4 "
148
+
149
+ # A 4-year cycle has an extra leap day over what we'd get from pasting
150
+ # together 4 single years.
151
+ assert _DI4Y == 4 * 365 + 1
152
+
153
+ # Similarly, a 400-year cycle has an extra leap day over what we'd get from
154
+ # pasting together 4 100-year cycles.
155
+ assert _DI400Y == 4 * _DI100Y + 1
156
+
157
+ # OTOH, a 100-year cycle has one fewer leap day than we'd get from
158
+ # pasting together 25 4-year cycles.
159
+ assert _DI100Y == 25 * _DI4Y - 1
160
+
161
+
162
+ def _ord2ymd(n):
163
+ """ordinal -> (year, month, day), considering 01-Jan-0001 as day 1."""
164
+ n -= 1
165
+ n400, n = divmod(n, _DI400Y)
166
+ year = n400 * 400 + 1 # ..., -399, 1, 401, ...
167
+ n100, n = divmod(n, _DI100Y)
168
+ n4, n = divmod(n, _DI4Y)
169
+ n1, n = divmod(n, 365)
170
+ year += n100 * 100 + n4 * 4 + n1
171
+ if n1 == 4 or n100 == 4:
172
+ assert n == 0
173
+ return year - 1, 12, 31
174
+ leapyear = n1 == 3 and (n4 != 24 or n100 == 3)
175
+ assert leapyear == _is_leap(year)
176
+ month = (n + 50) >> 5
177
+ preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)
178
+ if preceding > n: # estimate is too large
179
+ month -= 1
180
+ preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)
181
+ n -= preceding
182
+ assert 0 <= n < _days_in_month(year, month)
183
+ return year, month, n + 1
184
+
185
+
186
+ # Month and day names. For localized versions, see the calendar module.
187
+ _MONTHNAMES = [
188
+ None,
189
+ "Jan",
190
+ "Feb",
191
+ "Mar",
192
+ "Apr",
193
+ "May",
194
+ "Jun",
195
+ "Jul",
196
+ "Aug",
197
+ "Sep",
198
+ "Oct",
199
+ "Nov",
200
+ "Dec",
201
+ ]
202
+ _DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
203
+
204
+
205
+ def _build_struct_time(y, m, d, hh, mm, ss, dstflag):
206
+ wday = (_ymd2ord(y, m, d) + 6) % 7
207
+ dnum = _days_before_month(y, m) + d
208
+ return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))
209
+
210
+
211
+ def _format_time(hh, mm, ss, us, timespec="auto"):
212
+ specs = {
213
+ "hours": "{:02d}",
214
+ "minutes": "{:02d}:{:02d}",
215
+ "seconds": "{:02d}:{:02d}:{:02d}",
216
+ "milliseconds": "{:02d}:{:02d}:{:02d}.{:03d}",
217
+ "microseconds": "{:02d}:{:02d}:{:02d}.{:06d}",
218
+ }
219
+
220
+ if timespec == "auto":
221
+ # Skip trailing microseconds when us==0.
222
+ timespec = "microseconds" if us else "seconds"
223
+ elif timespec == "milliseconds":
224
+ us //= 1000
225
+ try:
226
+ fmt = specs[timespec]
227
+ except KeyError:
228
+ raise ValueError("Unknown timespec value")
229
+ else:
230
+ return fmt.format(hh, mm, ss, us)
231
+
232
+
233
+ def _format_offset(off):
234
+ s = ""
235
+ if off is not None:
236
+ if off.days < 0:
237
+ sign = "-"
238
+ off = -off
239
+ else:
240
+ sign = "+"
241
+ hh, mm = divmod(off, timedelta(hours=1))
242
+ mm, ss = divmod(mm, timedelta(minutes=1))
243
+ s += "%s%02d:%02d" % (sign, hh, mm)
244
+ if ss or ss.microseconds:
245
+ s += ":%02d" % ss.seconds
246
+
247
+ if ss.microseconds:
248
+ s += ".%06d" % ss.microseconds
249
+ return s
250
+
251
+
252
+ # Correctly substitute for %z and %Z escapes in strftime formats.
253
+ def _wrap_strftime(object, format, timetuple):
254
+ format = realize(format)
255
+ # Don't call utcoffset() or tzname() unless actually needed.
256
+ freplace = None # the string to use for %f
257
+ zreplace = None # the string to use for %z
258
+ Zreplace = None # the string to use for %Z
259
+
260
+ # Scan format for %z and %Z escapes, replacing as needed.
261
+ newformat = []
262
+ push = newformat.append
263
+ i, n = 0, len(format)
264
+ while i < n:
265
+ ch = format[i]
266
+ i += 1
267
+ if ch == "%":
268
+ if i < n:
269
+ ch = format[i]
270
+ i += 1
271
+ if ch == "f":
272
+ if freplace is None:
273
+ freplace = "%06d" % getattr(object, "microsecond", 0)
274
+ newformat.append(freplace)
275
+ elif ch == "z":
276
+ if zreplace is None:
277
+ zreplace = ""
278
+ if hasattr(object, "utcoffset"):
279
+ offset = object.utcoffset()
280
+ if offset is not None:
281
+ sign = "+"
282
+ if offset.days < 0:
283
+ offset = -offset
284
+ sign = "-"
285
+ h, rest = divmod(offset, timedelta(hours=1))
286
+ m, rest = divmod(rest, timedelta(minutes=1))
287
+ s = rest.seconds
288
+ u = offset.microseconds
289
+ if u:
290
+ zreplace = "%c%02d%02d%02d.%06d" % (
291
+ sign,
292
+ h,
293
+ m,
294
+ s,
295
+ u,
296
+ )
297
+ elif s:
298
+ zreplace = "%c%02d%02d%02d" % (sign, h, m, s)
299
+ else:
300
+ zreplace = "%c%02d%02d" % (sign, h, m)
301
+ assert "%" not in zreplace
302
+ newformat.append(zreplace)
303
+ elif ch == "Z":
304
+ if Zreplace is None:
305
+ Zreplace = ""
306
+ if hasattr(object, "tzname"):
307
+ s = object.tzname()
308
+ if s is not None:
309
+ # strftime is going to have at this: escape %
310
+ Zreplace = s.replace("%", "%%")
311
+ newformat.append(Zreplace)
312
+ else:
313
+ push("%")
314
+ push(ch)
315
+ else:
316
+ push("%")
317
+ else:
318
+ push(ch)
319
+ newformat = "".join(newformat)
320
+ return _time.strftime(newformat, timetuple)
321
+
322
+
323
+ # Helpers for parsing the result of isoformat()
324
+ def _parse_isoformat_date(dtstr):
325
+ # It is assumed that this function will only be called with a
326
+ # string of length exactly 10, and (though this is not used) ASCII-only
327
+ year = int(dtstr[0:4])
328
+ if dtstr[4] != "-":
329
+ raise ValueError("Invalid date separator")
330
+
331
+ month = int(dtstr[5:7])
332
+
333
+ if dtstr[7] != "-":
334
+ raise ValueError("Invalid date separator")
335
+
336
+ day = int(dtstr[8:10])
337
+
338
+ return [year, month, day]
339
+
340
+
341
+ def _parse_hh_mm_ss_ff(tstr):
342
+ # Parses things of the form HH[:MM[:SS[.fff[fff]]]]
343
+ len_str = len(tstr)
344
+
345
+ time_comps = [0, 0, 0, 0]
346
+ pos = 0
347
+ for comp in range(0, 3):
348
+ if (len_str - pos) < 2:
349
+ raise ValueError("Incomplete time component")
350
+
351
+ time_comps[comp] = int(tstr[pos : pos + 2])
352
+
353
+ pos += 2
354
+ next_char = tstr[pos : pos + 1]
355
+
356
+ if not next_char or comp >= 2:
357
+ break
358
+
359
+ if next_char != ":":
360
+ raise ValueError("Invalid time separator")
361
+
362
+ pos += 1
363
+
364
+ if pos < len_str:
365
+ if tstr[pos] != ".":
366
+ raise ValueError("Invalid microsecond component")
367
+ else:
368
+ pos += 1
369
+
370
+ len_remainder = len_str - pos
371
+ if len_remainder not in (3, 6):
372
+ raise ValueError("Invalid microsecond component")
373
+
374
+ time_comps[3] = int(tstr[pos:])
375
+ if len_remainder == 3:
376
+ time_comps[3] *= 1000
377
+
378
+ return time_comps
379
+
380
+
381
+ def _parse_isoformat_time(tstr):
382
+ # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]]
383
+ len_str = len(tstr)
384
+ if len_str < 2:
385
+ raise ValueError("Isoformat time too short")
386
+
387
+ # This is equivalent to re.search('[+-]', tstr), but faster
388
+ tz_pos = tstr.find("-") + 1 or tstr.find("+") + 1
389
+ timestr = tstr[: tz_pos - 1] if tz_pos > 0 else tstr
390
+
391
+ time_comps = _parse_hh_mm_ss_ff(timestr)
392
+
393
+ tzi = None
394
+ if tz_pos > 0:
395
+ tzstr = tstr[tz_pos:]
396
+
397
+ # Valid time zone strings are:
398
+ # HH:MM len: 5
399
+ # HH:MM:SS len: 8
400
+ # HH:MM:SS.ffffff len: 15
401
+
402
+ if len(tzstr) not in (5, 8, 15):
403
+ raise ValueError("Malformed time zone string")
404
+
405
+ tz_comps = _parse_hh_mm_ss_ff(tzstr)
406
+ if all(x == 0 for x in tz_comps):
407
+ tzi = timezone.utc
408
+ else:
409
+ tzsign = -1 if tstr[tz_pos - 1] == "-" else 1
410
+
411
+ td = timedelta(
412
+ hours=tz_comps[0],
413
+ minutes=tz_comps[1],
414
+ seconds=tz_comps[2],
415
+ microseconds=tz_comps[3],
416
+ )
417
+
418
+ tzi = timezone(tzsign * td)
419
+
420
+ time_comps.append(tzi)
421
+
422
+ return time_comps
423
+
424
+
425
+ # Just raise TypeError if the arg isn't None or a string.
426
+ def _check_tzname(name):
427
+ if name is not None and not isinstance(name, str):
428
+ raise TypeError(
429
+ "tzinfo.tzname() must return None or string, " "not '%s'" % type(name)
430
+ )
431
+
432
+
433
+ # name is the offset-producing method, "utcoffset" or "dst".
434
+ # offset is what it returned.
435
+ # If offset isn't None or timedelta, raises TypeError.
436
+ # If offset is None, returns None.
437
+ # Else offset is checked for being in range.
438
+ # If it is, its integer value is returned. Else ValueError is raised.
439
+ def _check_utc_offset(name, offset):
440
+ assert name in ("utcoffset", "dst")
441
+ if offset is None:
442
+ return
443
+ if not isinstance(offset, any_timedelta):
444
+ raise TypeError(
445
+ "tzinfo.%s() must return None "
446
+ "or timedelta, not '%s'" % (name, type(offset))
447
+ )
448
+ if not -timedelta(1) < offset < timedelta(1):
449
+ raise ValueError(
450
+ "%s() must be strictly between "
451
+ "-timedelta(hours=24) and timedelta(hours=24)" % (name)
452
+ )
453
+
454
+
455
+ def _check_ints(values):
456
+ for value in values:
457
+ if not isinstance(value, int):
458
+ raise TypeError
459
+
460
+
461
+ def _check_date_fields(year, month, day):
462
+ _check_ints((year, month, day))
463
+ if not MINYEAR <= year <= MAXYEAR:
464
+ raise ValueError("year must be in %d..%d" % (MINYEAR, MAXYEAR))
465
+ if not 1 <= month <= 12:
466
+ raise ValueError("month must be in 1..12")
467
+ dim = _days_in_month(year, month)
468
+ if not 1 <= day <= dim:
469
+ raise ValueError("day must be in 1..%d" % dim)
470
+ return year, month, day
471
+
472
+
473
+ def _check_time_fields(hour, minute, second, microsecond, fold):
474
+ _check_ints((hour, minute, second, microsecond, fold))
475
+ if not 0 <= hour <= 23:
476
+ raise ValueError("hour must be in 0..23")
477
+ if not 0 <= minute <= 59:
478
+ raise ValueError("minute must be in 0..59")
479
+ if not 0 <= second <= 59:
480
+ raise ValueError("second must be in 0..59")
481
+ if not 0 <= microsecond <= 999999:
482
+ raise ValueError("microsecond must be in 0..999999")
483
+ if fold not in (0, 1):
484
+ raise ValueError("fold must be either 0 or 1")
485
+ return hour, minute, second, microsecond, fold
486
+
487
+
488
+ def _check_tzinfo_arg(tz):
489
+ if tz is not None and not isinstance(tz, any_tzinfo):
490
+ raise TypeError("tzinfo argument must be None or of a tzinfo subclass")
491
+
492
+
493
+ def _cmperror(x, y):
494
+ raise TypeError("can't compare '%s' to '%s'" % (type(x).__name__, type(y).__name__))
495
+
496
+
497
+ def _divide_and_round(a, b):
498
+ """
499
+ divide a by b and round result to the nearest integer
500
+
501
+ When the ratio is exactly half-way between two integers,
502
+ the even integer is returned.
503
+ """
504
+ # Based on the reference implementation for divmod_near
505
+ # in Objects/longobject.c.
506
+ q, r = divmod(a, b)
507
+ # round up if either r / b > 0.5, or r / b == 0.5 and q is odd.
508
+ # The expression r / b > 0.5 is equivalent to 2 * r > b if b is
509
+ # positive, 2 * r < b if b negative.
510
+ r *= 2
511
+ greater_than_half = r > b if b > 0 else r < b
512
+ if greater_than_half or r == b and q % 2 == 1:
513
+ q += 1
514
+
515
+ return q
516
+
517
+
518
+ def _timedelta_to_microseconds(td):
519
+ return (td.days * (24 * 3600) + td.seconds) * 1000000 + td.microseconds
520
+
521
+
522
+ def _timedelta_getstate(self):
523
+ return (self.days, self.seconds, self.microseconds)
524
+
525
+
526
+ class timedelta:
527
+ """
528
+ Represent the difference between two datetime objects.
529
+
530
+ Supported operators:
531
+
532
+ - add, subtract timedelta
533
+ - unary plus, minus, abs
534
+ - compare to timedelta
535
+ - multiply, divide by int
536
+
537
+ In addition, datetime supports subtraction of two datetime objects
538
+ returning a timedelta, and addition or subtraction of a datetime
539
+ and a timedelta giving a datetime.
540
+
541
+ Representation: (days, seconds, microseconds). Why? Because I
542
+ felt like it.
543
+ """
544
+
545
+ def __new__(
546
+ cls,
547
+ days=0,
548
+ seconds=0,
549
+ microseconds=0,
550
+ milliseconds=0,
551
+ minutes=0,
552
+ hours=0,
553
+ weeks=0,
554
+ ):
555
+ # Doing this efficiently and accurately in C is going to be difficult
556
+ # and error-prone, due to ubiquitous overflow possibilities, and that
557
+ # C double doesn't have enough bits of precision to represent
558
+ # microseconds over 10K years faithfully. The code here tries to make
559
+ # explicit where go-fast assumptions can be relied on, in order to
560
+ # guide the C implementation; it's way more convoluted than speed-
561
+ # ignoring auto-overflow-to-long idiomatic Python could be.
562
+
563
+ # XXX Check that all inputs are ints or floats.
564
+
565
+ # Normalize everything to days, seconds, microseconds.
566
+ days += weeks * 7
567
+ seconds += minutes * 60 + hours * 3600
568
+ microseconds += milliseconds * 1000
569
+
570
+ # roll fractional values down to lower tiers
571
+ if isinstance(days, float):
572
+ days, fractional_days = divmod(days, 1)
573
+ seconds += fractional_days * (24 * 3600)
574
+ if isinstance(seconds, float):
575
+ seconds, fractional_seconds = divmod(seconds, 1)
576
+ microseconds += fractional_seconds * (1_000_000)
577
+ if isinstance(microseconds, float):
578
+ microseconds = round(microseconds)
579
+
580
+ # now everything is an integer; roll overflow back up into higher tiers:
581
+ if not (0 <= microseconds < 1_000_000):
582
+ addl_seconds, microseconds = divmod(microseconds, 1_000_000)
583
+ seconds += addl_seconds
584
+ if not (0 <= seconds < 24 * 3600):
585
+ addl_days, seconds = divmod(seconds, 24 * 3600)
586
+ days += addl_days
587
+
588
+ if abs(days) > 999999999:
589
+ raise OverflowError
590
+
591
+ self = object.__new__(cls)
592
+ self._days = days
593
+ self._seconds = seconds
594
+ self._microseconds = microseconds
595
+ self._hashcode = -1
596
+ return self
597
+
598
+ def __repr__(self):
599
+ args = []
600
+ if self._days:
601
+ args.append("days=%d" % self._days)
602
+ if self._seconds:
603
+ args.append("seconds=%d" % self._seconds)
604
+ if self._microseconds:
605
+ args.append("microseconds=%d" % self._microseconds)
606
+ if not args:
607
+ args.append("0")
608
+ return "%s.%s(%s)" % (
609
+ type(self).__module__,
610
+ self.__class__.__qualname__,
611
+ ", ".join(args),
612
+ )
613
+
614
+ def __str__(self):
615
+ mm, ss = divmod(self._seconds, 60)
616
+ hh, mm = divmod(mm, 60)
617
+ s = "%d:%02d:%02d" % (hh, mm, ss)
618
+ if self._days:
619
+
620
+ def plural(n):
621
+ return n, abs(n) != 1 and "s" or ""
622
+
623
+ s = ("%d day%s, " % plural(self._days)) + s
624
+ if self._microseconds:
625
+ s = s + ".%06d" % self._microseconds
626
+ return s
627
+
628
+ def total_seconds(self):
629
+ """Total seconds in the duration."""
630
+ return ((self.days * 86400 + self.seconds) * 10**6 + self.microseconds) / 10**6
631
+
632
+ # Read-only field accessors
633
+ @property
634
+ def days(self):
635
+ """days"""
636
+ return self._days
637
+
638
+ @property
639
+ def seconds(self):
640
+ """seconds"""
641
+ return self._seconds
642
+
643
+ @property
644
+ def microseconds(self):
645
+ """microseconds"""
646
+ return self._microseconds
647
+
648
+ def __add__(self, other):
649
+ if isinstance(other, any_timedelta):
650
+ # for CPython compatibility, we cannot use
651
+ # our __class__ here, but need a real timedelta
652
+ return timedelta(
653
+ self._days + other.days,
654
+ self._seconds + other.seconds,
655
+ self._microseconds + other.microseconds,
656
+ )
657
+ elif isinstance(other, any_datetime):
658
+ return datetime.fromdatetime(other).__add__(self)
659
+ elif isinstance(other, any_date):
660
+ return date.fromdate(other).__add__(self)
661
+ return NotImplemented
662
+
663
+ __radd__ = __add__
664
+
665
+ def __sub__(self, other):
666
+ if isinstance(other, any_timedelta):
667
+ # for CPython compatibility, we cannot use
668
+ # our __class__ here, but need a real timedelta
669
+ return timedelta(
670
+ self._days - other.days,
671
+ self._seconds - other.seconds,
672
+ self._microseconds - other.microseconds,
673
+ )
674
+ elif isinstance(other, any_date):
675
+ return date.fromdate(other).__add__(self)
676
+ elif isinstance(other, any_datetime):
677
+ return datetime.fromdatetime(other).__add__(self)
678
+ return NotImplemented
679
+
680
+ def __rsub__(self, other):
681
+ if isinstance(other, any_timedelta):
682
+ return -self + other
683
+ return NotImplemented
684
+
685
+ def __neg__(self):
686
+ # for CPython compatibility, we cannot use
687
+ # our __class__ here, but need a real timedelta
688
+ return timedelta(-self._days, -self._seconds, -self._microseconds)
689
+
690
+ def __pos__(self):
691
+ return self
692
+
693
+ def __abs__(self):
694
+ if self._days < 0:
695
+ return -self
696
+ else:
697
+ return self
698
+
699
+ def __mul__(self, other):
700
+ if isinstance(other, int):
701
+ # for CPython compatibility, we cannot use
702
+ # our __class__ here, but need a real timedelta
703
+ return timedelta(
704
+ self._days * other, self._seconds * other, self._microseconds * other
705
+ )
706
+ if isinstance(other, float):
707
+ usec = _timedelta_to_microseconds(self)
708
+ a, b = other.as_integer_ratio()
709
+ return timedelta(0, 0, _divide_and_round(usec * a, b))
710
+ return NotImplemented
711
+
712
+ __rmul__ = __mul__
713
+
714
+ def __floordiv__(self, other):
715
+ if not isinstance(other, (int, any_timedelta)):
716
+ return NotImplemented
717
+ usec = _timedelta_to_microseconds(self)
718
+ if isinstance(other, any_timedelta):
719
+ return usec // _timedelta_to_microseconds(other)
720
+ if isinstance(other, int):
721
+ return timedelta(0, 0, usec // other)
722
+
723
+ def __truediv__(self, other):
724
+ if not isinstance(other, (int, float, any_timedelta)):
725
+ return NotImplemented
726
+ usec = _timedelta_to_microseconds(self)
727
+ if isinstance(other, any_timedelta):
728
+ return usec / _timedelta_to_microseconds(other)
729
+ if isinstance(other, int):
730
+ return timedelta(0, 0, _divide_and_round(usec, other))
731
+ if isinstance(other, float):
732
+ a, b = other.as_integer_ratio()
733
+ return timedelta(0, 0, _divide_and_round(b * usec, a))
734
+
735
+ def __mod__(self, other):
736
+ if isinstance(other, any_timedelta):
737
+ r = _timedelta_to_microseconds(self) % _timedelta_to_microseconds(other)
738
+ return timedelta(0, 0, r)
739
+ return NotImplemented
740
+
741
+ def __divmod__(self, other):
742
+ if isinstance(other, any_timedelta):
743
+ q, r = divmod(
744
+ _timedelta_to_microseconds(self), _timedelta_to_microseconds(other)
745
+ )
746
+ return q, timedelta(0, 0, r)
747
+ return NotImplemented
748
+
749
+ # Comparisons of timedelta objects with other.
750
+
751
+ def __eq__(self, other):
752
+ if isinstance(other, any_timedelta):
753
+ return self._cmp(other) == 0
754
+ else:
755
+ return NotImplemented
756
+
757
+ def __le__(self, other):
758
+ if isinstance(other, any_timedelta):
759
+ return self._cmp(other) <= 0
760
+ else:
761
+ return NotImplemented
762
+
763
+ def __lt__(self, other):
764
+ if isinstance(other, any_timedelta):
765
+ return self._cmp(other) < 0
766
+ else:
767
+ return NotImplemented
768
+
769
+ def __ge__(self, other):
770
+ if isinstance(other, any_timedelta):
771
+ return self._cmp(other) >= 0
772
+ else:
773
+ return NotImplemented
774
+
775
+ def __gt__(self, other):
776
+ if isinstance(other, any_timedelta):
777
+ return self._cmp(other) > 0
778
+ else:
779
+ return NotImplemented
780
+
781
+ def _cmp(self, other):
782
+ assert isinstance(other, any_timedelta)
783
+ return _cmp(_timedelta_getstate(self), _timedelta_getstate(other))
784
+
785
+ def __hash__(self):
786
+ if self._hashcode == -1:
787
+ self._hashcode = hash(_timedelta_getstate(self))
788
+ return self._hashcode
789
+
790
+ def __bool__(self):
791
+ return realize(
792
+ smt_or(
793
+ self._microseconds != 0,
794
+ smt_or(
795
+ self._seconds != 0,
796
+ self._days != 0,
797
+ ),
798
+ )
799
+ )
800
+
801
+ def __ch_realize__(self):
802
+ return real_timedelta(
803
+ days=realize(self._days),
804
+ seconds=realize(self._seconds),
805
+ milliseconds=realize(self._microseconds) / 1000.0,
806
+ )
807
+
808
+ def __ch_pytype__(self):
809
+ return real_timedelta
810
+
811
+
812
+ any_timedelta = (timedelta, real_timedelta)
813
+
814
+ timedelta.min = timedelta(-999999999) # type: ignore
815
+ timedelta.max = timedelta( # type: ignore
816
+ days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999
817
+ )
818
+ timedelta.resolution = timedelta(microseconds=1) # type: ignore
819
+
820
+
821
+ def _date_getstate(self):
822
+ yhi, ylo = divmod(self.year, 256)
823
+ return bytes([yhi, ylo, self.month, self.day])
824
+
825
+
826
+ class date:
827
+ """
828
+ Concrete date type.
829
+
830
+ Constructors:
831
+
832
+ __new__()
833
+ fromtimestamp()
834
+ today()
835
+ fromordinal()
836
+
837
+ Operators
838
+ ---------
839
+ __repr__, __str__
840
+ __eq__, __le__, __lt__, __ge__, __gt__, __hash__
841
+ __add__, __radd__, __sub__ (add/radd only with timedelta arg)
842
+
843
+ Methods
844
+ -------
845
+ timetuple()
846
+ toordinal()
847
+ weekday()
848
+ isoweekday(), isocalendar(), isoformat()
849
+ ctime()
850
+ strftime()
851
+
852
+ Properties (readonly):
853
+ ---------------------
854
+ year, month, day
855
+
856
+ """
857
+
858
+ def __init__(self, year, month=None, day=None):
859
+ """
860
+ Constructor.
861
+
862
+ :param year:
863
+ :param month: month, starting at 1
864
+ :param day: day, starting at 1
865
+ """
866
+ if month is None:
867
+ # We can receive a string/bytes single argument when unpickling a concrete date
868
+ with NoTracing():
869
+ dt = real_date(realize(year)) # type: ignore
870
+ year, month, day = dt.year, dt.month, dt.day
871
+ else:
872
+ year, month, day = _check_date_fields(year, month, day)
873
+ self._year = year
874
+ self._month = month
875
+ self._day = day
876
+ self._hashcode = -1
877
+
878
+ # Additional constructors
879
+
880
+ @classmethod
881
+ def fromtimestamp(cls, t):
882
+ """Construct a date from a POSIX timestamp (like time.time())."""
883
+ y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
884
+ return cls(y, m, d)
885
+
886
+ @classmethod
887
+ def today(cls):
888
+ """Construct a date from time.time()."""
889
+ t = _time.time()
890
+ return cls.fromtimestamp(t)
891
+
892
+ @classmethod
893
+ def fromordinal(cls, n):
894
+ """
895
+ Construct a date from a proleptic Gregorian ordinal.
896
+
897
+ January 1 of year 1 is day 1. Only the year, month and day are
898
+ non-zero in the result.
899
+ """
900
+ y, m, d = _ord2ymd(n)
901
+ return cls(y, m, d)
902
+
903
+ @classmethod
904
+ def fromisoformat(cls, date_string):
905
+ """Construct a date from the output of date.isoformat()."""
906
+ if not isinstance(date_string, str):
907
+ raise TypeError("fromisoformat: argument must be str")
908
+
909
+ try:
910
+ assert len(date_string) == 10
911
+ return cls(*_parse_isoformat_date(date_string))
912
+ except Exception:
913
+ raise ValueError(f"Invalid isoformat string")
914
+
915
+ @classmethod
916
+ def fromisocalendar(cls, year, week, day):
917
+ """
918
+ Construct a date from the ISO year, week number and weekday.
919
+
920
+ This is the inverse of the date.isocalendar() function
921
+ """
922
+ # Year is bounded this way because 9999-12-31 is (9999, 52, 5)
923
+ if not MINYEAR <= year <= MAXYEAR:
924
+ raise ValueError(f"Year is out of range")
925
+
926
+ if not 0 < week < 53:
927
+ out_of_range = True
928
+
929
+ if week == 53:
930
+ # ISO years have 53 weeks in them on years starting with a
931
+ # Thursday and leap years starting on a Wednesday
932
+ first_weekday = _ymd2ord(year, 1, 1) % 7
933
+ if first_weekday == 4 or (first_weekday == 3 and _is_leap(year)):
934
+ out_of_range = False
935
+
936
+ if out_of_range:
937
+ raise ValueError(f"Invalid week: {week}")
938
+
939
+ if not 0 < day < 8:
940
+ raise ValueError(f"Invalid weekday (range is [1, 7])")
941
+
942
+ # Now compute the offset from (Y, 1, 1) in days:
943
+ day_offset = (week - 1) * 7 + (day - 1)
944
+
945
+ # Calculate the ordinal day for monday, week 1
946
+ day_1 = _isoweek1monday(year)
947
+ ord_day = day_1 + day_offset
948
+
949
+ return cls(*_ord2ymd(ord_day))
950
+
951
+ @classmethod
952
+ def fromdate(cls, d: real_date):
953
+ return cls(d.year, d.month, d.day)
954
+
955
+ # Conversions to string
956
+
957
+ def __repr__(self):
958
+ return "%s.%s(%d, %d, %d)" % (
959
+ type(self).__module__,
960
+ self.__class__.__qualname__,
961
+ self._year,
962
+ self._month,
963
+ self._day,
964
+ )
965
+
966
+ # XXX These shouldn't depend on time.localtime(), because that
967
+ # clips the usable dates to [1970 .. 2038). At least ctime() is
968
+ # easily done without using strftime() -- that's better too because
969
+ # strftime("%c", ...) is locale specific.
970
+
971
+ def ctime(self):
972
+ """Return ctime() style string."""
973
+ weekday = self.toordinal() % 7 or 7
974
+ return "%s %s %2d 00:00:00 %04d" % (
975
+ _DAYNAMES[weekday],
976
+ _MONTHNAMES[self._month],
977
+ self._day,
978
+ self._year,
979
+ )
980
+
981
+ def strftime(self, fmt):
982
+ """Format using strftime()."""
983
+ return _wrap_strftime(self, fmt, self.timetuple())
984
+
985
+ def __format__(self, fmt):
986
+ if not isinstance(fmt, str):
987
+ raise TypeError("must be str, not %s" % type(fmt).__name__)
988
+ if len(fmt) != 0:
989
+ return self.strftime(fmt)
990
+ return str(self)
991
+
992
+ def isoformat(self):
993
+ """
994
+ Return the date formatted according to ISO.
995
+
996
+ This is 'YYYY-MM-DD'.
997
+
998
+ References
999
+ ----------
1000
+ - http://www.w3.org/TR/NOTE-datetime
1001
+ - http://www.cl.cam.ac.uk/~mgk25/iso-time.html
1002
+
1003
+ """
1004
+ return "%04d-%02d-%02d" % (self._year, self._month, self._day)
1005
+
1006
+ __str__ = isoformat
1007
+
1008
+ # Read-only field accessors
1009
+ @property
1010
+ def year(self):
1011
+ """year (1-9999)"""
1012
+ return self._year
1013
+
1014
+ @property
1015
+ def month(self):
1016
+ """month (1-12)"""
1017
+ return self._month
1018
+
1019
+ @property
1020
+ def day(self):
1021
+ """day (1-31)"""
1022
+ return self._day
1023
+
1024
+ # Standard conversions, __eq__, __le__, __lt__, __ge__, __gt__,
1025
+ # __hash__ (and helpers)
1026
+
1027
+ def timetuple(self):
1028
+ """Return local time tuple compatible with time.localtime()."""
1029
+ return _build_struct_time(self._year, self._month, self._day, 0, 0, 0, -1)
1030
+
1031
+ def toordinal(self):
1032
+ """
1033
+ Return proleptic Gregorian ordinal for the year, month and day.
1034
+
1035
+ January 1 of year 1 is day 1. Only the year, month and day values
1036
+ contribute to the result.
1037
+ """
1038
+ return _ymd2ord(self._year, self._month, self._day)
1039
+
1040
+ def replace(self, year=None, month=None, day=None):
1041
+ """Return a new date with new values for the specified fields."""
1042
+ if year is None:
1043
+ year = self._year
1044
+ if month is None:
1045
+ month = self._month
1046
+ if day is None:
1047
+ day = self._day
1048
+ return date(year, month, day)
1049
+
1050
+ # Comparisons of date objects with other.
1051
+
1052
+ def __eq__(self, other):
1053
+ if isinstance(other, any_date):
1054
+ return self._cmp(other) == 0
1055
+ return NotImplemented
1056
+
1057
+ def __le__(self, other):
1058
+ if isinstance(other, any_date):
1059
+ return self._cmp(other) <= 0
1060
+ return NotImplemented
1061
+
1062
+ def __lt__(self, other):
1063
+ if isinstance(other, any_date):
1064
+ return self._cmp(other) < 0
1065
+ return NotImplemented
1066
+
1067
+ def __ge__(self, other):
1068
+ if isinstance(other, any_date):
1069
+ return self._cmp(other) >= 0
1070
+ return NotImplemented
1071
+
1072
+ def __gt__(self, other):
1073
+ if isinstance(other, any_date):
1074
+ return self._cmp(other) > 0
1075
+ return NotImplemented
1076
+
1077
+ def _cmp(self, other):
1078
+ assert isinstance(other, any_date)
1079
+ y, m, d = self._year, self._month, self._day
1080
+ y2, m2, d2 = other.year, other.month, other.day
1081
+ return _cmp((y, m, d), (y2, m2, d2))
1082
+
1083
+ def __hash__(self):
1084
+ if self._hashcode == -1:
1085
+ self._hashcode = hash(_date_getstate(self))
1086
+ return self._hashcode
1087
+
1088
+ # Computations
1089
+
1090
+ def __add__(self, other):
1091
+ """Add a date to a timedelta."""
1092
+ if isinstance(other, any_timedelta):
1093
+ o = self.toordinal() + other.days
1094
+ if 0 < o <= _MAXORDINAL:
1095
+ return date.fromordinal(o)
1096
+ raise OverflowError("result out of range")
1097
+ return NotImplemented
1098
+
1099
+ __radd__ = __add__
1100
+
1101
+ def __sub__(self, other):
1102
+ """Subtract two dates, or a date and a timedelta."""
1103
+ if isinstance(other, any_timedelta):
1104
+ return self + timedelta(-other.days)
1105
+ if isinstance(other, any_date):
1106
+ days1 = self.toordinal()
1107
+ days2 = other.toordinal()
1108
+ return timedelta(days1 - days2)
1109
+ return NotImplemented
1110
+
1111
+ def weekday(self):
1112
+ """Return day of the week, where Monday == 0 ... Sunday == 6."""
1113
+ return (self.toordinal() + 6) % 7
1114
+
1115
+ # Day-of-the-week and week-of-the-year, according to ISO
1116
+
1117
+ def isoweekday(self):
1118
+ """Return day of the week, where Monday == 1 ... Sunday == 7."""
1119
+ # 1-Jan-0001 is a Monday
1120
+ return self.toordinal() % 7 or 7
1121
+
1122
+ def isocalendar(self):
1123
+ """
1124
+ Return a named tuple containing ISO year, week number, and weekday.
1125
+
1126
+ The first ISO week of the year is the (Mon-Sun) week
1127
+ containing the year's first Thursday; everything else derives
1128
+ from that.
1129
+
1130
+ The first week is 1; Monday is 1 ... Sunday is 7.
1131
+
1132
+ ISO calendar algorithm taken from
1133
+ http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
1134
+ (used with permission)
1135
+ """
1136
+ year = self._year
1137
+ week1monday = _isoweek1monday(year)
1138
+ today = _ymd2ord(self._year, self._month, self._day)
1139
+ # Internally, week and day have origin 0
1140
+ week, day = divmod(today - week1monday, 7)
1141
+ if week < 0:
1142
+ year -= 1
1143
+ week1monday = _isoweek1monday(year)
1144
+ week, day = divmod(today - week1monday, 7)
1145
+ elif week >= 52:
1146
+ if today >= _isoweek1monday(year + 1):
1147
+ year += 1
1148
+ week = 0
1149
+ return _IsoCalendarDate(year, week + 1, day + 1)
1150
+
1151
+ def __ch_realize__(self):
1152
+ return real_date(realize(self._year), realize(self._month), realize(self._day))
1153
+
1154
+ def __ch_pytype__(self):
1155
+ return real_date
1156
+
1157
+
1158
+ any_date = (date, real_date)
1159
+
1160
+ _date_class = date # so functions w/ args named "date" can get at the class
1161
+
1162
+ date.min = date(1, 1, 1) # type: ignore
1163
+ date.max = date(9999, 12, 31) # type: ignore
1164
+ date.resolution = timedelta(days=1) # type: ignore
1165
+
1166
+
1167
+ class tzinfo:
1168
+ """
1169
+ Abstract base class for time zone info classes.
1170
+
1171
+ Subclasses must override the name(), utcoffset() and dst() methods.
1172
+ """
1173
+
1174
+ def tzname(self, dt):
1175
+ """datetime -> string name of time zone."""
1176
+ raise NotImplementedError("tzinfo subclass must override tzname()")
1177
+
1178
+ def utcoffset(self, dt):
1179
+ """datetime -> timedelta, positive for east of UTC, negative for west of UTC"""
1180
+ raise NotImplementedError("tzinfo subclass must override utcoffset()")
1181
+
1182
+ def dst(self, dt):
1183
+ """
1184
+ datetime -> DST offset as timedelta, positive for east of UTC.
1185
+
1186
+ Return 0 if DST not in effect. utcoffset() must include the DST
1187
+ offset.
1188
+ """
1189
+ raise NotImplementedError("tzinfo subclass must override dst()")
1190
+
1191
+ def fromutc(self, dt):
1192
+ """datetime in UTC -> datetime in local time."""
1193
+ if not isinstance(dt, any_datetime):
1194
+ raise TypeError("fromutc() requires a datetime argument")
1195
+ if dt.tzinfo is not self:
1196
+ raise ValueError("dt.tzinfo is not self")
1197
+
1198
+ dtoff = dt.utcoffset()
1199
+ if dtoff is None:
1200
+ raise ValueError("fromutc() requires a non-None utcoffset() " "result")
1201
+
1202
+ # See the long comment block at the end of this file for an
1203
+ # explanation of this algorithm.
1204
+ dtdst = dt.dst()
1205
+ if dtdst is None:
1206
+ raise ValueError("fromutc() requires a non-None dst() result")
1207
+ delta = dtoff - dtdst
1208
+ if delta:
1209
+ dt += delta
1210
+ dtdst = dt.dst()
1211
+ if dtdst is None:
1212
+ raise ValueError(
1213
+ "fromutc(): dt.dst gave inconsistent " "results; cannot convert"
1214
+ )
1215
+ return dt + dtdst
1216
+
1217
+ def __reduce__(self):
1218
+ getinitargs = getattr(self, "__getinitargs__", None)
1219
+ if getinitargs:
1220
+ args = getinitargs()
1221
+ else:
1222
+ args = ()
1223
+ getstate = getattr(self, "__getstate__", None)
1224
+ if getstate:
1225
+ state = getstate()
1226
+ else:
1227
+ state = getattr(self, "__dict__", None) or None
1228
+ if state is None:
1229
+ return (type(self), args)
1230
+ else:
1231
+ return (type(self), args, state)
1232
+
1233
+ def __ch_pytype__(self):
1234
+ return real_tzinfo
1235
+
1236
+
1237
+ any_tzinfo = (tzinfo, real_tzinfo)
1238
+
1239
+
1240
+ class IsoCalendarDate(tuple):
1241
+ def __new__(cls, year, week, weekday):
1242
+ return super().__new__(cls, (year, week, weekday))
1243
+
1244
+ @property
1245
+ def year(self):
1246
+ return self[0]
1247
+
1248
+ @property
1249
+ def week(self):
1250
+ return self[1]
1251
+
1252
+ @property
1253
+ def weekday(self):
1254
+ return self[2]
1255
+
1256
+ def __repr__(self):
1257
+ return (
1258
+ f"{self.__class__.__name__}"
1259
+ f"(year={self[0]}, week={self[1]}, weekday={self[2]})"
1260
+ )
1261
+
1262
+
1263
+ _IsoCalendarDate = IsoCalendarDate
1264
+ del IsoCalendarDate
1265
+ _tzinfo_class = tzinfo
1266
+
1267
+
1268
+ def _time_getstate(self):
1269
+ us2, us3 = divmod(self.microsecond, 256)
1270
+ us1, us2 = divmod(us2, 256)
1271
+ h = self.hour
1272
+ basestate = bytes([h, self.minute, self.second, us1, us2, us3])
1273
+ return (basestate,)
1274
+
1275
+
1276
+ class time:
1277
+ """
1278
+ Time with time zone.
1279
+
1280
+ Constructors
1281
+ ------------
1282
+ __new__()
1283
+
1284
+ Operators
1285
+ ---------
1286
+ __repr__, __str__
1287
+ __eq__, __le__, __lt__, __ge__, __gt__, __hash__
1288
+
1289
+ Methods
1290
+ -------
1291
+ strftime()
1292
+ isoformat()
1293
+ utcoffset()
1294
+ tzname()
1295
+ dst()
1296
+
1297
+ Properties (readonly):
1298
+ ---------------------
1299
+ hour, minute, second, microsecond, tzinfo, fold
1300
+
1301
+ """
1302
+
1303
+ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, fold=0):
1304
+ hour, minute, second, microsecond, fold = _check_time_fields(
1305
+ hour, minute, second, microsecond, fold
1306
+ )
1307
+ _check_tzinfo_arg(tzinfo)
1308
+ self = object.__new__(cls)
1309
+ self._hour = hour
1310
+ self._minute = minute
1311
+ self._second = second
1312
+ self._microsecond = microsecond
1313
+ self._tzinfo = tzinfo
1314
+ self._hashcode = -1
1315
+ self._fold = fold
1316
+ return self
1317
+
1318
+ # Read-only field accessors
1319
+ @property
1320
+ def hour(self):
1321
+ """hour (0-23)"""
1322
+ return self._hour
1323
+
1324
+ @property
1325
+ def minute(self):
1326
+ """minute (0-59)"""
1327
+ return self._minute
1328
+
1329
+ @property
1330
+ def second(self):
1331
+ """second (0-59)"""
1332
+ return self._second
1333
+
1334
+ @property
1335
+ def microsecond(self):
1336
+ """microsecond (0-999999)"""
1337
+ return self._microsecond
1338
+
1339
+ @property
1340
+ def tzinfo(self):
1341
+ """timezone info object"""
1342
+ return self._tzinfo
1343
+
1344
+ @property
1345
+ def fold(self):
1346
+ return self._fold
1347
+
1348
+ # Standard conversions, __hash__ (and helpers)
1349
+
1350
+ # Comparisons of time objects with other.
1351
+
1352
+ def __eq__(self, other):
1353
+ if isinstance(other, any_time):
1354
+ return self._cmp(other, allow_mixed=True) == 0
1355
+ else:
1356
+ return NotImplemented
1357
+
1358
+ def __le__(self, other):
1359
+ if isinstance(other, any_time):
1360
+ return self._cmp(other) <= 0
1361
+ else:
1362
+ return NotImplemented
1363
+
1364
+ def __lt__(self, other):
1365
+ if isinstance(other, any_time):
1366
+ return self._cmp(other) < 0
1367
+ else:
1368
+ return NotImplemented
1369
+
1370
+ def __ge__(self, other):
1371
+ if isinstance(other, any_time):
1372
+ return self._cmp(other) >= 0
1373
+ else:
1374
+ return NotImplemented
1375
+
1376
+ def __gt__(self, other):
1377
+ if isinstance(other, any_time):
1378
+ return self._cmp(other) > 0
1379
+ else:
1380
+ return NotImplemented
1381
+
1382
+ def _cmp(self, other, allow_mixed=False):
1383
+ assert isinstance(other, any_time)
1384
+ mytz = self._tzinfo
1385
+ ottz = other.tzinfo
1386
+ myoff = otoff = None
1387
+
1388
+ if mytz is ottz:
1389
+ base_compare = True
1390
+ else:
1391
+ myoff = self.utcoffset()
1392
+ otoff = other.utcoffset()
1393
+ base_compare = myoff == otoff
1394
+
1395
+ if base_compare:
1396
+ return _cmp(
1397
+ (self._hour, self._minute, self._second, self._microsecond),
1398
+ (other.hour, other.minute, other.second, other.microsecond),
1399
+ )
1400
+ if myoff is None or otoff is None:
1401
+ if allow_mixed:
1402
+ return 2 # arbitrary non-zero value
1403
+ else:
1404
+ raise TypeError("cannot compare naive and aware times")
1405
+ myhhmm = self._hour * 60 + self._minute - myoff // timedelta(minutes=1)
1406
+ othhmm = other.hour * 60 + other.minute - otoff // timedelta(minutes=1)
1407
+ return _cmp(
1408
+ (myhhmm, self._second, self._microsecond),
1409
+ (othhmm, other.second, other.microsecond),
1410
+ )
1411
+
1412
+ def __hash__(self):
1413
+ """Hash."""
1414
+ if self._hashcode == -1:
1415
+ if self.fold:
1416
+ t = self.replace(fold=0)
1417
+ else:
1418
+ t = self
1419
+ tzoff = t.utcoffset()
1420
+ if not tzoff: # zero or None
1421
+ self._hashcode = hash(_time_getstate(t)[0])
1422
+ else:
1423
+ h, m = divmod(
1424
+ timedelta(hours=self.hour, minutes=self.minute) - tzoff,
1425
+ timedelta(hours=1),
1426
+ )
1427
+ assert not m % timedelta(minutes=1), "whole minute"
1428
+ m //= timedelta(minutes=1)
1429
+ if 0 <= h < 24:
1430
+ self._hashcode = hash(time(h, m, self.second, self.microsecond))
1431
+ else:
1432
+ self._hashcode = hash((h, m, self.second, self.microsecond))
1433
+ return self._hashcode
1434
+
1435
+ # Conversion to string
1436
+
1437
+ def _tzstr(self):
1438
+ """Return formatted timezone offset (+xx:xx) or an empty string."""
1439
+ off = self.utcoffset()
1440
+ return _format_offset(off)
1441
+
1442
+ def __repr__(self):
1443
+ """Convert to formal string, for repr()."""
1444
+ if self._microsecond != 0:
1445
+ s = ", %d, %d" % (self._second, self._microsecond)
1446
+ elif self._second != 0:
1447
+ s = ", %d" % self._second
1448
+ else:
1449
+ s = ""
1450
+ s = "%s.%s(%d, %d%s)" % (
1451
+ type(self).__module__,
1452
+ self.__class__.__qualname__,
1453
+ self._hour,
1454
+ self._minute,
1455
+ s,
1456
+ )
1457
+ if self._tzinfo is not None:
1458
+ assert s[-1:] == ")"
1459
+ s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
1460
+ if self._fold:
1461
+ assert s[-1:] == ")"
1462
+ s = s[:-1] + ", fold=1)"
1463
+ return s
1464
+
1465
+ def isoformat(self, timespec="auto"):
1466
+ """
1467
+ Return the time formatted according to ISO.
1468
+
1469
+ The full format is 'HH:MM:SS.mmmmmm+zz:zz'. By default, the fractional
1470
+ part is omitted if self.microsecond == 0.
1471
+
1472
+ The optional argument timespec specifies the number of additional
1473
+ terms of the time to include. Valid options are 'auto', 'hours',
1474
+ 'minutes', 'seconds', 'milliseconds' and 'microseconds'.
1475
+ """
1476
+ s = _format_time(
1477
+ self._hour, self._minute, self._second, self._microsecond, timespec
1478
+ )
1479
+ tz = self._tzstr()
1480
+ if tz:
1481
+ s += tz
1482
+ return s
1483
+
1484
+ __str__ = isoformat
1485
+
1486
+ @classmethod
1487
+ def fromisoformat(cls, time_string):
1488
+ """Construct a time from the output of isoformat()."""
1489
+ if not isinstance(time_string, str):
1490
+ raise TypeError("fromisoformat: argument must be str")
1491
+
1492
+ try:
1493
+ return cls(*_parse_isoformat_time(time_string))
1494
+ except Exception:
1495
+ raise ValueError(f"Invalid isoformat string")
1496
+
1497
+ def strftime(self, fmt):
1498
+ """
1499
+ Format using strftime(). The date part of the timestamp passed
1500
+ to underlying strftime should not be used.
1501
+ """
1502
+ # The year must be >= 1000 else Python's strftime implementation
1503
+ # can raise a bogus exception.
1504
+ timetuple = (1900, 1, 1, self._hour, self._minute, self._second, 0, 1, -1)
1505
+ return _wrap_strftime(self, fmt, timetuple)
1506
+
1507
+ def __format__(self, fmt):
1508
+ if not isinstance(fmt, str):
1509
+ raise TypeError("must be str, not %s" % type(fmt).__name__)
1510
+ if len(fmt) != 0:
1511
+ return self.strftime(fmt)
1512
+ return str(self)
1513
+
1514
+ # Timezone functions
1515
+
1516
+ def utcoffset(self):
1517
+ """
1518
+ Return the timezone offset as timedelta, positive east of UTC
1519
+ (negative west of UTC).
1520
+ """
1521
+ if self._tzinfo is None:
1522
+ return None
1523
+ offset = self._tzinfo.utcoffset(None)
1524
+ _check_utc_offset("utcoffset", offset)
1525
+ return offset
1526
+
1527
+ def tzname(self):
1528
+ """
1529
+ Return the timezone name.
1530
+
1531
+ Note that the name is 100% informational -- there's no requirement that
1532
+ it mean anything in particular. For example, "GMT", "UTC", "-500",
1533
+ "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
1534
+ """
1535
+ if self._tzinfo is None:
1536
+ return None
1537
+ name = self._tzinfo.tzname(None)
1538
+ _check_tzname(name)
1539
+ return name
1540
+
1541
+ def dst(self):
1542
+ """
1543
+ Return 0 if DST is not in effect, or the DST offset (as timedelta
1544
+ positive eastward) if DST is in effect.
1545
+
1546
+ This is purely informational; the DST offset has already been added to
1547
+ the UTC offset returned by utcoffset() if applicable, so there's no
1548
+ need to consult dst() unless you're interested in displaying the DST
1549
+ info.
1550
+ """
1551
+ if self._tzinfo is None:
1552
+ return None
1553
+ offset = self._tzinfo.dst(None)
1554
+ _check_utc_offset("dst", offset)
1555
+ return offset
1556
+
1557
+ def replace(
1558
+ self,
1559
+ hour=None,
1560
+ minute=None,
1561
+ second=None,
1562
+ microsecond=None,
1563
+ tzinfo=True,
1564
+ *,
1565
+ fold=None,
1566
+ ):
1567
+ """Return a new time with new values for the specified fields."""
1568
+ if hour is None:
1569
+ hour = self.hour
1570
+ if minute is None:
1571
+ minute = self.minute
1572
+ if second is None:
1573
+ second = self.second
1574
+ if microsecond is None:
1575
+ microsecond = self.microsecond
1576
+ if tzinfo is True:
1577
+ tzinfo = self.tzinfo
1578
+ if fold is None:
1579
+ fold = self._fold
1580
+ return time(hour, minute, second, microsecond, tzinfo, fold=fold)
1581
+
1582
+ def __ch_realize__(self):
1583
+ return real_time(
1584
+ realize(self._hour),
1585
+ realize(self._minute),
1586
+ realize(self._second),
1587
+ realize(self._microsecond),
1588
+ realize(self._tzinfo),
1589
+ fold=realize(self._fold),
1590
+ )
1591
+
1592
+ def __ch_pytype__(self):
1593
+ return real_time
1594
+
1595
+
1596
+ any_time = (time, real_time)
1597
+
1598
+ _time_class = time # so functions w/ args named "time" can get at the class
1599
+
1600
+ time.min = time(0, 0, 0) # type: ignore
1601
+ time.max = time(23, 59, 59, 999999) # type: ignore
1602
+ time.resolution = timedelta(microseconds=1) # type: ignore
1603
+
1604
+
1605
+ def _datetime_getstate(self):
1606
+ yhi, ylo = divmod(self.year, 256)
1607
+ us2, us3 = divmod(self.microsecond, 256)
1608
+ us1, us2 = divmod(us2, 256)
1609
+ m = self._month
1610
+ basestate = bytes(
1611
+ [yhi, ylo, m, self._day, self.hour, self.minute, self.second, us1, us2, us3]
1612
+ )
1613
+ return (basestate,)
1614
+
1615
+
1616
+ class datetime(date):
1617
+ """
1618
+ datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
1619
+
1620
+ The year, month and day arguments are required. tzinfo may be None, or an
1621
+ instance of a tzinfo subclass. The remaining arguments may be ints.
1622
+ """
1623
+
1624
+ def __init__(
1625
+ self,
1626
+ year,
1627
+ month,
1628
+ day=None,
1629
+ hour=0,
1630
+ minute=0,
1631
+ second=0,
1632
+ microsecond=0,
1633
+ tzinfo=None,
1634
+ *,
1635
+ fold=0,
1636
+ ):
1637
+ year, month, day = _check_date_fields(year, month, day)
1638
+ hour, minute, second, microsecond, fold = _check_time_fields(
1639
+ hour, minute, second, microsecond, fold
1640
+ )
1641
+ _check_tzinfo_arg(tzinfo)
1642
+ date.__init__(self, year, month, day)
1643
+ self._hour = hour
1644
+ self._minute = minute
1645
+ self._second = second
1646
+ self._microsecond = microsecond
1647
+ self._tzinfo = tzinfo
1648
+ self._hashcode = -1
1649
+ self._fold = fold
1650
+
1651
+ # Read-only field accessors
1652
+ @property
1653
+ def hour(self):
1654
+ """hour (0-23)"""
1655
+ return self._hour
1656
+
1657
+ @property
1658
+ def minute(self):
1659
+ """minute (0-59)"""
1660
+ return self._minute
1661
+
1662
+ @property
1663
+ def second(self):
1664
+ """second (0-59)"""
1665
+ return self._second
1666
+
1667
+ @property
1668
+ def microsecond(self):
1669
+ """microsecond (0-999999)"""
1670
+ return self._microsecond
1671
+
1672
+ @property
1673
+ def tzinfo(self):
1674
+ """timezone info object"""
1675
+ return self._tzinfo
1676
+
1677
+ @property
1678
+ def fold(self):
1679
+ return self._fold
1680
+
1681
+ @classmethod
1682
+ def _fromtimestamp(cls, t, utc, tz):
1683
+ """
1684
+ Construct a datetime from a POSIX timestamp (like time.time()).
1685
+
1686
+ A timezone info object may be passed in as well.
1687
+ """
1688
+ frac, t = _math.modf(t)
1689
+ us = round(frac * 1e6)
1690
+ if us >= 1000000:
1691
+ t += 1
1692
+ us -= 1000000
1693
+ elif us < 0:
1694
+ t -= 1
1695
+ us += 1000000
1696
+
1697
+ converter = _time.gmtime if utc else _time.localtime
1698
+ y, m, d, hh, mm, ss, weekday, jday, dst = converter(t)
1699
+ ss = min(ss, 59) # clamp out leap seconds if the platform has them
1700
+ result = cls(y, m, d, hh, mm, ss, us, tz)
1701
+ if tz is None:
1702
+ # As of version 2015f max fold in IANA database is
1703
+ # 23 hours at 1969-09-30 13:00:00 in Kwajalein.
1704
+ # Let's probe 24 hours in the past to detect a transition:
1705
+ max_fold_seconds = 24 * 3600
1706
+
1707
+ # On Windows localtime_s throws an OSError for negative values,
1708
+ # thus we can't perform fold detection for values of time less
1709
+ # than the max time fold. See comments in _datetimemodule's
1710
+ # version of this method for more details.
1711
+ if t < max_fold_seconds and sys.platform.startswith("win"):
1712
+ return result
1713
+
1714
+ y, m, d, hh, mm, ss = converter(t - max_fold_seconds)[:6]
1715
+ probe1 = cls(y, m, d, hh, mm, ss, us, tz)
1716
+ trans = result - probe1 - timedelta(0, max_fold_seconds)
1717
+ if trans.days < 0:
1718
+ y, m, d, hh, mm, ss = converter(t + trans // timedelta(0, 1))[:6]
1719
+ probe2 = cls(y, m, d, hh, mm, ss, us, tz)
1720
+ if probe2 == result:
1721
+ result._fold = 1
1722
+ else:
1723
+ result = tz.fromutc(result)
1724
+ return result
1725
+
1726
+ @classmethod
1727
+ def fromtimestamp(cls, t, tz=None):
1728
+ """
1729
+ Construct a datetime from a POSIX timestamp (like time.time()).
1730
+
1731
+ A timezone info object may be passed in as well.
1732
+ """
1733
+ _check_tzinfo_arg(tz)
1734
+
1735
+ return cls._fromtimestamp(t, tz is not None, tz)
1736
+
1737
+ @classmethod
1738
+ def utcfromtimestamp(cls, t):
1739
+ """Construct a naive UTC datetime from a POSIX timestamp."""
1740
+ return cls._fromtimestamp(t, True, None)
1741
+
1742
+ @classmethod
1743
+ def now(cls, tz=None):
1744
+ """Construct a datetime from time.time() and optional time zone info."""
1745
+ t = _time.time()
1746
+ return cls.fromtimestamp(t, tz)
1747
+
1748
+ @classmethod
1749
+ def utcnow(cls):
1750
+ """Construct a UTC datetime from time.time()."""
1751
+ t = _time.time()
1752
+ return cls.utcfromtimestamp(t)
1753
+
1754
+ @classmethod
1755
+ def combine(cls, date, time, tzinfo=True):
1756
+ """Construct a datetime from a given date and a given time."""
1757
+ if not isinstance(date, any_date):
1758
+ raise TypeError("date argument must be a date instance")
1759
+ if not isinstance(time, any_time):
1760
+ raise TypeError("time argument must be a time instance")
1761
+ if tzinfo is True:
1762
+ tzinfo = time.tzinfo
1763
+ return cls(
1764
+ date.year,
1765
+ date.month,
1766
+ date.day,
1767
+ time.hour,
1768
+ time.minute,
1769
+ time.second,
1770
+ time.microsecond,
1771
+ tzinfo,
1772
+ fold=time.fold,
1773
+ )
1774
+
1775
+ @classmethod
1776
+ def fromisoformat(cls, date_string):
1777
+ """Construct a datetime from the output of datetime.isoformat()."""
1778
+ if not isinstance(date_string, str):
1779
+ raise TypeError("fromisoformat: argument must be str")
1780
+
1781
+ # Split this at the separator
1782
+ dstr = date_string[0:10]
1783
+ tstr = date_string[11:]
1784
+
1785
+ try:
1786
+ date_components = _parse_isoformat_date(dstr)
1787
+ except ValueError:
1788
+ raise ValueError(f"Invalid isoformat string")
1789
+
1790
+ if tstr:
1791
+ try:
1792
+ time_components = _parse_isoformat_time(tstr)
1793
+ except ValueError:
1794
+ raise ValueError(f"Invalid isoformat string")
1795
+ else:
1796
+ time_components = [0, 0, 0, 0, None]
1797
+
1798
+ return cls(*(date_components + time_components))
1799
+
1800
+ @classmethod
1801
+ def fromdatetime(cls, d: real_datetime):
1802
+ return cls(
1803
+ d.year,
1804
+ d.month,
1805
+ d.day,
1806
+ d.hour,
1807
+ d.minute,
1808
+ d.second,
1809
+ d.microsecond,
1810
+ d.tzinfo,
1811
+ fold=d.fold,
1812
+ )
1813
+
1814
+ def timetuple(self):
1815
+ """Return local time tuple compatible with time.localtime()."""
1816
+ dst = self.dst()
1817
+ if dst is None:
1818
+ dst = -1
1819
+ elif dst:
1820
+ dst = 1
1821
+ else:
1822
+ dst = 0
1823
+ return _build_struct_time(
1824
+ self.year, self.month, self.day, self.hour, self.minute, self.second, dst
1825
+ )
1826
+
1827
+ def _mktime(self):
1828
+ """Return integer POSIX timestamp."""
1829
+ epoch = datetime(1970, 1, 1)
1830
+ max_fold_seconds = 24 * 3600
1831
+ t = (self - epoch) // timedelta(0, 1)
1832
+
1833
+ def local(u):
1834
+ y, m, d, hh, mm, ss = _time.localtime(u)[:6]
1835
+ return (datetime(y, m, d, hh, mm, ss) - epoch) // timedelta(0, 1)
1836
+
1837
+ # Our goal is to solve t = local(u) for u.
1838
+ a = local(t) - t
1839
+ u1 = t - a
1840
+ t1 = local(u1)
1841
+ if t1 == t:
1842
+ # We found one solution, but it may not be the one we need.
1843
+ # Look for an earlier solution (if `fold` is 0), or a
1844
+ # later one (if `fold` is 1).
1845
+ u2 = u1 + (-max_fold_seconds, max_fold_seconds)[self.fold]
1846
+ b = local(u2) - u2
1847
+ if a == b:
1848
+ return u1
1849
+ else:
1850
+ b = t1 - u1
1851
+ assert a != b
1852
+ u2 = t - b
1853
+ t2 = local(u2)
1854
+ if t2 == t:
1855
+ return u2
1856
+ if t1 == t:
1857
+ return u1
1858
+ # We have found both offsets a and b, but neither t - a nor t - b is
1859
+ # a solution. This means t is in the gap.
1860
+ return (max, min)[self.fold](u1, u2)
1861
+
1862
+ def timestamp(self):
1863
+ """Return POSIX timestamp as float"""
1864
+ if self._tzinfo is None:
1865
+ s = self._mktime()
1866
+ return s + self.microsecond / 1e6
1867
+ else:
1868
+ return (self - _EPOCH).total_seconds()
1869
+
1870
+ def utctimetuple(self):
1871
+ """Return UTC time tuple compatible with time.gmtime()."""
1872
+ offset = self.utcoffset()
1873
+ if offset:
1874
+ self -= offset
1875
+ y, m, d = self.year, self.month, self.day
1876
+ hh, mm, ss = self.hour, self.minute, self.second
1877
+ return _build_struct_time(y, m, d, hh, mm, ss, 0)
1878
+
1879
+ def date(self):
1880
+ """Return the date part."""
1881
+ return date(self._year, self._month, self._day)
1882
+
1883
+ def time(self):
1884
+ """Return the time part, with tzinfo None."""
1885
+ return time(
1886
+ self.hour, self.minute, self.second, self.microsecond, fold=self.fold
1887
+ )
1888
+
1889
+ def timetz(self):
1890
+ """Return the time part, with same tzinfo."""
1891
+ return time(
1892
+ self.hour,
1893
+ self.minute,
1894
+ self.second,
1895
+ self.microsecond,
1896
+ self._tzinfo,
1897
+ fold=self.fold,
1898
+ )
1899
+
1900
+ def replace(
1901
+ self,
1902
+ year=None,
1903
+ month=None,
1904
+ day=None,
1905
+ hour=None,
1906
+ minute=None,
1907
+ second=None,
1908
+ microsecond=None,
1909
+ tzinfo=True,
1910
+ *,
1911
+ fold=None,
1912
+ ):
1913
+ """Return a new datetime with new values for the specified fields."""
1914
+ if year is None:
1915
+ year = self.year
1916
+ if month is None:
1917
+ month = self.month
1918
+ if day is None:
1919
+ day = self.day
1920
+ if hour is None:
1921
+ hour = self.hour
1922
+ if minute is None:
1923
+ minute = self.minute
1924
+ if second is None:
1925
+ second = self.second
1926
+ if microsecond is None:
1927
+ microsecond = self.microsecond
1928
+ if tzinfo is True:
1929
+ tzinfo = self.tzinfo
1930
+ if fold is None:
1931
+ fold = self.fold
1932
+ return datetime(
1933
+ year, month, day, hour, minute, second, microsecond, tzinfo, fold=fold
1934
+ )
1935
+
1936
+ def _local_timezone(self):
1937
+ if self.tzinfo is None:
1938
+ ts = self._mktime()
1939
+ else:
1940
+ ts = (self - _EPOCH) // timedelta(seconds=1)
1941
+ localtm = _time.localtime(ts)
1942
+ local = datetime(*localtm[:6])
1943
+ # Extract TZ data
1944
+ gmtoff = localtm.tm_gmtoff
1945
+ zone = localtm.tm_zone
1946
+ return timezone(timedelta(seconds=gmtoff), zone)
1947
+
1948
+ def astimezone(self, tz=None):
1949
+ if tz is None:
1950
+ tz = self._local_timezone()
1951
+ elif not isinstance(tz, any_tzinfo):
1952
+ raise TypeError("tz argument must be an instance of tzinfo")
1953
+
1954
+ mytz = self.tzinfo
1955
+ if mytz is None:
1956
+ mytz = self._local_timezone()
1957
+ myoffset = mytz.utcoffset(self)
1958
+ else:
1959
+ myoffset = mytz.utcoffset(self)
1960
+ if myoffset is None:
1961
+ mytz = self.replace(tzinfo=None)._local_timezone()
1962
+ myoffset = mytz.utcoffset(self)
1963
+
1964
+ if tz is mytz:
1965
+ return self
1966
+
1967
+ # Convert self to UTC, and attach the new time zone object.
1968
+ utc = (self - myoffset).replace(tzinfo=tz)
1969
+
1970
+ # Convert from UTC to tz's local time.
1971
+ return tz.fromutc(utc)
1972
+
1973
+ # Ways to produce a string.
1974
+
1975
+ def ctime(self):
1976
+ """Return ctime() style string."""
1977
+ weekday = self.toordinal() % 7 or 7
1978
+ return "%s %s %2d %02d:%02d:%02d %04d" % (
1979
+ _DAYNAMES[weekday],
1980
+ _MONTHNAMES[self._month],
1981
+ self._day,
1982
+ self._hour,
1983
+ self._minute,
1984
+ self._second,
1985
+ self._year,
1986
+ )
1987
+
1988
+ def isoformat(self, sep="T", timespec="auto"):
1989
+ """
1990
+ Return the time formatted according to ISO.
1991
+
1992
+ The full format looks like 'YYYY-MM-DD HH:MM:SS.mmmmmm'.
1993
+ By default, the fractional part is omitted if self.microsecond == 0.
1994
+
1995
+ If self.tzinfo is not None, the UTC offset is also attached, giving
1996
+ giving a full format of 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM'.
1997
+
1998
+ Optional argument sep specifies the separator between date and
1999
+ time, default 'T'.
2000
+
2001
+ The optional argument timespec specifies the number of additional
2002
+ terms of the time to include. Valid options are 'auto', 'hours',
2003
+ 'minutes', 'seconds', 'milliseconds' and 'microseconds'.
2004
+ """
2005
+ s = "%04d-%02d-%02d%c" % (
2006
+ self._year,
2007
+ self._month,
2008
+ self._day,
2009
+ sep,
2010
+ ) + _format_time(
2011
+ self._hour, self._minute, self._second, self._microsecond, timespec
2012
+ )
2013
+
2014
+ off = self.utcoffset()
2015
+ tz = _format_offset(off)
2016
+ if tz:
2017
+ s += tz
2018
+
2019
+ return s
2020
+
2021
+ def __repr__(self):
2022
+ """Convert to formal string, for repr()."""
2023
+ L = [
2024
+ self._year,
2025
+ self._month,
2026
+ self._day, # These are never zero
2027
+ self._hour,
2028
+ self._minute,
2029
+ self._second,
2030
+ self._microsecond,
2031
+ ]
2032
+ if L[-1] == 0:
2033
+ del L[-1]
2034
+ if L[-1] == 0:
2035
+ del L[-1]
2036
+ s = "%s.%s(%s)" % (
2037
+ type(self).__module__,
2038
+ self.__class__.__qualname__,
2039
+ ", ".join(map(str, L)),
2040
+ )
2041
+ if self._tzinfo is not None:
2042
+ assert s[-1:] == ")"
2043
+ s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")"
2044
+ if self._fold:
2045
+ assert s[-1:] == ")"
2046
+ s = s[:-1] + ", fold=1)"
2047
+ return s
2048
+
2049
+ def __str__(self):
2050
+ """Convert to string, for str()."""
2051
+ return self.isoformat(sep=" ")
2052
+
2053
+ @classmethod
2054
+ def strptime(cls, date_string, format):
2055
+ """string, format -> new datetime parsed from a string (like time.strptime())."""
2056
+ import _strptime # type: ignore
2057
+
2058
+ return _strptime._strptime_datetime(cls, date_string, format)
2059
+
2060
+ def _realized_if_concrete_tzinfo(self):
2061
+ with NoTracing():
2062
+ if isinstance(self._tzinfo, any_tzinfo):
2063
+ return realize(self)
2064
+ return self
2065
+
2066
+ def utcoffset(self):
2067
+ """
2068
+ Return the timezone offset as timedelta positive east of UTC (negative west of
2069
+ UTC).
2070
+ """
2071
+ if self._tzinfo is None:
2072
+ return None
2073
+ offset = self._tzinfo.utcoffset(self._realized_if_concrete_tzinfo())
2074
+ _check_utc_offset("utcoffset", offset)
2075
+ return offset
2076
+
2077
+ def tzname(self):
2078
+ """
2079
+ Return the timezone name.
2080
+
2081
+ Note that the name is 100% informational -- there's no requirement that
2082
+ it mean anything in particular. For example, "GMT", "UTC", "-500",
2083
+ "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies.
2084
+ """
2085
+ if self._tzinfo is None:
2086
+ return None
2087
+ name = self._tzinfo.tzname(self._realized_if_concrete_tzinfo())
2088
+ _check_tzname(name)
2089
+ return name
2090
+
2091
+ def dst(self):
2092
+ """
2093
+ Return 0 if DST is not in effect, or the DST offset (as timedelta
2094
+ positive eastward) if DST is in effect.
2095
+
2096
+ This is purely informational; the DST offset has already been added to
2097
+ the UTC offset returned by utcoffset() if applicable, so there's no
2098
+ need to consult dst() unless you're interested in displaying the DST
2099
+ info.
2100
+ """
2101
+ if self._tzinfo is None:
2102
+ return None
2103
+ offset = self._tzinfo.dst(self._realized_if_concrete_tzinfo())
2104
+ _check_utc_offset("dst", offset)
2105
+ return offset
2106
+
2107
+ # Comparisons of datetime objects with other.
2108
+
2109
+ def __eq__(self, other):
2110
+ if isinstance(other, any_datetime):
2111
+ return self._cmp(other, allow_mixed=True) == 0
2112
+ elif not isinstance(other, any_date):
2113
+ return NotImplemented
2114
+ else:
2115
+ return False
2116
+
2117
+ def __le__(self, other):
2118
+ if isinstance(other, any_datetime):
2119
+ return self._cmp(other) <= 0
2120
+ elif not isinstance(other, any_date):
2121
+ return NotImplemented
2122
+ else:
2123
+ _cmperror(self, other)
2124
+
2125
+ def __lt__(self, other):
2126
+ if isinstance(other, any_datetime):
2127
+ return self._cmp(other) < 0
2128
+ elif not isinstance(other, any_date):
2129
+ return NotImplemented
2130
+ else:
2131
+ _cmperror(self, other)
2132
+
2133
+ def __ge__(self, other):
2134
+ if isinstance(other, any_datetime):
2135
+ return self._cmp(other) >= 0
2136
+ elif not isinstance(other, any_date):
2137
+ return NotImplemented
2138
+ else:
2139
+ _cmperror(self, other)
2140
+
2141
+ def __gt__(self, other):
2142
+ if isinstance(other, any_datetime):
2143
+ return self._cmp(other) > 0
2144
+ elif not isinstance(other, any_date):
2145
+ return NotImplemented
2146
+ else:
2147
+ _cmperror(self, other)
2148
+
2149
+ def _cmp(self, other, allow_mixed=False):
2150
+ assert isinstance(other, any_datetime)
2151
+ mytz = self._tzinfo
2152
+ ottz = other.tzinfo
2153
+ myoff = otoff = None
2154
+
2155
+ if mytz is ottz:
2156
+ base_compare = True
2157
+ else:
2158
+ myoff = self.utcoffset()
2159
+ otoff = other.utcoffset()
2160
+ # Assume that allow_mixed means that we are called from __eq__
2161
+ if allow_mixed:
2162
+ if myoff != self.replace(fold=not self.fold).utcoffset():
2163
+ return 2
2164
+ if otoff != other.replace(fold=not other.fold).utcoffset():
2165
+ return 2
2166
+ base_compare = myoff == otoff
2167
+
2168
+ if base_compare:
2169
+ return _cmp(
2170
+ (
2171
+ self._year,
2172
+ self._month,
2173
+ self._day,
2174
+ self._hour,
2175
+ self._minute,
2176
+ self._second,
2177
+ self._microsecond,
2178
+ ),
2179
+ (
2180
+ other.year,
2181
+ other.month,
2182
+ other.day,
2183
+ other.hour,
2184
+ other.minute,
2185
+ other.second,
2186
+ other.microsecond,
2187
+ ),
2188
+ )
2189
+ if myoff is None or otoff is None:
2190
+ if allow_mixed:
2191
+ return 2 # arbitrary non-zero value
2192
+ else:
2193
+ raise TypeError("cannot compare naive and aware datetimes")
2194
+ # XXX What follows could be done more efficiently...
2195
+ diff = self - other # this will take offsets into account
2196
+ if diff.days < 0:
2197
+ return -1
2198
+ return diff and 1 or 0
2199
+
2200
+ def __add__(self, other):
2201
+ """Add a datetime and a timedelta."""
2202
+ if not isinstance(other, any_timedelta):
2203
+ return NotImplemented
2204
+ delta = timedelta(
2205
+ self.toordinal(),
2206
+ hours=self._hour,
2207
+ minutes=self._minute,
2208
+ seconds=self._second,
2209
+ microseconds=self._microsecond,
2210
+ )
2211
+ delta += other
2212
+ hour, rem = divmod(delta.seconds, 3600)
2213
+ minute, second = divmod(rem, 60)
2214
+ if 0 < delta.days <= _MAXORDINAL:
2215
+ return datetime.combine(
2216
+ date.fromordinal(delta.days),
2217
+ time(hour, minute, second, delta.microseconds, tzinfo=self._tzinfo),
2218
+ )
2219
+ raise OverflowError("result out of range")
2220
+
2221
+ __radd__ = __add__
2222
+
2223
+ def __sub__(self, other):
2224
+ """Subtract two datetimes, or a datetime and a timedelta."""
2225
+ if not isinstance(other, any_datetime):
2226
+ if isinstance(other, any_timedelta):
2227
+ return self + -other
2228
+ return NotImplemented
2229
+
2230
+ days1 = self.toordinal()
2231
+ days2 = other.toordinal()
2232
+ secs1 = self._second + self._minute * 60 + self._hour * 3600
2233
+ secs2 = other.second + other.minute * 60 + other.hour * 3600
2234
+ base = timedelta(
2235
+ days1 - days2, secs1 - secs2, self._microsecond - other.microsecond
2236
+ )
2237
+ if self._tzinfo is other.tzinfo:
2238
+ return base
2239
+ myoff = self.utcoffset()
2240
+ otoff = other.utcoffset()
2241
+ if myoff == otoff:
2242
+ return base
2243
+ if myoff is None or otoff is None:
2244
+ raise TypeError("cannot mix naive and timezone-aware time")
2245
+ return base + otoff - myoff
2246
+
2247
+ def __hash__(self):
2248
+ if self._hashcode == -1:
2249
+ if self.fold:
2250
+ t = self.replace(fold=0)
2251
+ else:
2252
+ t = self
2253
+ tzoff = t.utcoffset()
2254
+ if tzoff is None:
2255
+ self._hashcode = hash(_datetime_getstate(t)[0])
2256
+ else:
2257
+ days = _ymd2ord(self.year, self.month, self.day)
2258
+ seconds = self.hour * 3600 + self.minute * 60 + self.second
2259
+ self._hashcode = hash(
2260
+ timedelta(days, seconds, self.microsecond) - tzoff
2261
+ )
2262
+ return self._hashcode
2263
+
2264
+ def __ch_realize__(self):
2265
+ return real_datetime(
2266
+ realize(self._year),
2267
+ realize(self._month),
2268
+ realize(self._day),
2269
+ realize(self._hour),
2270
+ realize(self._minute),
2271
+ realize(self._second),
2272
+ realize(self.microsecond),
2273
+ realize(self._tzinfo),
2274
+ fold=realize(self._fold),
2275
+ )
2276
+
2277
+ def __ch_pytype__(self):
2278
+ return real_datetime
2279
+
2280
+
2281
+ any_datetime = (datetime, real_datetime)
2282
+
2283
+ datetime.min = datetime(1, 1, 1) # type: ignore
2284
+ datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999) # type: ignore
2285
+ datetime.resolution = timedelta(microseconds=1) # type: ignore
2286
+
2287
+
2288
+ def _isoweek1monday(year):
2289
+ # Helper to calculate the day number of the Monday starting week 1
2290
+ # XXX This could be done more efficiently
2291
+ THURSDAY = 3
2292
+ firstday = _ymd2ord(year, 1, 1)
2293
+ firstweekday = (firstday + 6) % 7 # See weekday() above
2294
+ week1monday = firstday - firstweekday
2295
+ if firstweekday > THURSDAY:
2296
+ week1monday += 7
2297
+ return week1monday
2298
+
2299
+
2300
+ class timezone(tzinfo):
2301
+ class Omitted(Enum):
2302
+ value = 0
2303
+
2304
+ _Omitted = Omitted.value
2305
+
2306
+ def __new__(cls, offset: real_timedelta, name: Union[str, Omitted] = _Omitted):
2307
+ if not isinstance(offset, any_timedelta):
2308
+ raise TypeError("offset must be a timedelta")
2309
+ if name == cls._Omitted:
2310
+ if not offset:
2311
+ return cls.utc # type: ignore
2312
+ name = None # type: ignore
2313
+ elif not isinstance(name, str):
2314
+ raise TypeError("name must be a string")
2315
+ if not cls._minoffset <= offset <= cls._maxoffset:
2316
+ raise ValueError(
2317
+ "offset must be a timedelta "
2318
+ "strictly between -timedelta(hours=24) and "
2319
+ "timedelta(hours=24)."
2320
+ )
2321
+ return cls._create(offset, name)
2322
+
2323
+ @classmethod
2324
+ def _create(cls, offset, name=None):
2325
+ self = tzinfo.__new__(cls)
2326
+ self._offset = offset
2327
+ self._name = name
2328
+ return self
2329
+
2330
+ def __getinitargs__(self):
2331
+ """pickle support"""
2332
+ if self._name is None:
2333
+ return (self._offset,)
2334
+ return (self._offset, self._name)
2335
+
2336
+ def __eq__(self, other):
2337
+ if isinstance(other, any_timezone):
2338
+ return self.utcoffset(None) == other.utcoffset(None)
2339
+ return NotImplemented
2340
+
2341
+ def __hash__(self):
2342
+ return hash(self._offset)
2343
+
2344
+ def __repr__(self):
2345
+ if self is self.utc:
2346
+ return "datetime.timezone.utc"
2347
+ if self._name is None:
2348
+ return "%s.%s(%r)" % (
2349
+ type(self).__module__,
2350
+ self.__class__.__qualname__,
2351
+ self._offset,
2352
+ )
2353
+ return "%s.%s(%r, %r)" % (
2354
+ type(self).__module__,
2355
+ self.__class__.__qualname__,
2356
+ self._offset,
2357
+ self._name,
2358
+ )
2359
+
2360
+ def __str__(self):
2361
+ return self.tzname(None)
2362
+
2363
+ def utcoffset(self, dt):
2364
+ if isinstance(dt, any_datetime) or dt is None:
2365
+ return self._offset
2366
+ raise TypeError("utcoffset() argument must be a datetime instance" " or None")
2367
+
2368
+ def tzname(self, dt):
2369
+ if isinstance(dt, any_datetime) or dt is None:
2370
+ if self._name is None:
2371
+ return self._name_from_offset(self._offset)
2372
+ return self._name
2373
+ raise TypeError("tzname() argument must be a datetime instance" " or None")
2374
+
2375
+ def dst(self, dt):
2376
+ if isinstance(dt, any_datetime) or dt is None:
2377
+ return None
2378
+ raise TypeError("dst() argument must be a datetime instance" " or None")
2379
+
2380
+ def fromutc(self, dt):
2381
+ if isinstance(dt, any_datetime):
2382
+ if dt.tzinfo is not self:
2383
+ raise ValueError("fromutc: dt.tzinfo " "is not self")
2384
+ return dt + self._offset
2385
+ raise TypeError("fromutc() argument must be a datetime instance" " or None")
2386
+
2387
+ _maxoffset = (
2388
+ timedelta(hours=24, microseconds=-1)
2389
+ if sys.version_info >= (3, 8)
2390
+ else timedelta(hours=23, minutes=59)
2391
+ )
2392
+ _minoffset = -_maxoffset
2393
+
2394
+ @staticmethod
2395
+ def _name_from_offset(delta):
2396
+ if not delta:
2397
+ return "UTC"
2398
+ if delta < timedelta(0):
2399
+ sign = "-"
2400
+ delta = -delta
2401
+ else:
2402
+ sign = "+"
2403
+ hours, rest = divmod(delta, timedelta(hours=1))
2404
+ minutes, rest = divmod(rest, timedelta(minutes=1))
2405
+ seconds = rest.seconds
2406
+ microseconds = rest.microseconds
2407
+ if microseconds:
2408
+ return (
2409
+ f"UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}"
2410
+ f".{microseconds:06d}"
2411
+ )
2412
+ if seconds:
2413
+ return f"UTC{sign}{hours:02d}:{minutes:02d}:{seconds:02d}"
2414
+ return f"UTC{sign}{hours:02d}:{minutes:02d}"
2415
+
2416
+ def __ch_realize__(self):
2417
+ offset = realize(self._offset)
2418
+ name = realize(self._name)
2419
+ return real_timezone(offset) if name is None else real_timezone(offset, name)
2420
+
2421
+ def __ch_pytype__(self):
2422
+ return real_timezone
2423
+
2424
+
2425
+ any_timezone = (timezone, real_timezone)
2426
+
2427
+ timezone.utc = timezone._create(timedelta(0)) # type: ignore
2428
+ # bpo-37642: These attributes are rounded to the nearest minute for backwards
2429
+ # compatibility, even though the constructor will accept a wider range of
2430
+ # values. This may change in the future.
2431
+ timezone.min = timezone._create(-timedelta(hours=23, minutes=59)) # type: ignore
2432
+ timezone.max = timezone._create(timedelta(hours=23, minutes=59)) # type: ignore
2433
+ _EPOCH = datetime(1970, 1, 1, tzinfo=real_timezone.utc) # type: ignore
2434
+
2435
+
2436
+ def _raises_value_error(fn, args):
2437
+ try:
2438
+ fn(*args)
2439
+ return False
2440
+ except ValueError:
2441
+ return True
2442
+
2443
+
2444
+ def _timedelta_skip_construct(days, seconds, microseconds):
2445
+ # timedelta's constructor is convoluted to guide C implementations.
2446
+ # We use something simpler, and just ensure elsewhere that the inputs fall in the right ranges:
2447
+ delta = timedelta()
2448
+ delta._days = days # type: ignore
2449
+ delta._seconds = seconds # type: ignore
2450
+ delta._microseconds = microseconds # type: ignore
2451
+ return delta
2452
+
2453
+
2454
+ def _time_skip_construct(hour, minute, second, microsecond, tzinfo, fold):
2455
+ tm = time()
2456
+ tm._hour = hour
2457
+ tm._minute = minute
2458
+ tm._second = second
2459
+ tm._microsecond = microsecond
2460
+ tm._tzinfo = tzinfo
2461
+ tm._fold = fold
2462
+ return tm
2463
+
2464
+
2465
+ def _date_skip_construct(year, month, day):
2466
+ dt = date(2020, 1, 1)
2467
+ dt._year = year
2468
+ dt._month = month
2469
+ dt._day = day
2470
+ return dt
2471
+
2472
+
2473
+ def _datetime_skip_construct(
2474
+ year, month, day, hour, minute, second, microsecond, tzinfo
2475
+ ):
2476
+ dt = datetime(2020, 1, 1)
2477
+ dt._year = year
2478
+ dt._month = month
2479
+ dt._day = day
2480
+ dt._hour = hour
2481
+ dt._minute = minute
2482
+ dt._second = second
2483
+ dt._microsecond = microsecond
2484
+ dt._tzinfo = tzinfo
2485
+ return dt
2486
+
2487
+
2488
+ def _symbolic_date_fields(varname: str) -> Tuple:
2489
+ year = make_bounded_int(varname + "_year", MINYEAR, MAXYEAR)
2490
+ month = make_bounded_int(varname + "_month", 1, 12)
2491
+ day = make_bounded_int(varname + "_day", 1, 31)
2492
+ context_statespace().add(_smt_days_in_month(year.var, month.var, day.var))
2493
+ return (year, month, day)
2494
+
2495
+
2496
+ def _symbolic_time_fields(varname: str) -> Tuple:
2497
+ return (
2498
+ make_bounded_int(varname + "_hour", 0, 23),
2499
+ make_bounded_int(varname + "_min", 0, 59),
2500
+ make_bounded_int(varname + "_sec", 0, 59),
2501
+ make_bounded_int(varname + "_usec", 0, 999999),
2502
+ make_bounded_int(varname + "_fold", 0, 1),
2503
+ )
2504
+
2505
+
2506
+ def make_registrations():
2507
+
2508
+ # TODO: `timezone` never makes a tzinfo with DST, so this is incomplete.
2509
+ # A complete solution would require generating a symbolc dst() member function.
2510
+ register_type(real_tzinfo, lambda p: p(timezone))
2511
+
2512
+ def make_timezone(p: Any) -> timezone:
2513
+ if p.space.smt_fork(desc="use explicit timezone"):
2514
+ delta = p(timedelta, "_offset")
2515
+ with ResumedTracing():
2516
+ if timezone._minoffset < delta < timezone._maxoffset:
2517
+ return timezone(delta, realize(p(str, "_name")))
2518
+ else:
2519
+ raise IgnoreAttempt("Invalid timezone offset")
2520
+ else:
2521
+ return timezone.utc # type: ignore
2522
+
2523
+ register_type(real_timezone, make_timezone)
2524
+ register_patch(real_timezone, lambda *a, **kw: timezone(*a, **kw))
2525
+
2526
+ def make_date(p: Any) -> date:
2527
+ year, month, day = _symbolic_date_fields(p.varname)
2528
+ return _date_skip_construct(year, month, day)
2529
+
2530
+ register_type(real_date, make_date)
2531
+ register_patch(real_date, lambda *a, **kw: date(*a, **kw))
2532
+
2533
+ def make_time(p: Any) -> time:
2534
+ (hour, minute, sec, usec, fold) = _symbolic_time_fields(p.varname)
2535
+ tzinfo = p(Optional[timezone], "_tzinfo")
2536
+ return _time_skip_construct(hour, minute, sec, usec, tzinfo, fold)
2537
+
2538
+ register_type(real_time, make_time)
2539
+ register_patch(real_time, lambda *a, **kw: time(*a, **kw))
2540
+
2541
+ def make_datetime(p: Any) -> datetime:
2542
+ year, month, day = _symbolic_date_fields(p.varname)
2543
+ (hour, minute, sec, usec, fold) = _symbolic_time_fields(p.varname)
2544
+ tzinfo = p(Optional[timezone], "_tzinfo")
2545
+ return _datetime_skip_construct(
2546
+ year, month, day, hour, minute, sec, usec, tzinfo
2547
+ )
2548
+
2549
+ register_type(real_datetime, make_datetime)
2550
+ register_patch(real_datetime, lambda *a, **kw: datetime(*a, **kw))
2551
+
2552
+ def make_timedelta(p: SymbolicFactory) -> timedelta:
2553
+ microseconds = make_bounded_int(p.varname + "_usec", 0, 999999)
2554
+ seconds = make_bounded_int(p.varname + "_sec", 0, 3600 * 24 - 1)
2555
+ days = make_bounded_int(p.varname + "_days", -999999999, 999999999)
2556
+ return _timedelta_skip_construct(days, seconds, microseconds)
2557
+
2558
+ register_type(real_timedelta, make_timedelta)
2559
+ register_patch(real_timedelta, lambda *a, **kw: timedelta(*a, **kw))