Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/python-wasm
Path: blob/main/python/pylang/src/lib/time.py
1398 views
1
"""
2
Time module.
3
4
This is a work in progress port that I started from the time module in **Transcrypt**.
5
6
No:
7
8
- Platform specific functions
9
- sleep. In js currently not possible in browsers
10
except via busy loops, we don't do that.
11
- struct_time CLASS. we work only via the tuple interface of it.
12
- handling of weird stuff.
13
e.g.: In Europe/Simferopool (Ukraine) the UTC offset before 1924 was +2.67
14
15
Spec for all below (must have open to read this module):
16
17
> https://docs.python.org/3.5/library/time.html
18
19
20
Jul 2016, Gunther Klessinger, Axiros GmbH
21
"""
22
23
try:
24
__language = window.navigator.language
25
except:
26
__language = 'en-US'
27
28
def __debugGetLanguage ():
29
return __language
30
31
def __adapt__ (request): # Needed when running on top of Node.js rather than in browser
32
global __language
33
__language = request.headers ['accept-language'] .split (',')[0]
34
35
# js date object. might be modified during calculations:
36
__date = new Date(0)
37
__now = new Date()
38
39
40
# build the locale's weekday names
41
__weekdays = []
42
__weekdays_long = []
43
__d = new Date(1467662339080) # a monday
44
for i in range(7):
45
for l, s in [(__weekdays, 'short'), (__weekdays_long, 'long')]:
46
l.append(__d.toLocaleString(__language,
47
{'weekday': s}).toLowerCase())
48
__d.setDate(__d.getDate() + 1)
49
50
51
# build the locale's months names
52
__months = []
53
__months_long = []
54
__d = new Date(946681200000.0) # 1.1.2000
55
for i in range(12):
56
for l, s in [(__months, 'short'), (__months_long, 'long')]:
57
l.append(__d.toLocaleString(__language,
58
{'month': s}).toLowerCase())
59
__d.setMonth(__d.getMonth() + 1)
60
61
62
63
# lookup for positions directives in struct_time tuples:
64
# its a 9-sequence
65
# time.struct_time(tm_year=2016, tm_mon=7, tm_mday=19, tm_hour=2,
66
# tm_min=24, tm_sec=2, tm_wday=1, tm_yday=201,
67
# tm_isdst=1)
68
__lu = {'Y': 0, 'm': 1, 'd': 2, 'H': 3, 'M': 4, 'S': 5}
69
70
def _lsplit(s, sep, maxsplit):
71
""" not yet in TS """
72
if maxsplit == 0:
73
return [s]
74
split = s.split(sep)
75
if not maxsplit:
76
return split
77
ret = split.slice(0, maxsplit, 1)
78
if len(ret) == len(split):
79
return ret
80
ret.append(sep.join(split[maxsplit:]))
81
return ret
82
83
84
def _local_time_tuple(jd):
85
""" jd: javascript Date object, from unixtimestamp """
86
res = ( jd.getFullYear()
87
,jd.getMonth() + 1 # zero based
88
,jd.getDate()
89
,jd.getHours()
90
,jd.getMinutes()
91
,jd.getSeconds()
92
,jd.getDay() - 1 if jd.getDay() > 0 else 6
93
,_day_of_year(jd, True)
94
,_daylight_in_effect(jd)
95
,jd.getMilliseconds() # not in use by the pub API
96
)
97
return res
98
99
def _utc_time_tuple(jd):
100
""" jd: javascript Date object, from unixtimestamp """
101
res = ( jd.getUTCFullYear()
102
,jd.getUTCMonth() + 1 # zero based
103
,jd.getUTCDate()
104
,jd.getUTCHours()
105
,jd.getUTCMinutes()
106
,jd.getUTCSeconds()
107
,jd.getUTCDay() - 1
108
,_day_of_year(jd, False)
109
,0 # is dst for utc: 0
110
,jd.getUTCMilliseconds()
111
)
112
return res
113
114
def _day_of_year(jd, local):
115
# check if jd hours are ahead of UTC less than the offset to it:
116
day_offs = 0
117
if jd.getHours() + jd.getTimezoneOffset() * 60 / 3600 < 0:
118
day_offs = -1
119
was = jd.getTime()
120
cur = jd.setHours(23)
121
jd.setUTCDate(1)
122
jd.setUTCMonth(0)
123
jd.setUTCHours(0)
124
jd.setUTCMinutes(0)
125
jd.setUTCSeconds(0)
126
res = Math.round((cur - jd) / 86400000 )
127
#res = round(((jd.setHours(23) - new Date(jd.getYear(), 0, 1, 0, 0, 0)
128
# ) / 1000 / 60 / 60 / 24))
129
if not local:
130
res += day_offs
131
132
if res == 0:
133
res = 365
134
jd.setTime(jd.getTime() - 86400)
135
last_year = jd.getUTCFullYear()
136
if _is_leap(last_year):
137
res = 366
138
jd.setTime(was)
139
return res
140
141
def _is_leap(year):
142
return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
143
144
def __jan_jun_tz(t, func):
145
""" information about local jan and jun month of a t's year
146
default is to deliver timezone offset, but a function can be handed to us,
147
which we'll run on those two months
148
"""
149
# required to detect dst (daylight saving time) in effect:
150
was = t.getTime() # avoid new date objs
151
t.setDate(1)
152
res = []
153
for m in 0, 6:
154
t.setMonth(m)
155
if not func:
156
res.append(t.getTimezoneOffset())
157
else:
158
res.append(func(t))
159
t.setTime(was)
160
return res
161
162
def _daylight(t):
163
"""
164
http://stackoverflow.com/questions/11887934/
165
check-if-daylight-saving-time-is-in-effect-and-if-it-is-for-how-many-hours
166
167
return 0 or 1 like python
168
CAUTION: https://docs.python.org/2/library/time.html#time.daylight:
169
"Nonzero if a DST timezone is DEFINED." (but not necessarily in effect!!)
170
-> we just check if there is a delta of tz offsets in june an jan of the
171
year of t:
172
"""
173
jj = __jan_jun_tz(t)
174
if jj[0] != jj[1]:
175
# daylight saving is DEFINED, since there's a difference in tz offsets
176
# in jan and jun, in the year of t:
177
return 1
178
return 0
179
180
def _daylight_in_effect(t):
181
jj = __jan_jun_tz(t)
182
if min(jj[0], jj[1]) == t.getTimezoneOffset():
183
return 1
184
return 0
185
186
def _timezone(t):
187
jj = __jan_jun_tz(t)
188
# in southern hemisphere the daylight saving is in winter months!
189
return max(jj[0], jj[1])
190
191
192
def __tzn(t):
193
# depending on browser ? new Date() -> Wed Jul... (CEST)
194
try:
195
return str(t).split('(')[1].split(')')[0]
196
except:
197
# better no crash:
198
return 'n.a.'
199
200
def _tzname(t):
201
'''the first is the name of the local non-DST timezone,
202
the second is the name of the local DST timezone.'''
203
cn = __tzn(t)
204
ret = [cn, cn]
205
jj = __jan_jun_tz(t, __tzn)
206
ind = 0
207
if not _daylight_in_effect(t):
208
ind = 1
209
for i in jj:
210
if i != cn:
211
ret[ind] = i
212
return ret
213
214
215
216
217
# ------------------------------------------------------------------ Public API
218
219
# we calc those only once. I mean - we run in the browser in the end.
220
221
altzone = __now.getTimezoneOffset()
222
if not _daylight_in_effect(__now):
223
# then we must use the other offset we have in the current year:
224
_jj = __jan_jun_tz(__now)
225
altzone = _jj[0] if altzone == _jj[1] else _jj[1]
226
altzone = altzone * 60
227
228
timezone = _timezone(__now) * 60
229
230
daylight = _daylight(__now)
231
232
tzname = _tzname(__now)
233
234
235
def time():
236
"""
237
time() -> floating point number\n\nReturn the current time in seconds
238
since the Epoch.
239
Fractions of a second may be present if the system clock provides them.
240
"""
241
return Date.now() / 1000
242
243
244
def asctime(t):
245
return strftime('%a %b %d %H:%M:%S %Y', t)
246
247
248
def mktime(t):
249
''' inverse of localtime '''
250
d = new Date(t[0], t[1] - 1, t[2], t[3], t[4], t[5], 0)
251
return (d - 0) / 1000
252
253
254
def ctime(seconds):
255
"""
256
ctime(seconds) -> string
257
258
Convert a time in seconds since the Epoch to a string in local time.
259
This is equivalent to asctime(localtime(seconds)). When the time tuple is
260
not present, current time as returned by localtime() is used.'
261
"""
262
if not seconds:
263
seconds = time()
264
return asctime(localtime(seconds))
265
266
267
def localtime(seconds):
268
"""
269
localtime([seconds]) -> (tm_year,tm_mon,tm_mday,tm_hour,tm_min,
270
tm_sec,tm_wday,tm_yday,tm_isdst)
271
272
Convert seconds since the Epoch to a time tuple expressing local time.
273
When 'seconds' is not passed in, convert the current time instead.
274
"""
275
if not seconds:
276
seconds = time()
277
return gmtime(seconds, True)
278
279
280
def gmtime(seconds, localtime):
281
"""
282
localtime([seconds]) -> (tm_year,tm_mon,tm_mday,tm_hour,tm_min,
283
tm_sec,tm_wday,tm_yday,tm_isdst)
284
285
Convert seconds since the Epoch to a time tuple expressing local time.
286
When 'seconds' is not passed in, convert the current time instead.
287
"""
288
if not seconds:
289
seconds = time()
290
millis = seconds * 1000
291
__date.setTime(millis)
292
if localtime:
293
t = _local_time_tuple(__date)
294
else:
295
t = _utc_time_tuple(__date)
296
return t[:9]
297
298
# ----------------------------------------------------------------------------
299
# now the workhorses:
300
def strptime(string, format):
301
"""
302
strptime(string, format) -> struct_time
303
304
Parse a string to a time tuple according to a format specification.
305
See the library reference manual for formatting codes (same as
306
strftime()).
307
308
Commonly used format codes:
309
310
%Y Year with century as a decimal number.
311
%m Month as a decimal number [01,12].
312
%d Day of the month as a decimal number [01,31].
313
%H Hour (24-hour clock) as a decimal number [00,23].
314
%M Minute as a decimal number [00,59].
315
%S Second as a decimal number [00,61].
316
%z Time zone offset from UTC.
317
%a Locale's abbreviated weekday name.
318
%A Locale's full weekday name.
319
%b Locale's abbreviated month name.
320
%B Locale's full month name.
321
%c Locale's appropriate date and time representation.
322
%I Hour (12-hour clock) as a decimal number [01,12].
323
%p Locale's equivalent of either AM or PM.
324
325
Tradoffs of this Transcrypt implementation:
326
327
1. platform specific codes not supported
328
2. %% and %c not supported
329
"""
330
331
if not format:
332
format = "%a %b %d %H:%M:%S %Y"
333
ts, fmt = string, format
334
def get_next(fmt):
335
''' returns next directive, next seperator, rest of format str'''
336
def get_sep(fmt):
337
res = []
338
if not fmt:
339
return '', ''
340
for i in range(len(fmt)-1):
341
c = fmt[i]
342
if c == '%':
343
break
344
res.append(c)
345
return ''.join(res), fmt[i:]
346
347
# return next seperator:
348
d, sep, f = None, None, None
349
if fmt:
350
if fmt[0] == '%':
351
d = fmt[1]
352
sep, f = get_sep(fmt[2:])
353
else:
354
sep, f = get_sep(fmt)
355
return d, sep, f
356
357
# directive / value tuples go in here:
358
dir_val = {}
359
while ts:
360
d, sep, fmt = get_next(fmt)
361
if sep == '':
362
lv = None
363
if d:
364
# we have a directive, seperator is empty. Is the directive
365
# fixed length, with next w/o sep? e.g. %Y%Z ?
366
# then get the next one like:
367
l = -1
368
if d == 'Y': l = 4
369
elif d == 'a': l = len(__weekdays[0])
370
elif d == 'A': l = len(__weekdays_long[0])
371
elif d == 'b': l = len(__months[0])
372
elif d in ('d', 'm', 'H', 'M', 'S'):
373
l = 2
374
if l > -1:
375
lv = [ts[:l], ts[l:]]
376
if not lv:
377
lv = [ts, '']
378
else:
379
lv = _lsplit(ts, sep, 1)
380
if d == None:
381
ts = lv[1]
382
continue
383
ts, dir_val[d] = lv[1], lv[0]
384
if fmt == '':
385
break
386
# defaults when not specified:
387
t = [1900, 1, 1, 0, 0, 0, 0, 1, -1]
388
ignore_keys = []
389
have_weekday = False
390
for d, v in dir_val.items():
391
if d in ignore_keys:
392
continue
393
394
if d == 'p':
395
continue
396
397
if d in __lu.keys():
398
t[__lu[d]] = int(v)
399
continue
400
401
if d in ('a', 'A', 'b', 'B'):
402
v = v.toLowerCase()
403
404
if d == 'm':
405
# we go the python 2(!) way for conflicting %b %m and take %m
406
# why? because there IS no Py3 way (see strp time testlet)
407
ignore_keys.append('b')
408
ignore_keys.append('B')
409
410
# better readable than short:
411
if d == 'a':
412
# funny. the weekday is only set but does not override %d.
413
# -> produces impossible dates but well its how py does it:
414
if not v in __weekdays:
415
raise ValueError('Weekday unknown in your locale')
416
have_weekday = True
417
t[6] = __weekdays.index(v)
418
419
elif d == 'A':
420
if not v in __weekdays_long:
421
raise ValueError('Weekday unknown in your locale')
422
have_weekday = True
423
t[6] = __weekdays_long.index(v)
424
425
elif d == 'b':
426
# month short. overruled by m if present
427
if not v in __months:
428
raise ValueError('Month unknown in your locale')
429
t[1] = __months.index(v) + 1
430
431
elif d == 'B':
432
# month long. overruled by m if present
433
if not v in __months_long:
434
raise ValueError('Month unknown in your locale')
435
t[1] = __months_long.index(v) + 1
436
437
438
elif d == 'I':
439
# 0-12 hour, with AM/PM.
440
ampm = dir_val['p'] or 'am'
441
ampm = ampm.toLowerCase()
442
v = int(v)
443
# thats how py does it
444
if v == 12:
445
v = 0
446
elif v > 12:
447
raise ValueError("time data '" + string + \
448
"' does not match format '" + format + "'")
449
if ampm == 'pm':
450
v += 12
451
t[__lu['H']] = v
452
453
elif d == 'y':
454
t[0] = 2000 + int(v) # producing a y3k problem. try find me, then.
455
456
elif d == 'Z':
457
if v.toLowerCase() in ['gmt', 'utc']:
458
t[-1] = 0
459
460
# get day of year, costing us an object, to stay safe:
461
__date = new Date(0)
462
__date.setUTCFullYear( t[0] )
463
__date.setUTCMonth(t[1] -1 )
464
__date.setUTCDate( t[2] )
465
__date.setUTCHours(t[3])
466
t[7] = _day_of_year(__date, True) # JdeH y2018m08d21: Added actual parameter True, needed to match CPython 3.7, somehow not needed with 3.6
467
if not have_weekday:
468
t[6] = __date.getUTCDay() -1
469
470
return t
471
472
473
def strftime(format, t):
474
def zf2(v):
475
''' zfill missing '''
476
if v < 10:
477
return '0' + str(v)
478
return v
479
480
if not t:
481
t = localtime()
482
483
f = format
484
for d in __lu.keys():
485
k = '%' + d
486
if not k in f:
487
continue
488
v = zf2(t[__lu[d]])
489
f = f.replace(k, v)
490
for d, l, pos in (('b', __months , 1), ('B', __months_long , 1), ('a', __weekdays, 6), ('A', __weekdays_long, 6)):
491
p = t[pos]
492
if pos == 1:
493
p = p -1
494
v = l[p].capitalize()
495
f = f.replace('%' + d, v)
496
497
if '%p' in f:
498
if t[3] > 11:
499
ap = 'PM'
500
else:
501
ap = 'AM'
502
f = f.replace('%p', ap)
503
504
if '%y' in f:
505
f = f.replace('%y', str(t[0])[-2:])
506
507
if '%I' in f:
508
v = t[3]
509
if v == 0:
510
v = 12
511
elif v > 12:
512
v = v - 12
513
f = f.replace('%I', zf2(v))
514
515
return f
516
517