rapydscript-ns 0.9.0 → 0.9.2
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.
- package/.agignore +1 -1
- package/.github/workflows/ci.yml +38 -38
- package/=template.pyj +5 -5
- package/CHANGELOG.md +10 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/README.md +7 -6
- package/TODO.md +116 -1
- package/add-toc-to-readme +2 -2
- package/bin/export +75 -75
- package/bin/rapydscript +70 -70
- package/bin/web-repl-export +102 -102
- package/build +2 -2
- package/language-service/index.js +9 -9
- package/language-service/language-service.d.ts +1 -1
- package/package.json +6 -2
- package/publish.py +37 -37
- package/release/compiler.js +246 -231
- package/release/signatures.json +23 -23
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/aes.pyj +646 -646
- package/src/lib/contextlib.pyj +379 -0
- package/src/lib/copy.pyj +120 -120
- package/src/lib/datetime.pyj +712 -0
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/io.pyj +500 -0
- package/src/lib/itertools.pyj +580 -580
- package/src/lib/json.pyj +227 -0
- package/src/lib/math.pyj +193 -193
- package/src/lib/operator.pyj +11 -11
- package/src/lib/pythonize.pyj +20 -20
- package/src/lib/random.pyj +118 -118
- package/src/lib/react.pyj +74 -74
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/diagnostics.js +4 -4
- package/src/monaco-language-service/dts.js +550 -550
- package/src/monaco-language-service/index.js +2 -2
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -201
- package/src/output/jsx.pyj +164 -164
- package/src/output/loops.pyj +9 -0
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +1 -1
- package/src/unicode_aliases.pyj +576 -576
- package/src/utils.pyj +192 -192
- package/test/_import_one.pyj +37 -37
- package/test/_import_two/__init__.pyj +11 -11
- package/test/_import_two/level2/deep.pyj +4 -4
- package/test/_import_two/other.pyj +6 -6
- package/test/_import_two/sub.pyj +13 -13
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/contextlib.pyj +362 -0
- package/test/datetime.pyj +500 -0
- package/test/debugger_stmt.pyj +41 -0
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/functions.pyj +151 -151
- package/test/generators.pyj +41 -41
- package/test/generic.pyj +370 -370
- package/test/imports.pyj +72 -72
- package/test/internationalization.pyj +73 -73
- package/test/io.pyj +316 -0
- package/test/json.pyj +196 -0
- package/test/lint.pyj +164 -164
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/omit_function_metadata.pyj +20 -20
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/unit/index.js +66 -0
- package/test/unit/language-service-dts.js +543 -543
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service.js +1 -1
- package/test/unit/web-repl.js +533 -0
- package/tools/compiler.d.ts +367 -0
- package/tools/completer.js +131 -131
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +128 -128
- package/tools/web_repl.js +95 -95
- package/try +41 -41
- package/web-repl/env.js +196 -196
- package/web-repl/index.html +163 -163
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +224 -224
- package/web-repl/sha1.js +25 -25
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
# vim:fileencoding=utf-8
|
|
2
|
+
# License: Apache 2.0
|
|
3
|
+
# RapydScript implementation of Python's datetime standard library.
|
|
4
|
+
#
|
|
5
|
+
# Supported classes : timedelta, date, time, datetime
|
|
6
|
+
# Constants : MINYEAR, MAXYEAR
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# from datetime import date, time, datetime, timedelta, MINYEAR, MAXYEAR
|
|
10
|
+
#
|
|
11
|
+
# Notes:
|
|
12
|
+
# - Arithmetic operators (+, -, *, etc.) only dispatch to __add__/__sub__
|
|
13
|
+
# etc. when ``from __python__ import overload_operators`` is active.
|
|
14
|
+
# Without that flag, call the dunder methods directly, e.g.
|
|
15
|
+
# ``d.__add__(timedelta(days=1))``.
|
|
16
|
+
# - Timezone-aware datetimes are not supported (no tzinfo / UTC offsets).
|
|
17
|
+
# - strftime supports: %Y %m %d %H %M %S %f %A %a %B %b %j %p %I %%
|
|
18
|
+
|
|
19
|
+
MINYEAR = 1
|
|
20
|
+
MAXYEAR = 9999
|
|
21
|
+
|
|
22
|
+
# ── Internal helpers ──────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
_DAYS_IN_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
25
|
+
# Cumulative days before month m in a non-leap year (index 0 unused)
|
|
26
|
+
_DBM = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]
|
|
27
|
+
|
|
28
|
+
def _is_leap(year):
|
|
29
|
+
return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0
|
|
30
|
+
|
|
31
|
+
def _days_in_month(year, month):
|
|
32
|
+
if month == 2 and _is_leap(year):
|
|
33
|
+
return 29
|
|
34
|
+
return _DAYS_IN_MONTH[month]
|
|
35
|
+
|
|
36
|
+
def _check_field(val, name, lo, hi):
|
|
37
|
+
if jstype(val) is not 'number' or not isFinite(val) or Math.floor(val) != val:
|
|
38
|
+
raise TypeError(name + ' must be an integer')
|
|
39
|
+
if val < lo or val > hi:
|
|
40
|
+
raise ValueError(name + ' must be in ' + str(lo) + '..' + str(hi))
|
|
41
|
+
|
|
42
|
+
def _pad2(n):
|
|
43
|
+
if n < 10:
|
|
44
|
+
return '0' + str(n)
|
|
45
|
+
return str(n)
|
|
46
|
+
|
|
47
|
+
def _pad6(n):
|
|
48
|
+
s = str(n)
|
|
49
|
+
while s.length < 6:
|
|
50
|
+
s = '0' + s
|
|
51
|
+
return s
|
|
52
|
+
|
|
53
|
+
def _days_before_year(year):
|
|
54
|
+
y = year - 1
|
|
55
|
+
return y * 365 + Math.floor(y / 4) - Math.floor(y / 100) + Math.floor(y / 400)
|
|
56
|
+
|
|
57
|
+
def _days_before_month(year, month):
|
|
58
|
+
dbm = _DBM[month]
|
|
59
|
+
if month > 2 and _is_leap(year):
|
|
60
|
+
dbm += 1
|
|
61
|
+
return dbm
|
|
62
|
+
|
|
63
|
+
def _ymd_to_ord(year, month, day):
|
|
64
|
+
return _days_before_year(year) + _days_before_month(year, month) + day
|
|
65
|
+
|
|
66
|
+
def _ord_to_ymd(n):
|
|
67
|
+
# Ordinal 719163 == 1970-01-01 (Unix epoch in proleptic Gregorian calendar)
|
|
68
|
+
d = v"new Date((n - 719163) * 86400000)"
|
|
69
|
+
return [v"d.getUTCFullYear()", v"d.getUTCMonth() + 1", v"d.getUTCDate()"]
|
|
70
|
+
|
|
71
|
+
def _yday(year, month, day):
|
|
72
|
+
return _days_before_month(year, month) + day
|
|
73
|
+
|
|
74
|
+
def _strftime(fmt, obj):
|
|
75
|
+
result = ''
|
|
76
|
+
i = 0
|
|
77
|
+
n = fmt.length
|
|
78
|
+
while i < n:
|
|
79
|
+
c = fmt.charAt(i)
|
|
80
|
+
if c is '%' and i + 1 < n:
|
|
81
|
+
directive = fmt.charAt(i + 1)
|
|
82
|
+
if directive is 'Y':
|
|
83
|
+
sy = str(obj.year)
|
|
84
|
+
while sy.length < 4:
|
|
85
|
+
sy = '0' + sy
|
|
86
|
+
result += sy
|
|
87
|
+
elif directive is 'm':
|
|
88
|
+
result += _pad2(obj.month)
|
|
89
|
+
elif directive is 'd':
|
|
90
|
+
result += _pad2(obj.day)
|
|
91
|
+
elif directive is 'H':
|
|
92
|
+
result += _pad2(obj.hour if hasattr(obj, 'hour') else 0)
|
|
93
|
+
elif directive is 'M':
|
|
94
|
+
result += _pad2(obj.minute if hasattr(obj, 'minute') else 0)
|
|
95
|
+
elif directive is 'S':
|
|
96
|
+
result += _pad2(obj.second if hasattr(obj, 'second') else 0)
|
|
97
|
+
elif directive is 'f':
|
|
98
|
+
result += _pad6(obj.microsecond if hasattr(obj, 'microsecond') else 0)
|
|
99
|
+
elif directive is 'A':
|
|
100
|
+
_dn = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
|
|
101
|
+
result += _dn[obj.weekday()]
|
|
102
|
+
elif directive is 'a':
|
|
103
|
+
_dns = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
|
|
104
|
+
result += _dns[obj.weekday()]
|
|
105
|
+
elif directive is 'B':
|
|
106
|
+
_mn = ['','January','February','March','April','May','June','July','August','September','October','November','December']
|
|
107
|
+
result += _mn[obj.month]
|
|
108
|
+
elif directive is 'b':
|
|
109
|
+
_mns = ['','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
|
|
110
|
+
result += _mns[obj.month]
|
|
111
|
+
elif directive is 'j':
|
|
112
|
+
jday = str(_yday(obj.year, obj.month, obj.day))
|
|
113
|
+
while jday.length < 3:
|
|
114
|
+
jday = '0' + jday
|
|
115
|
+
result += jday
|
|
116
|
+
elif directive is 'p':
|
|
117
|
+
result += 'AM' if (obj.hour if hasattr(obj, 'hour') else 0) < 12 else 'PM'
|
|
118
|
+
elif directive is 'I':
|
|
119
|
+
h12 = (obj.hour if hasattr(obj, 'hour') else 0) % 12
|
|
120
|
+
result += _pad2(h12 if h12 else 12)
|
|
121
|
+
elif directive is '%':
|
|
122
|
+
result += '%'
|
|
123
|
+
else:
|
|
124
|
+
result += c + directive
|
|
125
|
+
i += 2
|
|
126
|
+
else:
|
|
127
|
+
result += c
|
|
128
|
+
i += 1
|
|
129
|
+
return result
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
# ── timedelta ─────────────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
class timedelta:
|
|
135
|
+
"""Represent a duration: the difference between two date or datetime objects."""
|
|
136
|
+
|
|
137
|
+
def __init__(self, days=0, seconds=0, microseconds=0, milliseconds=0, minutes=0, hours=0, weeks=0):
|
|
138
|
+
d = days + weeks * 7
|
|
139
|
+
s = seconds + minutes * 60 + hours * 3600
|
|
140
|
+
us = microseconds + milliseconds * 1000
|
|
141
|
+
|
|
142
|
+
# Push fractional days into seconds, fractional seconds into microseconds
|
|
143
|
+
d_int = Math.trunc(d)
|
|
144
|
+
s += (d - d_int) * 86400
|
|
145
|
+
d = d_int
|
|
146
|
+
|
|
147
|
+
s_int = Math.trunc(s)
|
|
148
|
+
us += (s - s_int) * 1000000
|
|
149
|
+
s = s_int
|
|
150
|
+
|
|
151
|
+
us = Math.round(us)
|
|
152
|
+
|
|
153
|
+
# Normalise microseconds → seconds
|
|
154
|
+
s_carry = Math.floor(us / 1000000)
|
|
155
|
+
us = us - s_carry * 1000000
|
|
156
|
+
if us < 0:
|
|
157
|
+
us += 1000000
|
|
158
|
+
s_carry -= 1
|
|
159
|
+
s += s_carry
|
|
160
|
+
|
|
161
|
+
# Normalise seconds → days
|
|
162
|
+
d_carry = Math.floor(s / 86400)
|
|
163
|
+
s = s - d_carry * 86400
|
|
164
|
+
if s < 0:
|
|
165
|
+
s += 86400
|
|
166
|
+
d_carry -= 1
|
|
167
|
+
d += d_carry
|
|
168
|
+
|
|
169
|
+
self.days = d
|
|
170
|
+
self.seconds = s
|
|
171
|
+
self.microseconds = us
|
|
172
|
+
|
|
173
|
+
def total_seconds(self):
|
|
174
|
+
return self.days * 86400 + self.seconds + self.microseconds / 1000000
|
|
175
|
+
|
|
176
|
+
def __repr__(self):
|
|
177
|
+
parts = []
|
|
178
|
+
if self.days:
|
|
179
|
+
parts.push('days=' + str(self.days))
|
|
180
|
+
if self.seconds:
|
|
181
|
+
parts.push('seconds=' + str(self.seconds))
|
|
182
|
+
if self.microseconds:
|
|
183
|
+
parts.push('microseconds=' + str(self.microseconds))
|
|
184
|
+
if not parts.length:
|
|
185
|
+
return 'datetime.timedelta(0)'
|
|
186
|
+
return 'datetime.timedelta(' + parts.join(', ') + ')'
|
|
187
|
+
|
|
188
|
+
def __str__(self):
|
|
189
|
+
h = Math.floor(self.seconds / 3600)
|
|
190
|
+
rem = self.seconds % 3600
|
|
191
|
+
mins = Math.floor(rem / 60)
|
|
192
|
+
secs = rem % 60
|
|
193
|
+
time_str = str(h) + ':' + _pad2(mins) + ':' + _pad2(secs)
|
|
194
|
+
if self.microseconds:
|
|
195
|
+
time_str += '.' + _pad6(self.microseconds)
|
|
196
|
+
if self.days != 0:
|
|
197
|
+
day_word = ' day, ' if (self.days == 1 or self.days == -1) else ' days, '
|
|
198
|
+
return str(self.days) + day_word + time_str
|
|
199
|
+
return time_str
|
|
200
|
+
|
|
201
|
+
def __add__(self, other):
|
|
202
|
+
if isinstance(other, timedelta):
|
|
203
|
+
return timedelta(self.days + other.days, self.seconds + other.seconds, self.microseconds + other.microseconds)
|
|
204
|
+
raise TypeError('unsupported operand type(s) for +: timedelta and ' + jstype(other))
|
|
205
|
+
|
|
206
|
+
def __sub__(self, other):
|
|
207
|
+
if isinstance(other, timedelta):
|
|
208
|
+
return timedelta(self.days - other.days, self.seconds - other.seconds, self.microseconds - other.microseconds)
|
|
209
|
+
raise TypeError('unsupported operand type(s) for -: timedelta and ' + jstype(other))
|
|
210
|
+
|
|
211
|
+
def __neg__(self):
|
|
212
|
+
return timedelta(-self.days, -self.seconds, -self.microseconds)
|
|
213
|
+
|
|
214
|
+
def __abs__(self):
|
|
215
|
+
if self.days < 0:
|
|
216
|
+
return timedelta(-self.days, -self.seconds, -self.microseconds)
|
|
217
|
+
return self
|
|
218
|
+
|
|
219
|
+
def __mul__(self, other):
|
|
220
|
+
if jstype(other) is 'number':
|
|
221
|
+
return timedelta(0, 0, Math.round(self.total_seconds() * 1000000 * other))
|
|
222
|
+
raise TypeError('unsupported operand type(s) for *')
|
|
223
|
+
|
|
224
|
+
def __eq__(self, other):
|
|
225
|
+
if isinstance(other, timedelta):
|
|
226
|
+
return self.days is other.days and self.seconds is other.seconds and self.microseconds is other.microseconds
|
|
227
|
+
return False
|
|
228
|
+
|
|
229
|
+
def __lt__(self, other):
|
|
230
|
+
if isinstance(other, timedelta):
|
|
231
|
+
if self.days is not other.days:
|
|
232
|
+
return self.days < other.days
|
|
233
|
+
if self.seconds is not other.seconds:
|
|
234
|
+
return self.seconds < other.seconds
|
|
235
|
+
return self.microseconds < other.microseconds
|
|
236
|
+
raise TypeError('cannot compare timedelta with ' + jstype(other))
|
|
237
|
+
|
|
238
|
+
def __le__(self, other):
|
|
239
|
+
return self.__lt__(other) or self.__eq__(other)
|
|
240
|
+
|
|
241
|
+
def __gt__(self, other):
|
|
242
|
+
if isinstance(other, timedelta):
|
|
243
|
+
return other.__lt__(self)
|
|
244
|
+
raise TypeError('cannot compare timedelta with ' + jstype(other))
|
|
245
|
+
|
|
246
|
+
def __ge__(self, other):
|
|
247
|
+
return self.__gt__(other) or self.__eq__(other)
|
|
248
|
+
|
|
249
|
+
def __hash__(self):
|
|
250
|
+
return hash(self.total_seconds())
|
|
251
|
+
|
|
252
|
+
def __bool__(self):
|
|
253
|
+
return self.days is not 0 or self.seconds is not 0 or self.microseconds is not 0
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
timedelta.min = timedelta(-999999999)
|
|
257
|
+
timedelta.max = timedelta(999999999, 86399, 999999)
|
|
258
|
+
timedelta.resolution = timedelta(0, 0, 1)
|
|
259
|
+
_timedelta_cls = timedelta # capture before subclasses can shadow the name
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
# ── date ──────────────────────────────────────────────────────────────────────
|
|
263
|
+
|
|
264
|
+
class date:
|
|
265
|
+
"""A calendar date (year, month, day) with no time of day."""
|
|
266
|
+
|
|
267
|
+
def __init__(self, year, month, day):
|
|
268
|
+
_check_field(year, 'year', MINYEAR, MAXYEAR)
|
|
269
|
+
_check_field(month, 'month', 1, 12)
|
|
270
|
+
_check_field(day, 'day', 1, _days_in_month(year, month))
|
|
271
|
+
self.year = year
|
|
272
|
+
self.month = month
|
|
273
|
+
self.day = day
|
|
274
|
+
|
|
275
|
+
@classmethod
|
|
276
|
+
def today(cls):
|
|
277
|
+
d = v"new Date()"
|
|
278
|
+
return cls(v"d.getFullYear()", v"d.getMonth() + 1", v"d.getDate()")
|
|
279
|
+
|
|
280
|
+
@classmethod
|
|
281
|
+
def fromordinal(cls, n):
|
|
282
|
+
ymd = _ord_to_ymd(n)
|
|
283
|
+
return cls(ymd[0], ymd[1], ymd[2])
|
|
284
|
+
|
|
285
|
+
@classmethod
|
|
286
|
+
def fromtimestamp(cls, timestamp):
|
|
287
|
+
d = v"new Date(timestamp * 1000)"
|
|
288
|
+
return cls(v"d.getFullYear()", v"d.getMonth() + 1", v"d.getDate()")
|
|
289
|
+
|
|
290
|
+
@classmethod
|
|
291
|
+
def fromisoformat(cls, s):
|
|
292
|
+
parts = s.split('-')
|
|
293
|
+
if parts.length != 3:
|
|
294
|
+
raise ValueError('Invalid ISO date: ' + s)
|
|
295
|
+
return cls(int(parts[0]), int(parts[1]), int(parts[2]))
|
|
296
|
+
|
|
297
|
+
def toordinal(self):
|
|
298
|
+
return _ymd_to_ord(self.year, self.month, self.day)
|
|
299
|
+
|
|
300
|
+
def weekday(self):
|
|
301
|
+
# Monday=0, Sunday=6
|
|
302
|
+
return (self.toordinal() + 6) % 7
|
|
303
|
+
|
|
304
|
+
def isoweekday(self):
|
|
305
|
+
# Monday=1, Sunday=7
|
|
306
|
+
return self.toordinal() % 7 or 7
|
|
307
|
+
|
|
308
|
+
def isocalendar(self):
|
|
309
|
+
# Returns [iso_year, iso_week, iso_weekday]
|
|
310
|
+
thu = self.toordinal() - self.weekday() + 3
|
|
311
|
+
ymd = _ord_to_ymd(thu)
|
|
312
|
+
iso_year = ymd[0]
|
|
313
|
+
week1_thu = _ymd_to_ord(iso_year, 1, 4)
|
|
314
|
+
iso_week = Math.floor((thu - (week1_thu - (week1_thu + 6) % 7)) / 7) + 1
|
|
315
|
+
return [iso_year, iso_week, self.isoweekday()]
|
|
316
|
+
|
|
317
|
+
def isoformat(self):
|
|
318
|
+
sy = str(self.year)
|
|
319
|
+
while sy.length < 4:
|
|
320
|
+
sy = '0' + sy
|
|
321
|
+
return sy + '-' + _pad2(self.month) + '-' + _pad2(self.day)
|
|
322
|
+
|
|
323
|
+
def ctime(self):
|
|
324
|
+
day_names = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
|
|
325
|
+
month_names = ['','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
|
|
326
|
+
sy = str(self.year)
|
|
327
|
+
while sy.length < 4:
|
|
328
|
+
sy = '0' + sy
|
|
329
|
+
return day_names[self.weekday()] + ' ' + month_names[self.month] + ' ' + _pad2(self.day) + ' 00:00:00 ' + sy
|
|
330
|
+
|
|
331
|
+
def strftime(self, fmt):
|
|
332
|
+
return _strftime(fmt, self)
|
|
333
|
+
|
|
334
|
+
def timetuple(self):
|
|
335
|
+
yday = _days_before_month(self.year, self.month) + self.day
|
|
336
|
+
return [self.year, self.month, self.day, 0, 0, 0, self.weekday(), yday, -1]
|
|
337
|
+
|
|
338
|
+
def replace(self, year=None, month=None, day=None):
|
|
339
|
+
y = year if year is not None else self.year
|
|
340
|
+
m = month if month is not None else self.month
|
|
341
|
+
d = day if day is not None else self.day
|
|
342
|
+
return date(y, m, d)
|
|
343
|
+
|
|
344
|
+
def __add__(self, other):
|
|
345
|
+
if isinstance(other, timedelta):
|
|
346
|
+
return date.fromordinal(self.toordinal() + other.days)
|
|
347
|
+
raise TypeError('unsupported operand type(s) for +: date and ' + jstype(other))
|
|
348
|
+
|
|
349
|
+
def __radd__(self, other):
|
|
350
|
+
return self.__add__(other)
|
|
351
|
+
|
|
352
|
+
def __sub__(self, other):
|
|
353
|
+
if isinstance(other, timedelta):
|
|
354
|
+
return date.fromordinal(self.toordinal() - other.days)
|
|
355
|
+
if isinstance(other, date) and not isinstance(other, datetime):
|
|
356
|
+
return timedelta(self.toordinal() - other.toordinal())
|
|
357
|
+
raise TypeError('unsupported operand type(s) for -')
|
|
358
|
+
|
|
359
|
+
def __str__(self):
|
|
360
|
+
return self.isoformat()
|
|
361
|
+
|
|
362
|
+
def __repr__(self):
|
|
363
|
+
return 'datetime.date(' + str(self.year) + ', ' + str(self.month) + ', ' + str(self.day) + ')'
|
|
364
|
+
|
|
365
|
+
def __eq__(self, other):
|
|
366
|
+
if isinstance(other, date) and not isinstance(other, datetime):
|
|
367
|
+
return self.year is other.year and self.month is other.month and self.day is other.day
|
|
368
|
+
return False
|
|
369
|
+
|
|
370
|
+
def __lt__(self, other):
|
|
371
|
+
if isinstance(other, date) and not isinstance(other, datetime):
|
|
372
|
+
if self.year is not other.year:
|
|
373
|
+
return self.year < other.year
|
|
374
|
+
if self.month is not other.month:
|
|
375
|
+
return self.month < other.month
|
|
376
|
+
return self.day < other.day
|
|
377
|
+
raise TypeError('cannot compare date with ' + jstype(other))
|
|
378
|
+
|
|
379
|
+
def __le__(self, other):
|
|
380
|
+
return self.__lt__(other) or self.__eq__(other)
|
|
381
|
+
|
|
382
|
+
def __gt__(self, other):
|
|
383
|
+
if isinstance(other, date) and not isinstance(other, datetime):
|
|
384
|
+
return other.__lt__(self)
|
|
385
|
+
raise TypeError('cannot compare date with ' + jstype(other))
|
|
386
|
+
|
|
387
|
+
def __ge__(self, other):
|
|
388
|
+
return self.__gt__(other) or self.__eq__(other)
|
|
389
|
+
|
|
390
|
+
def __hash__(self):
|
|
391
|
+
return hash(self.toordinal())
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
date.min = date(MINYEAR, 1, 1)
|
|
395
|
+
date.max = date(MAXYEAR, 12, 31)
|
|
396
|
+
date.resolution = timedelta(1)
|
|
397
|
+
_date_cls = date # capture before datetime can shadow the name
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
# ── time ──────────────────────────────────────────────────────────────────────
|
|
401
|
+
|
|
402
|
+
class time:
|
|
403
|
+
"""A time of day (hour, minute, second, microsecond), independent of date."""
|
|
404
|
+
|
|
405
|
+
def __init__(self, hour=0, minute=0, second=0, microsecond=0):
|
|
406
|
+
_check_field(hour, 'hour', 0, 23)
|
|
407
|
+
_check_field(minute, 'minute', 0, 59)
|
|
408
|
+
_check_field(second, 'second', 0, 59)
|
|
409
|
+
_check_field(microsecond, 'microsecond', 0, 999999)
|
|
410
|
+
self.hour = hour
|
|
411
|
+
self.minute = minute
|
|
412
|
+
self.second = second
|
|
413
|
+
self.microsecond = microsecond
|
|
414
|
+
|
|
415
|
+
def replace(self, hour=None, minute=None, second=None, microsecond=None):
|
|
416
|
+
h = hour if hour is not None else self.hour
|
|
417
|
+
m = minute if minute is not None else self.minute
|
|
418
|
+
s = second if second is not None else self.second
|
|
419
|
+
us = microsecond if microsecond is not None else self.microsecond
|
|
420
|
+
return time(h, m, s, us)
|
|
421
|
+
|
|
422
|
+
def isoformat(self):
|
|
423
|
+
s = _pad2(self.hour) + ':' + _pad2(self.minute) + ':' + _pad2(self.second)
|
|
424
|
+
if self.microsecond:
|
|
425
|
+
s += '.' + _pad6(self.microsecond)
|
|
426
|
+
return s
|
|
427
|
+
|
|
428
|
+
def strftime(self, fmt):
|
|
429
|
+
return _strftime(fmt, self)
|
|
430
|
+
|
|
431
|
+
def __str__(self):
|
|
432
|
+
return self.isoformat()
|
|
433
|
+
|
|
434
|
+
def __repr__(self):
|
|
435
|
+
if self.microsecond:
|
|
436
|
+
return 'datetime.time(' + str(self.hour) + ', ' + str(self.minute) + ', ' + str(self.second) + ', ' + str(self.microsecond) + ')'
|
|
437
|
+
if self.second:
|
|
438
|
+
return 'datetime.time(' + str(self.hour) + ', ' + str(self.minute) + ', ' + str(self.second) + ')'
|
|
439
|
+
if self.minute:
|
|
440
|
+
return 'datetime.time(' + str(self.hour) + ', ' + str(self.minute) + ')'
|
|
441
|
+
return 'datetime.time(' + str(self.hour) + ')'
|
|
442
|
+
|
|
443
|
+
def __eq__(self, other):
|
|
444
|
+
if isinstance(other, time):
|
|
445
|
+
return self.hour is other.hour and self.minute is other.minute and self.second is other.second and self.microsecond is other.microsecond
|
|
446
|
+
return False
|
|
447
|
+
|
|
448
|
+
def __lt__(self, other):
|
|
449
|
+
if isinstance(other, time):
|
|
450
|
+
if self.hour is not other.hour:
|
|
451
|
+
return self.hour < other.hour
|
|
452
|
+
if self.minute is not other.minute:
|
|
453
|
+
return self.minute < other.minute
|
|
454
|
+
if self.second is not other.second:
|
|
455
|
+
return self.second < other.second
|
|
456
|
+
return self.microsecond < other.microsecond
|
|
457
|
+
raise TypeError('cannot compare time with ' + jstype(other))
|
|
458
|
+
|
|
459
|
+
def __le__(self, other):
|
|
460
|
+
return self.__lt__(other) or self.__eq__(other)
|
|
461
|
+
|
|
462
|
+
def __gt__(self, other):
|
|
463
|
+
if isinstance(other, time):
|
|
464
|
+
return other.__lt__(self)
|
|
465
|
+
raise TypeError('cannot compare time with ' + jstype(other))
|
|
466
|
+
|
|
467
|
+
def __ge__(self, other):
|
|
468
|
+
return self.__gt__(other) or self.__eq__(other)
|
|
469
|
+
|
|
470
|
+
def __hash__(self):
|
|
471
|
+
return hash(self.hour * 3600 + self.minute * 60 + self.second + self.microsecond / 1000000)
|
|
472
|
+
|
|
473
|
+
def __bool__(self):
|
|
474
|
+
return self.hour is not 0 or self.minute is not 0 or self.second is not 0 or self.microsecond is not 0
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
time.min = time(0, 0, 0, 0)
|
|
478
|
+
time.max = time(23, 59, 59, 999999)
|
|
479
|
+
time.resolution = timedelta(0, 0, 1)
|
|
480
|
+
_time_cls = time # capture before datetime can shadow the name
|
|
481
|
+
|
|
482
|
+
|
|
483
|
+
# ── datetime ──────────────────────────────────────────────────────────────────
|
|
484
|
+
|
|
485
|
+
class datetime(date):
|
|
486
|
+
"""A date combined with a time of day."""
|
|
487
|
+
|
|
488
|
+
def __init__(self, year, month, day, hour=0, minute=0, second=0, microsecond=0):
|
|
489
|
+
date.__init__(self, year, month, day)
|
|
490
|
+
_check_field(hour, 'hour', 0, 23)
|
|
491
|
+
_check_field(minute, 'minute', 0, 59)
|
|
492
|
+
_check_field(second, 'second', 0, 59)
|
|
493
|
+
_check_field(microsecond, 'microsecond', 0, 999999)
|
|
494
|
+
self.hour = hour
|
|
495
|
+
self.minute = minute
|
|
496
|
+
self.second = second
|
|
497
|
+
self.microsecond = microsecond
|
|
498
|
+
|
|
499
|
+
@classmethod
|
|
500
|
+
def now(cls, tz=None):
|
|
501
|
+
d = v"new Date()"
|
|
502
|
+
return cls(v"d.getFullYear()", v"d.getMonth()+1", v"d.getDate()",
|
|
503
|
+
v"d.getHours()", v"d.getMinutes()", v"d.getSeconds()",
|
|
504
|
+
v"d.getMilliseconds() * 1000")
|
|
505
|
+
|
|
506
|
+
@classmethod
|
|
507
|
+
def utcnow(cls):
|
|
508
|
+
d = v"new Date()"
|
|
509
|
+
return cls(v"d.getUTCFullYear()", v"d.getUTCMonth()+1", v"d.getUTCDate()",
|
|
510
|
+
v"d.getUTCHours()", v"d.getUTCMinutes()", v"d.getUTCSeconds()",
|
|
511
|
+
v"d.getUTCMilliseconds() * 1000")
|
|
512
|
+
|
|
513
|
+
@classmethod
|
|
514
|
+
def fromtimestamp(cls, timestamp, tz=None):
|
|
515
|
+
d = v"new Date(timestamp * 1000)"
|
|
516
|
+
return cls(v"d.getFullYear()", v"d.getMonth()+1", v"d.getDate()",
|
|
517
|
+
v"d.getHours()", v"d.getMinutes()", v"d.getSeconds()",
|
|
518
|
+
v"d.getMilliseconds() * 1000")
|
|
519
|
+
|
|
520
|
+
@classmethod
|
|
521
|
+
def utcfromtimestamp(cls, timestamp):
|
|
522
|
+
d = v"new Date(timestamp * 1000)"
|
|
523
|
+
return cls(v"d.getUTCFullYear()", v"d.getUTCMonth()+1", v"d.getUTCDate()",
|
|
524
|
+
v"d.getUTCHours()", v"d.getUTCMinutes()", v"d.getUTCSeconds()",
|
|
525
|
+
v"d.getUTCMilliseconds() * 1000")
|
|
526
|
+
|
|
527
|
+
@classmethod
|
|
528
|
+
def combine(cls, d, t):
|
|
529
|
+
return cls(d.year, d.month, d.day, t.hour, t.minute, t.second, t.microsecond)
|
|
530
|
+
|
|
531
|
+
@classmethod
|
|
532
|
+
def fromisoformat(cls, s):
|
|
533
|
+
sep = s.indexOf('T')
|
|
534
|
+
if sep is -1:
|
|
535
|
+
sep = s.indexOf(' ')
|
|
536
|
+
if sep is -1:
|
|
537
|
+
# date only — produce a datetime at midnight
|
|
538
|
+
parts = s.split('-')
|
|
539
|
+
return cls(int(parts[0]), int(parts[1]), int(parts[2]))
|
|
540
|
+
ds = s.slice(0, sep)
|
|
541
|
+
ts = s.slice(sep + 1)
|
|
542
|
+
dp = ds.split('-')
|
|
543
|
+
us = 0
|
|
544
|
+
dot = ts.indexOf('.')
|
|
545
|
+
if dot is not -1:
|
|
546
|
+
frac = ts.slice(dot + 1)
|
|
547
|
+
while frac.length < 6:
|
|
548
|
+
frac += '0'
|
|
549
|
+
us = int(frac.slice(0, 6))
|
|
550
|
+
ts = ts.slice(0, dot)
|
|
551
|
+
tp = ts.split(':')
|
|
552
|
+
h = int(tp[0]) if tp.length > 0 else 0
|
|
553
|
+
mi = int(tp[1]) if tp.length > 1 else 0
|
|
554
|
+
sc = int(tp[2]) if tp.length > 2 else 0
|
|
555
|
+
return cls(int(dp[0]), int(dp[1]), int(dp[2]), h, mi, sc, us)
|
|
556
|
+
|
|
557
|
+
def date(self):
|
|
558
|
+
# Use _date_cls alias: inside `function date() {...}` the name 'date'
|
|
559
|
+
# would otherwise bind to the function itself (named-function-expression
|
|
560
|
+
# scoping), causing infinite recursion.
|
|
561
|
+
return _date_cls(self.year, self.month, self.day)
|
|
562
|
+
|
|
563
|
+
def time(self):
|
|
564
|
+
# Same rationale as date() above — use _time_cls alias.
|
|
565
|
+
return _time_cls(self.hour, self.minute, self.second, self.microsecond)
|
|
566
|
+
|
|
567
|
+
def timestamp(self):
|
|
568
|
+
v"""var _ts_d = new Date(this.year, this.month - 1, this.day,
|
|
569
|
+
this.hour, this.minute, this.second,
|
|
570
|
+
Math.floor(this.microsecond / 1000));"""
|
|
571
|
+
return v"_ts_d.getTime() / 1000"
|
|
572
|
+
|
|
573
|
+
def replace(self, year=None, month=None, day=None, hour=None, minute=None, second=None, microsecond=None):
|
|
574
|
+
y = year if year is not None else self.year
|
|
575
|
+
mo = month if month is not None else self.month
|
|
576
|
+
d = day if day is not None else self.day
|
|
577
|
+
h = hour if hour is not None else self.hour
|
|
578
|
+
mi = minute if minute is not None else self.minute
|
|
579
|
+
s = second if second is not None else self.second
|
|
580
|
+
us = microsecond if microsecond is not None else self.microsecond
|
|
581
|
+
return datetime(y, mo, d, h, mi, s, us)
|
|
582
|
+
|
|
583
|
+
def isoformat(self, sep='T'):
|
|
584
|
+
sy = str(self.year)
|
|
585
|
+
while sy.length < 4:
|
|
586
|
+
sy = '0' + sy
|
|
587
|
+
date_str = sy + '-' + _pad2(self.month) + '-' + _pad2(self.day)
|
|
588
|
+
time_str = _pad2(self.hour) + ':' + _pad2(self.minute) + ':' + _pad2(self.second)
|
|
589
|
+
if self.microsecond:
|
|
590
|
+
time_str += '.' + _pad6(self.microsecond)
|
|
591
|
+
return date_str + sep + time_str
|
|
592
|
+
|
|
593
|
+
def ctime(self):
|
|
594
|
+
day_names = ['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
|
|
595
|
+
month_names = ['','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
|
|
596
|
+
sy = str(self.year)
|
|
597
|
+
while sy.length < 4:
|
|
598
|
+
sy = '0' + sy
|
|
599
|
+
return day_names[self.weekday()] + ' ' + month_names[self.month] + ' ' + _pad2(self.day) + ' ' + _pad2(self.hour) + ':' + _pad2(self.minute) + ':' + _pad2(self.second) + ' ' + sy
|
|
600
|
+
|
|
601
|
+
def strftime(self, fmt):
|
|
602
|
+
return _strftime(fmt, self)
|
|
603
|
+
|
|
604
|
+
def timetuple(self):
|
|
605
|
+
yday = _days_before_month(self.year, self.month) + self.day
|
|
606
|
+
return [self.year, self.month, self.day, self.hour, self.minute, self.second, self.weekday(), yday, -1]
|
|
607
|
+
|
|
608
|
+
def __str__(self):
|
|
609
|
+
return self.isoformat(' ')
|
|
610
|
+
|
|
611
|
+
def __repr__(self):
|
|
612
|
+
s = 'datetime.datetime(' + str(self.year) + ', ' + str(self.month) + ', ' + str(self.day) + ', ' + str(self.hour) + ', ' + str(self.minute) + ', ' + str(self.second)
|
|
613
|
+
if self.microsecond:
|
|
614
|
+
s += ', ' + str(self.microsecond)
|
|
615
|
+
return s + ')'
|
|
616
|
+
|
|
617
|
+
def __add__(self, other):
|
|
618
|
+
if isinstance(other, timedelta):
|
|
619
|
+
# Operate on day-ordinal + time-of-day components separately to
|
|
620
|
+
# avoid floating-point precision issues with large microsecond counts.
|
|
621
|
+
us = self.microsecond + other.microseconds
|
|
622
|
+
s = self.second + other.seconds
|
|
623
|
+
mi = self.minute
|
|
624
|
+
h = self.hour
|
|
625
|
+
ord_day = self.toordinal() + other.days
|
|
626
|
+
|
|
627
|
+
# Cascade carries
|
|
628
|
+
s_carry = Math.floor(us / 1000000)
|
|
629
|
+
us = us - s_carry * 1000000
|
|
630
|
+
if us < 0:
|
|
631
|
+
us += 1000000
|
|
632
|
+
s_carry -= 1
|
|
633
|
+
s += s_carry
|
|
634
|
+
|
|
635
|
+
mi_carry = Math.floor(s / 60)
|
|
636
|
+
s = s % 60
|
|
637
|
+
if s < 0:
|
|
638
|
+
s += 60
|
|
639
|
+
mi_carry -= 1
|
|
640
|
+
mi += mi_carry
|
|
641
|
+
|
|
642
|
+
h_carry = Math.floor(mi / 60)
|
|
643
|
+
mi = mi % 60
|
|
644
|
+
if mi < 0:
|
|
645
|
+
mi += 60
|
|
646
|
+
h_carry -= 1
|
|
647
|
+
h += h_carry
|
|
648
|
+
|
|
649
|
+
d_carry = Math.floor(h / 24)
|
|
650
|
+
h = h % 24
|
|
651
|
+
if h < 0:
|
|
652
|
+
h += 24
|
|
653
|
+
d_carry -= 1
|
|
654
|
+
ord_day += d_carry
|
|
655
|
+
|
|
656
|
+
ymd = _ord_to_ymd(ord_day)
|
|
657
|
+
return datetime(ymd[0], ymd[1], ymd[2], h, mi, s, us)
|
|
658
|
+
raise TypeError('unsupported operand type(s) for +: datetime and ' + jstype(other))
|
|
659
|
+
|
|
660
|
+
def __radd__(self, other):
|
|
661
|
+
return self.__add__(other)
|
|
662
|
+
|
|
663
|
+
def __sub__(self, other):
|
|
664
|
+
if isinstance(other, datetime):
|
|
665
|
+
delta_days = self.toordinal() - other.toordinal()
|
|
666
|
+
delta_secs = (self.hour - other.hour) * 3600 + (self.minute - other.minute) * 60 + (self.second - other.second)
|
|
667
|
+
delta_us = self.microsecond - other.microsecond
|
|
668
|
+
return timedelta(delta_days, delta_secs, delta_us)
|
|
669
|
+
if isinstance(other, timedelta):
|
|
670
|
+
return self.__add__(timedelta(-other.days, -other.seconds, -other.microseconds))
|
|
671
|
+
raise TypeError('unsupported operand type(s) for -')
|
|
672
|
+
|
|
673
|
+
def __eq__(self, other):
|
|
674
|
+
if isinstance(other, datetime):
|
|
675
|
+
return self.year is other.year and self.month is other.month and self.day is other.day and self.hour is other.hour and self.minute is other.minute and self.second is other.second and self.microsecond is other.microsecond
|
|
676
|
+
return False
|
|
677
|
+
|
|
678
|
+
def __lt__(self, other):
|
|
679
|
+
if isinstance(other, datetime):
|
|
680
|
+
if self.year is not other.year:
|
|
681
|
+
return self.year < other.year
|
|
682
|
+
if self.month is not other.month:
|
|
683
|
+
return self.month < other.month
|
|
684
|
+
if self.day is not other.day:
|
|
685
|
+
return self.day < other.day
|
|
686
|
+
if self.hour is not other.hour:
|
|
687
|
+
return self.hour < other.hour
|
|
688
|
+
if self.minute is not other.minute:
|
|
689
|
+
return self.minute < other.minute
|
|
690
|
+
if self.second is not other.second:
|
|
691
|
+
return self.second < other.second
|
|
692
|
+
return self.microsecond < other.microsecond
|
|
693
|
+
raise TypeError('cannot compare datetime with ' + jstype(other))
|
|
694
|
+
|
|
695
|
+
def __le__(self, other):
|
|
696
|
+
return self.__lt__(other) or self.__eq__(other)
|
|
697
|
+
|
|
698
|
+
def __gt__(self, other):
|
|
699
|
+
if isinstance(other, datetime):
|
|
700
|
+
return other.__lt__(self)
|
|
701
|
+
raise TypeError('cannot compare datetime with ' + jstype(other))
|
|
702
|
+
|
|
703
|
+
def __ge__(self, other):
|
|
704
|
+
return self.__gt__(other) or self.__eq__(other)
|
|
705
|
+
|
|
706
|
+
def __hash__(self):
|
|
707
|
+
return hash(self.isoformat())
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
datetime.min = datetime(MINYEAR, 1, 1, 0, 0, 0)
|
|
711
|
+
datetime.max = datetime(MAXYEAR, 12, 31, 23, 59, 59, 999999)
|
|
712
|
+
datetime.resolution = timedelta(0, 0, 1)
|