Mercurial > repos > rliterman > csp2
comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/DateTime/DateTime.py @ 69:33d812a61356
planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author | jpayne |
---|---|
date | Tue, 18 Mar 2025 17:55:14 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
67:0e9998148a16 | 69:33d812a61356 |
---|---|
1 ############################################################################## | |
2 # | |
3 # Copyright (c) 2002 Zope Foundation and Contributors. | |
4 # | |
5 # This software is subject to the provisions of the Zope Public License, | |
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. | |
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED | |
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS | |
10 # FOR A PARTICULAR PURPOSE | |
11 # | |
12 ############################################################################## | |
13 | |
14 import copyreg as copy_reg | |
15 import math | |
16 import re | |
17 from datetime import datetime | |
18 from time import altzone | |
19 from time import daylight | |
20 from time import gmtime | |
21 from time import localtime | |
22 from time import time | |
23 from time import timezone | |
24 from time import tzname | |
25 | |
26 from zope.interface import implementer | |
27 | |
28 from .interfaces import DateError | |
29 from .interfaces import DateTimeError | |
30 from .interfaces import IDateTime | |
31 from .interfaces import SyntaxError | |
32 from .interfaces import TimeError | |
33 from .pytz_support import PytzCache | |
34 | |
35 | |
36 basestring = str | |
37 long = int | |
38 explicit_unicode_type = type(None) | |
39 | |
40 default_datefmt = None | |
41 | |
42 | |
43 def getDefaultDateFormat(): | |
44 global default_datefmt | |
45 if default_datefmt is None: | |
46 try: | |
47 from App.config import getConfiguration | |
48 default_datefmt = getConfiguration().datetime_format | |
49 return default_datefmt | |
50 except Exception: | |
51 return 'us' | |
52 else: | |
53 return default_datefmt | |
54 | |
55 | |
56 # To control rounding errors, we round system time to the nearest | |
57 # microsecond. Then delicate calculations can rely on the fact that the | |
58 # maximum precision that needs to be preserved is known. | |
59 _system_time = time | |
60 | |
61 | |
62 def time(): | |
63 return round(_system_time(), 6) | |
64 | |
65 | |
66 # Determine machine epoch | |
67 tm = ((0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334), | |
68 (0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)) | |
69 yr, mo, dy, hr, mn, sc = gmtime(0)[:6] | |
70 i = int(yr - 1) | |
71 to_year = int(i * 365 + i // 4 - i // 100 + i // 400 - 693960.0) | |
72 to_month = tm[yr % 4 == 0 and (yr % 100 != 0 or yr % 400 == 0)][mo] | |
73 EPOCH = ((to_year + to_month + dy + | |
74 (hr / 24.0 + mn / 1440.0 + sc / 86400.0)) * 86400) | |
75 jd1901 = 2415385 | |
76 | |
77 _TZINFO = PytzCache() | |
78 | |
79 INT_PATTERN = re.compile(r'([0-9]+)') | |
80 FLT_PATTERN = re.compile(r':([0-9]+\.[0-9]+)') | |
81 NAME_PATTERN = re.compile(r'([a-zA-Z]+)', re.I) | |
82 SPACE_CHARS = ' \t\n' | |
83 DELIMITERS = '-/.:,+' | |
84 | |
85 _MONTH_LEN = ((0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), | |
86 (0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)) | |
87 _MONTHS = ('', 'January', 'February', 'March', 'April', 'May', 'June', | |
88 'July', 'August', 'September', 'October', 'November', 'December') | |
89 _MONTHS_A = ('', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', | |
90 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec') | |
91 _MONTHS_P = ('', 'Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June', | |
92 'July', 'Aug.', 'Sep.', 'Oct.', 'Nov.', 'Dec.') | |
93 _MONTHMAP = {'january': 1, 'jan': 1, | |
94 'february': 2, 'feb': 2, | |
95 'march': 3, 'mar': 3, | |
96 'april': 4, 'apr': 4, | |
97 'may': 5, | |
98 'june': 6, 'jun': 6, | |
99 'july': 7, 'jul': 7, | |
100 'august': 8, 'aug': 8, | |
101 'september': 9, 'sep': 9, 'sept': 9, | |
102 'october': 10, 'oct': 10, | |
103 'november': 11, 'nov': 11, | |
104 'december': 12, 'dec': 12} | |
105 _DAYS = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', | |
106 'Thursday', 'Friday', 'Saturday') | |
107 _DAYS_A = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat') | |
108 _DAYS_P = ('Sun.', 'Mon.', 'Tue.', 'Wed.', 'Thu.', 'Fri.', 'Sat.') | |
109 _DAYMAP = {'sunday': 1, 'sun': 1, | |
110 'monday': 2, 'mon': 2, | |
111 'tuesday': 3, 'tues': 3, 'tue': 3, | |
112 'wednesday': 4, 'wed': 4, | |
113 'thursday': 5, 'thurs': 5, 'thur': 5, 'thu': 5, | |
114 'friday': 6, 'fri': 6, | |
115 'saturday': 7, 'sat': 7} | |
116 | |
117 numericTimeZoneMatch = re.compile(r'[+-][0-9][0-9][0-9][0-9]').match | |
118 iso8601Match = re.compile(r''' | |
119 (?P<year>\d\d\d\d) # four digits year | |
120 (?:-? # one optional dash | |
121 (?: # followed by: | |
122 (?P<year_day>\d\d\d # three digits year day | |
123 (?!\d)) # when there is no fourth digit | |
124 | # or: | |
125 W # one W | |
126 (?P<week>\d\d) # two digits week | |
127 (?:-? # one optional dash | |
128 (?P<week_day>\d) # one digit week day | |
129 )? # week day is optional | |
130 | # or: | |
131 (?P<month>\d\d)? # two digits month | |
132 (?:-? # one optional dash | |
133 (?P<day>\d\d)? # two digits day | |
134 )? # after day is optional | |
135 ) # | |
136 )? # after year is optional | |
137 (?:[T ] # one T or one whitespace | |
138 (?P<hour>\d\d) # two digits hour | |
139 (?::? # one optional colon | |
140 (?P<minute>\d\d)? # two digits minute | |
141 (?::? # one optional colon | |
142 (?P<second>\d\d)? # two digits second | |
143 (?:[.,] # one dot or one comma | |
144 (?P<fraction>\d+) # n digits fraction | |
145 )? # after second is optional | |
146 )? # after minute is optional | |
147 )? # after hour is optional | |
148 (?: # timezone: | |
149 (?P<Z>Z) # one Z | |
150 | # or: | |
151 (?P<signal>[-+]) # one plus or one minus as signal | |
152 (?P<hour_off>\d # one digit for hour offset... | |
153 (?:\d(?!\d$) # ...or two, if not the last two digits | |
154 )?) # second hour offset digit is optional | |
155 (?::? # one optional colon | |
156 (?P<min_off>\d\d) # two digits minute offset | |
157 )? # after hour offset is optional | |
158 )? # timezone is optional | |
159 )? # time is optional | |
160 (?P<garbage>.*) # store the extra garbage | |
161 ''', re.VERBOSE).match | |
162 | |
163 | |
164 def _findLocalTimeZoneName(isDST): | |
165 if not daylight: | |
166 # Daylight savings does not occur in this time zone. | |
167 isDST = 0 | |
168 try: | |
169 # Get the name of the current time zone depending | |
170 # on DST. | |
171 _localzone = PytzCache._zmap[tzname[isDST].lower()] | |
172 except BaseException: | |
173 try: | |
174 # Generate a GMT-offset zone name. | |
175 if isDST: | |
176 localzone = altzone | |
177 else: | |
178 localzone = timezone | |
179 offset = (-localzone / 3600.0) | |
180 majorOffset = int(offset) | |
181 if majorOffset != 0: | |
182 minorOffset = abs(int((offset % majorOffset) * 60.0)) | |
183 else: | |
184 minorOffset = 0 | |
185 m = majorOffset >= 0 and '+' or '' | |
186 lz = '%s%0.02d%0.02d' % (m, majorOffset, minorOffset) | |
187 _localzone = PytzCache._zmap[('GMT%s' % lz).lower()] | |
188 except BaseException: | |
189 _localzone = '' | |
190 return _localzone | |
191 | |
192 | |
193 _localzone0 = _findLocalTimeZoneName(0) | |
194 _localzone1 = _findLocalTimeZoneName(1) | |
195 _multipleZones = (_localzone0 != _localzone1) | |
196 | |
197 # Some utility functions for calculating dates: | |
198 | |
199 | |
200 def _calcSD(t): | |
201 # Returns timezone-independent days since epoch and the fractional | |
202 # part of the days. | |
203 dd = t + EPOCH - 86400.0 | |
204 d = dd / 86400.0 | |
205 s = d - math.floor(d) | |
206 return s, d | |
207 | |
208 | |
209 def _calcDependentSecond(tz, t): | |
210 # Calculates the timezone-dependent second (integer part only) | |
211 # from the timezone-independent second. | |
212 fset = _tzoffset(tz, t) | |
213 return fset + long(math.floor(t)) + long(EPOCH) - 86400 | |
214 | |
215 | |
216 def _calcDependentSecond2(yr, mo, dy, hr, mn, sc): | |
217 # Calculates the timezone-dependent second (integer part only) | |
218 # from the date given. | |
219 ss = int(hr) * 3600 + int(mn) * 60 + int(sc) | |
220 x = long(_julianday(yr, mo, dy) - jd1901) * 86400 + ss | |
221 return x | |
222 | |
223 | |
224 def _calcIndependentSecondEtc(tz, x, ms): | |
225 # Derive the timezone-independent second from the timezone | |
226 # dependent second. | |
227 fsetAtEpoch = _tzoffset(tz, 0.0) | |
228 nearTime = x - fsetAtEpoch - long(EPOCH) + 86400 + ms | |
229 # nearTime is now within an hour of being correct. | |
230 # Recalculate t according to DST. | |
231 fset = long(_tzoffset(tz, nearTime)) | |
232 d = (x - fset) / 86400.0 + (ms / 86400.0) | |
233 t = x - fset - long(EPOCH) + 86400 + ms | |
234 micros = (x + 86400 - fset) * 1000000 + \ | |
235 long(round(ms * 1000000.0)) - long(EPOCH * 1000000.0) | |
236 s = d - math.floor(d) | |
237 return (s, d, t, micros) | |
238 | |
239 | |
240 def _calcHMS(x, ms): | |
241 # hours, minutes, seconds from integer and float. | |
242 hr = x // 3600 | |
243 x = x - hr * 3600 | |
244 mn = x // 60 | |
245 sc = x - mn * 60 + ms | |
246 return (hr, mn, sc) | |
247 | |
248 | |
249 def _calcYMDHMS(x, ms): | |
250 # x is a timezone-dependent integer of seconds. | |
251 # Produces yr,mo,dy,hr,mn,sc. | |
252 yr, mo, dy = _calendarday(x // 86400 + jd1901) | |
253 x = int(x - (x // 86400) * 86400) | |
254 hr = x // 3600 | |
255 x = x - hr * 3600 | |
256 mn = x // 60 | |
257 sc = x - mn * 60 + ms | |
258 return (yr, mo, dy, hr, mn, sc) | |
259 | |
260 | |
261 def _julianday(yr, mo, dy): | |
262 y, m, d = long(yr), long(mo), long(dy) | |
263 if m > 12: | |
264 y = y + m // 12 | |
265 m = m % 12 | |
266 elif m < 1: | |
267 m = -m | |
268 y = y - m // 12 - 1 | |
269 m = 12 - m % 12 | |
270 if y > 0: | |
271 yr_correct = 0 | |
272 else: | |
273 yr_correct = 3 | |
274 if m < 3: | |
275 y, m = y - 1, m + 12 | |
276 if y * 10000 + m * 100 + d > 15821014: | |
277 b = 2 - y // 100 + y // 400 | |
278 else: | |
279 b = 0 | |
280 return ((1461 * y - yr_correct) // 4 + | |
281 306001 * (m + 1) // 10000 + d + 1720994 + b) | |
282 | |
283 | |
284 def _calendarday(j): | |
285 j = long(j) | |
286 if (j < 2299160): | |
287 b = j + 1525 | |
288 else: | |
289 a = (4 * j - 7468861) // 146097 | |
290 b = j + 1526 + a - a // 4 | |
291 c = (20 * b - 2442) // 7305 | |
292 d = 1461 * c // 4 | |
293 e = 10000 * (b - d) // 306001 | |
294 dy = int(b - d - 306001 * e // 10000) | |
295 mo = (e < 14) and int(e - 1) or int(e - 13) | |
296 yr = (mo > 2) and (c - 4716) or (c - 4715) | |
297 return (int(yr), int(mo), int(dy)) | |
298 | |
299 | |
300 def _tzoffset(tz, t): | |
301 """Returns the offset in seconds to GMT from a specific timezone (tz) at | |
302 a specific time (t). NB! The _tzoffset result is the same same sign as | |
303 the time zone, i.e. GMT+2 has a 7200 second offset. This is the opposite | |
304 sign of time.timezone which (confusingly) is -7200 for GMT+2.""" | |
305 try: | |
306 return _TZINFO[tz].info(t)[0] | |
307 except Exception: | |
308 if numericTimeZoneMatch(tz) is not None: | |
309 return int(tz[0:3]) * 3600 + int(tz[0] + tz[3:5]) * 60 | |
310 else: | |
311 return 0 # ?? | |
312 | |
313 | |
314 def _correctYear(year): | |
315 # Y2K patch. | |
316 if year >= 0 and year < 100: | |
317 # 00-69 means 2000-2069, 70-99 means 1970-1999. | |
318 if year < 70: | |
319 year = 2000 + year | |
320 else: | |
321 year = 1900 + year | |
322 return year | |
323 | |
324 | |
325 def safegmtime(t): | |
326 '''gmtime with a safety zone.''' | |
327 try: | |
328 return gmtime(t) | |
329 except (ValueError, OverflowError): | |
330 raise TimeError('The time %f is beyond the range of this Python ' | |
331 'implementation.' % float(t)) | |
332 | |
333 | |
334 def safelocaltime(t): | |
335 '''localtime with a safety zone.''' | |
336 try: | |
337 return localtime(t) | |
338 except (ValueError, OverflowError): | |
339 raise TimeError('The time %f is beyond the range of this Python ' | |
340 'implementation.' % float(t)) | |
341 | |
342 | |
343 def _tzoffset2rfc822zone(seconds): | |
344 """Takes an offset, such as from _tzoffset(), and returns an rfc822 | |
345 compliant zone specification. Please note that the result of | |
346 _tzoffset() is the negative of what time.localzone and time.altzone is. | |
347 """ | |
348 return "%+03d%02d" % divmod((seconds // 60), 60) | |
349 | |
350 | |
351 def _tzoffset2iso8601zone(seconds): | |
352 """Takes an offset, such as from _tzoffset(), and returns an ISO 8601 | |
353 compliant zone specification. Please note that the result of | |
354 _tzoffset() is the negative of what time.localzone and time.altzone is. | |
355 """ | |
356 return "%+03d:%02d" % divmod((seconds // 60), 60) | |
357 | |
358 | |
359 def Timezones(): | |
360 """Return the list of recognized timezone names""" | |
361 return sorted(list(PytzCache._zmap.values())) | |
362 | |
363 | |
364 class strftimeFormatter: | |
365 | |
366 def __init__(self, dt, format): | |
367 self.dt = dt | |
368 self.format = format | |
369 | |
370 def __call__(self): | |
371 return self.dt.strftime(self.format) | |
372 | |
373 | |
374 @implementer(IDateTime) | |
375 class DateTime: | |
376 """DateTime objects represent instants in time and provide | |
377 interfaces for controlling its representation without | |
378 affecting the absolute value of the object. | |
379 | |
380 DateTime objects may be created from a wide variety of string | |
381 or numeric data, or may be computed from other DateTime objects. | |
382 DateTimes support the ability to convert their representations | |
383 to many major timezones, as well as the ability to create a | |
384 DateTime object in the context of a given timezone. | |
385 | |
386 DateTime objects provide partial numerical behavior: | |
387 | |
388 - Two date-time objects can be subtracted to obtain a time, | |
389 in days between the two. | |
390 | |
391 - A date-time object and a positive or negative number may | |
392 be added to obtain a new date-time object that is the given | |
393 number of days later than the input date-time object. | |
394 | |
395 - A positive or negative number and a date-time object may | |
396 be added to obtain a new date-time object that is the given | |
397 number of days later than the input date-time object. | |
398 | |
399 - A positive or negative number may be subtracted from a | |
400 date-time object to obtain a new date-time object that is | |
401 the given number of days earlier than the input date-time | |
402 object. | |
403 | |
404 DateTime objects may be converted to integer, long, or float | |
405 numbers of days since January 1, 1901, using the standard int, | |
406 long, and float functions (Compatibility Note: int, long and | |
407 float return the number of days since 1901 in GMT rather than | |
408 local machine timezone). DateTime objects also provide access | |
409 to their value in a float format usable with the Python time | |
410 module, provided that the value of the object falls in the | |
411 range of the epoch-based time module, and as a datetime.datetime | |
412 object. | |
413 | |
414 A DateTime object should be considered immutable; all conversion | |
415 and numeric operations return a new DateTime object rather than | |
416 modify the current object.""" | |
417 | |
418 # For security machinery: | |
419 __roles__ = None | |
420 __allow_access_to_unprotected_subobjects__ = 1 | |
421 | |
422 # Limit the amount of instance attributes | |
423 __slots__ = ( | |
424 '_timezone_naive', | |
425 '_tz', | |
426 '_dayoffset', | |
427 '_year', | |
428 '_month', | |
429 '_day', | |
430 '_hour', | |
431 '_minute', | |
432 '_second', | |
433 '_nearsec', | |
434 '_d', | |
435 '_micros', | |
436 'time', | |
437 ) | |
438 | |
439 def __init__(self, *args, **kw): | |
440 """Return a new date-time object""" | |
441 try: | |
442 return self._parse_args(*args, **kw) | |
443 except (DateError, TimeError, DateTimeError): | |
444 raise | |
445 except Exception: | |
446 raise SyntaxError('Unable to parse {}, {}'.format(args, kw)) | |
447 | |
448 def __getstate__(self): | |
449 return (self._micros, | |
450 getattr(self, '_timezone_naive', False), | |
451 self._tz) | |
452 | |
453 def __setstate__(self, value): | |
454 if isinstance(value, tuple): | |
455 micros, tz_naive, tz = value | |
456 if isinstance(micros, float): | |
457 # BBB: support for pickle where micros was a float | |
458 micros = int(micros * 1000000) | |
459 self._parse_args(micros / 1000000., tz) | |
460 self._micros = micros | |
461 self._timezone_naive = tz_naive | |
462 else: | |
463 for k, v in value.items(): | |
464 if k in self.__slots__: | |
465 setattr(self, k, v) | |
466 # BBB: support for very old DateTime pickles | |
467 if '_micros' not in value: | |
468 self._micros = long(value['_t'] * 1000000) | |
469 if '_timezone_naive' not in value: | |
470 self._timezone_naive = False | |
471 | |
472 def _parse_args(self, *args, **kw): | |
473 """Return a new date-time object. | |
474 | |
475 A DateTime object always maintains its value as an absolute | |
476 UTC time, and is represented in the context of some timezone | |
477 based on the arguments used to create the object. A DateTime | |
478 object's methods return values based on the timezone context. | |
479 | |
480 Note that in all cases the local machine timezone is used for | |
481 representation if no timezone is specified. | |
482 | |
483 DateTimes may be created with zero to seven arguments. | |
484 | |
485 - If the function is called with no arguments or with None, | |
486 then the current date/time is returned, represented in the | |
487 timezone of the local machine. | |
488 | |
489 - If the function is invoked with a single string argument | |
490 which is a recognized timezone name, an object representing | |
491 the current time is returned, represented in the specified | |
492 timezone. | |
493 | |
494 - If the function is invoked with a single string argument | |
495 representing a valid date/time, an object representing | |
496 that date/time will be returned. | |
497 | |
498 As a general rule, any date-time representation that is | |
499 recognized and unambiguous to a resident of North America | |
500 is acceptable. The reason for this qualification is that | |
501 in North America, a date like: 2/1/1994 is interpreted | |
502 as February 1, 1994, while in some parts of the world, | |
503 it is interpreted as January 2, 1994. | |
504 | |
505 A date/time string consists of two components, a date | |
506 component and an optional time component, separated by one | |
507 or more spaces. If the time component is omitted, 12:00am is | |
508 assumed. Any recognized timezone name specified as the final | |
509 element of the date/time string will be used for computing | |
510 the date/time value. If you create a DateTime with the | |
511 string 'Mar 9, 1997 1:45pm US/Pacific', the value will | |
512 essentially be the same as if you had captured time.time() | |
513 at the specified date and time on a machine in that timezone: | |
514 | |
515 <pre> | |
516 e = DateTime('US/Eastern') | |
517 # returns current date/time, represented in US/Eastern. | |
518 | |
519 x = DateTime('1997/3/9 1:45pm') | |
520 # returns specified time, represented in local machine zone. | |
521 | |
522 y = DateTime('Mar 9, 1997 13:45:00') | |
523 # y is equal to x | |
524 </pre> | |
525 | |
526 The date component consists of year, month, and day | |
527 values. The year value must be a one-, two-, or | |
528 four-digit integer. If a one- or two-digit year is | |
529 used, the year is assumed to be in the twentieth | |
530 century. The month may be an integer, from 1 to 12, a | |
531 month name, or a month abbreviation, where a period may | |
532 optionally follow the abbreviation. The day must be an | |
533 integer from 1 to the number of days in the month. The | |
534 year, month, and day values may be separated by | |
535 periods, hyphens, forward slashes, or spaces. Extra | |
536 spaces are permitted around the delimiters. Year, | |
537 month, and day values may be given in any order as long | |
538 as it is possible to distinguish the components. If all | |
539 three components are numbers that are less than 13, | |
540 then a month-day-year ordering is assumed. | |
541 | |
542 The time component consists of hour, minute, and second | |
543 values separated by colons. The hour value must be an | |
544 integer between 0 and 23 inclusively. The minute value | |
545 must be an integer between 0 and 59 inclusively. The | |
546 second value may be an integer value between 0 and | |
547 59.999 inclusively. The second value or both the minute | |
548 and second values may be omitted. The time may be | |
549 followed by am or pm in upper or lower case, in which | |
550 case a 12-hour clock is assumed. | |
551 | |
552 New in Zope 2.4: | |
553 The DateTime constructor automatically detects and handles | |
554 ISO8601 compliant dates (YYYY-MM-DDThh:ss:mmTZD). | |
555 | |
556 New in Zope 2.9.6: | |
557 The existing ISO8601 parser was extended to support almost | |
558 the whole ISO8601 specification. New formats includes: | |
559 | |
560 <pre> | |
561 y = DateTime('1993-045') | |
562 # returns the 45th day from 1993, which is 14th February | |
563 | |
564 w = DateTime('1993-W06-7') | |
565 # returns the 7th day from the 6th week from 1993, which | |
566 # is also 14th February | |
567 </pre> | |
568 | |
569 See http://en.wikipedia.org/wiki/ISO_8601 for full specs. | |
570 | |
571 Note that the Zope DateTime parser assumes timezone naive ISO | |
572 strings to be in UTC rather than local time as specified. | |
573 | |
574 - If the DateTime function is invoked with a single numeric | |
575 argument, the number is assumed to be a floating point value | |
576 such as that returned by time.time(). | |
577 | |
578 A DateTime object is returned that represents the GMT value | |
579 of the time.time() float represented in the local machine's | |
580 timezone. | |
581 | |
582 - If the DateTime function is invoked with a single argument | |
583 that is a DateTime instance, a copy of the passed object will | |
584 be created. | |
585 | |
586 - New in 2.11: | |
587 The DateTime function may now be invoked with a single argument | |
588 that is a datetime.datetime instance. DateTimes may be converted | |
589 back to datetime.datetime objects with asdatetime(). | |
590 DateTime instances may be converted to a timezone naive | |
591 datetime.datetime in UTC with utcdatetime(). | |
592 | |
593 - If the function is invoked with two numeric arguments, then | |
594 the first is taken to be an integer year and the second | |
595 argument is taken to be an offset in days from the beginning | |
596 of the year, in the context of the local machine timezone. | |
597 | |
598 The date-time value returned is the given offset number of | |
599 days from the beginning of the given year, represented in | |
600 the timezone of the local machine. The offset may be positive | |
601 or negative. | |
602 | |
603 Two-digit years are assumed to be in the twentieth | |
604 century. | |
605 | |
606 - If the function is invoked with two arguments, the first | |
607 a float representing a number of seconds past the epoch | |
608 in gmt (such as those returned by time.time()) and the | |
609 second a string naming a recognized timezone, a DateTime | |
610 with a value of that gmt time will be returned, represented | |
611 in the given timezone. | |
612 | |
613 <pre> | |
614 import time | |
615 t = time.time() | |
616 | |
617 now_east = DateTime(t,'US/Eastern') | |
618 # Time t represented as US/Eastern | |
619 | |
620 now_west = DateTime(t,'US/Pacific') | |
621 # Time t represented as US/Pacific | |
622 | |
623 # now_east == now_west | |
624 # only their representations are different | |
625 </pre> | |
626 | |
627 - If the function is invoked with three or more numeric | |
628 arguments, then the first is taken to be an integer | |
629 year, the second is taken to be an integer month, and | |
630 the third is taken to be an integer day. If the | |
631 combination of values is not valid, then a | |
632 DateError is raised. Two-digit years are assumed | |
633 to be in the twentieth century. The fourth, fifth, and | |
634 sixth arguments specify a time in hours, minutes, and | |
635 seconds; hours and minutes should be positive integers | |
636 and seconds is a positive floating point value, all of | |
637 these default to zero if not given. An optional string may | |
638 be given as the final argument to indicate timezone (the | |
639 effect of this is as if you had taken the value of time.time() | |
640 at that time on a machine in the specified timezone). | |
641 | |
642 New in Zope 2.7: | |
643 A new keyword parameter "datefmt" can be passed to the | |
644 constructor. If set to "international", the constructor | |
645 is forced to treat ambiguous dates as "days before month | |
646 before year". This useful if you need to parse non-US | |
647 dates in a reliable way | |
648 | |
649 In any case that a floating point number of seconds is given | |
650 or derived, it's rounded to the nearest millisecond. | |
651 | |
652 If a string argument passed to the DateTime constructor cannot be | |
653 parsed, it will raise DateTime.SyntaxError. Invalid date components | |
654 will raise a DateError, while invalid time or timezone components | |
655 will raise a DateTimeError. | |
656 | |
657 The module function Timezones() will return a list of the (common) | |
658 timezones recognized by the DateTime module. Recognition of | |
659 timezone names is case-insensitive. | |
660 """ | |
661 | |
662 datefmt = kw.get('datefmt', getDefaultDateFormat()) | |
663 d = t = s = None | |
664 ac = len(args) | |
665 microsecs = None | |
666 | |
667 if ac == 10: | |
668 # Internal format called only by DateTime | |
669 yr, mo, dy, hr, mn, sc, tz, t, d, s = args | |
670 elif ac == 11: | |
671 # Internal format that includes milliseconds (from the epoch) | |
672 yr, mo, dy, hr, mn, sc, tz, t, d, s, millisecs = args | |
673 microsecs = millisecs * 1000 | |
674 | |
675 elif ac == 12: | |
676 # Internal format that includes microseconds (from the epoch) and a | |
677 # flag indicating whether this was constructed in a timezone naive | |
678 # manner | |
679 yr, mo, dy, hr, mn, sc, tz, t, d, s, microsecs, tznaive = args | |
680 if tznaive is not None: # preserve this information | |
681 self._timezone_naive = tznaive | |
682 | |
683 elif not args or (ac and args[0] is None): | |
684 # Current time, to be displayed in local timezone | |
685 t = time() | |
686 lt = safelocaltime(t) | |
687 tz = self.localZone(lt) | |
688 ms = (t - math.floor(t)) | |
689 s, d = _calcSD(t) | |
690 yr, mo, dy, hr, mn, sc = lt[:6] | |
691 sc = sc + ms | |
692 self._timezone_naive = False | |
693 | |
694 elif ac == 1: | |
695 arg = args[0] | |
696 | |
697 if arg == '': | |
698 raise SyntaxError(arg) | |
699 | |
700 if isinstance(arg, DateTime): | |
701 """Construct a new DateTime instance from a given | |
702 DateTime instance. | |
703 """ | |
704 t = arg.timeTime() | |
705 s, d = _calcSD(t) | |
706 yr, mo, dy, hr, mn, sc, tz = arg.parts() | |
707 | |
708 elif isinstance(arg, datetime): | |
709 yr, mo, dy, hr, mn, sc, numerictz, tznaive = \ | |
710 self._parse_iso8601_preserving_tznaive(arg.isoformat()) | |
711 if arg.tzinfo is None: | |
712 self._timezone_naive = True | |
713 tz = None | |
714 else: | |
715 self._timezone_naive = False | |
716 # if we have a pytz tzinfo, use the `zone` attribute | |
717 # as a key | |
718 tz = getattr(arg.tzinfo, 'zone', numerictz) | |
719 ms = sc - math.floor(sc) | |
720 x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc) | |
721 | |
722 if tz: | |
723 try: | |
724 zone = _TZINFO[tz] | |
725 except DateTimeError: | |
726 try: | |
727 zone = _TZINFO[numerictz] | |
728 except DateTimeError: | |
729 raise DateTimeError( | |
730 'Unknown time zone in date: %s' % arg) | |
731 tz = zone.tzinfo.zone | |
732 else: | |
733 tz = self._calcTimezoneName(x, ms) | |
734 s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms) | |
735 | |
736 elif (isinstance(arg, basestring) and | |
737 arg.lower() in _TZINFO._zidx): | |
738 # Current time, to be displayed in specified timezone | |
739 t, tz = time(), _TZINFO._zmap[arg.lower()] | |
740 ms = (t - math.floor(t)) | |
741 # Use integer arithmetic as much as possible. | |
742 s, d = _calcSD(t) | |
743 x = _calcDependentSecond(tz, t) | |
744 yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms) | |
745 | |
746 elif isinstance(arg, basestring): | |
747 # Date/time string | |
748 iso8601 = iso8601Match(arg.strip()) | |
749 fields_iso8601 = iso8601 and iso8601.groupdict() or {} | |
750 if fields_iso8601 and not fields_iso8601.get('garbage'): | |
751 yr, mo, dy, hr, mn, sc, tz, tznaive = \ | |
752 self._parse_iso8601_preserving_tznaive(arg) | |
753 self._timezone_naive = tznaive | |
754 else: | |
755 yr, mo, dy, hr, mn, sc, tz = self._parse(arg, datefmt) | |
756 | |
757 if not self._validDate(yr, mo, dy): | |
758 raise DateError('Invalid date: %s' % arg) | |
759 if not self._validTime(hr, mn, int(sc)): | |
760 raise TimeError('Invalid time: %s' % arg) | |
761 ms = sc - math.floor(sc) | |
762 x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc) | |
763 | |
764 if tz: | |
765 try: | |
766 tz = _TZINFO._zmap[tz.lower()] | |
767 except KeyError: | |
768 if numericTimeZoneMatch(tz) is None: | |
769 raise DateTimeError( | |
770 'Unknown time zone in date: %s' % arg) | |
771 else: | |
772 tz = self._calcTimezoneName(x, ms) | |
773 s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms) | |
774 | |
775 else: | |
776 # Seconds from epoch, gmt | |
777 t = arg | |
778 lt = safelocaltime(t) | |
779 tz = self.localZone(lt) | |
780 ms = (t - math.floor(t)) | |
781 s, d = _calcSD(t) | |
782 yr, mo, dy, hr, mn, sc = lt[:6] | |
783 sc = sc + ms | |
784 | |
785 elif ac == 2: | |
786 if isinstance(args[1], basestring): | |
787 # Seconds from epoch (gmt) and timezone | |
788 t, tz = args | |
789 ms = (t - math.floor(t)) | |
790 try: | |
791 tz = _TZINFO._zmap[tz.lower()] | |
792 except KeyError: | |
793 if numericTimeZoneMatch(tz) is None: | |
794 raise DateTimeError('Unknown time zone: %s' % tz) | |
795 # Use integer arithmetic as much as possible. | |
796 s, d = _calcSD(t) | |
797 x = _calcDependentSecond(tz, t) | |
798 yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms) | |
799 else: | |
800 # Year, julian expressed in local zone | |
801 t = time() | |
802 lt = safelocaltime(t) | |
803 tz = self.localZone(lt) | |
804 yr, jul = args | |
805 yr = _correctYear(yr) | |
806 d = (_julianday(yr, 1, 0) - jd1901) + jul | |
807 x_float = d * 86400.0 | |
808 x_floor = math.floor(x_float) | |
809 ms = x_float - x_floor | |
810 x = long(x_floor) | |
811 yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms) | |
812 s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms) | |
813 else: | |
814 # Explicit format | |
815 yr, mo, dy = args[:3] | |
816 hr, mn, sc, tz = 0, 0, 0, 0 | |
817 yr = _correctYear(yr) | |
818 if not self._validDate(yr, mo, dy): | |
819 raise DateError('Invalid date: {}'.format(args)) | |
820 args = args[3:] | |
821 if args: | |
822 hr, args = args[0], args[1:] | |
823 if args: | |
824 mn, args = args[0], args[1:] | |
825 if args: | |
826 sc, args = args[0], args[1:] | |
827 if args: | |
828 tz, args = args[0], args[1:] | |
829 if args: | |
830 raise DateTimeError('Too many arguments') | |
831 if not self._validTime(hr, mn, sc): | |
832 raise TimeError('Invalid time: %s' % repr(args)) | |
833 | |
834 x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc) | |
835 ms = sc - math.floor(sc) | |
836 if tz: | |
837 try: | |
838 tz = _TZINFO._zmap[tz.lower()] | |
839 except KeyError: | |
840 if numericTimeZoneMatch(tz) is None: | |
841 raise DateTimeError('Unknown time zone: %s' % tz) | |
842 else: | |
843 # Get local time zone name | |
844 tz = self._calcTimezoneName(x, ms) | |
845 s, d, t, microsecs = _calcIndependentSecondEtc(tz, x, ms) | |
846 | |
847 self._dayoffset = int((_julianday(yr, mo, dy) + 2) % 7) | |
848 # Round to nearest microsecond in platform-independent way. You | |
849 # cannot rely on C sprintf (Python '%') formatting to round | |
850 # consistently; doing it ourselves ensures that all but truly | |
851 # horrid C sprintf implementations will yield the same result | |
852 # cross-platform, provided the format asks for exactly 6 digits after | |
853 # the decimal point. | |
854 sc = round(sc, 6) | |
855 if sc >= 60.0: # can happen if, e.g., orig sc was 59.9999999 | |
856 sc = 59.999999 | |
857 self._nearsec = math.floor(sc) | |
858 self._year, self._month, self._day = yr, mo, dy | |
859 self._hour, self._minute, self._second = hr, mn, sc | |
860 self.time, self._d, self._tz = s, d, tz | |
861 # self._micros is the time since the epoch | |
862 # in long integer microseconds. | |
863 if microsecs is None: | |
864 microsecs = long(round(t * 1000000.0)) | |
865 self._micros = microsecs | |
866 | |
867 def localZone(self, ltm=None): | |
868 '''Returns the time zone on the given date. The time zone | |
869 can change according to daylight savings.''' | |
870 if not _multipleZones: | |
871 return _localzone0 | |
872 if ltm is None: | |
873 ltm = localtime(time()) | |
874 isDST = ltm[8] | |
875 lz = isDST and _localzone1 or _localzone0 | |
876 return lz | |
877 | |
878 def _calcTimezoneName(self, x, ms): | |
879 # Derive the name of the local time zone at the given | |
880 # timezone-dependent second. | |
881 if not _multipleZones: | |
882 return _localzone0 | |
883 fsetAtEpoch = _tzoffset(_localzone0, 0.0) | |
884 nearTime = x - fsetAtEpoch - long(EPOCH) + 86400 + ms | |
885 # nearTime is within an hour of being correct. | |
886 try: | |
887 ltm = safelocaltime(nearTime) | |
888 except BaseException: | |
889 # We are beyond the range of Python's date support. | |
890 # Hopefully we can assume that daylight savings schedules | |
891 # repeat every 28 years. Calculate the name of the | |
892 # time zone using a supported range of years. | |
893 yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, 0) | |
894 yr = ((yr - 1970) % 28) + 1970 | |
895 x = _calcDependentSecond2(yr, mo, dy, hr, mn, sc) | |
896 nearTime = x - fsetAtEpoch - long(EPOCH) + 86400 + ms | |
897 | |
898 # nearTime might still be negative if we are east of Greenwich. | |
899 # But we can assume on 1969/12/31 were no timezone changes. | |
900 nearTime = max(0, nearTime) | |
901 | |
902 ltm = safelocaltime(nearTime) | |
903 tz = self.localZone(ltm) | |
904 return tz | |
905 | |
906 def _parse(self, st, datefmt=getDefaultDateFormat()): | |
907 # Parse date-time components from a string | |
908 month = year = tz = tm = None | |
909 ValidZones = _TZINFO._zidx | |
910 TimeModifiers = ['am', 'pm'] | |
911 | |
912 # Find timezone first, since it should always be the last | |
913 # element, and may contain a slash, confusing the parser. | |
914 st = st.strip() | |
915 sp = st.split() | |
916 tz = sp[-1] | |
917 if tz and (tz.lower() in ValidZones): | |
918 self._timezone_naive = False | |
919 st = ' '.join(sp[:-1]) | |
920 else: | |
921 self._timezone_naive = True | |
922 tz = None # Decide later, since the default time zone | |
923 # could depend on the date. | |
924 | |
925 ints = [] | |
926 i = 0 | |
927 len_st = len(st) | |
928 while i < len_st: | |
929 while i < len_st and st[i] in SPACE_CHARS: | |
930 i += 1 | |
931 if i < len_st and st[i] in DELIMITERS: | |
932 d = st[i] | |
933 i += 1 | |
934 else: | |
935 d = '' | |
936 while i < len_st and st[i] in SPACE_CHARS: | |
937 i += 1 | |
938 | |
939 # The float pattern needs to look back 1 character, because it | |
940 # actually looks for a preceding colon like ':33.33'. This is | |
941 # needed to avoid accidentally matching the date part of a | |
942 # dot-separated date string such as '1999.12.31'. | |
943 if i > 0: | |
944 b = i - 1 | |
945 else: | |
946 b = i | |
947 | |
948 ts_results = FLT_PATTERN.match(st, b) | |
949 if ts_results: | |
950 s = ts_results.group(1) | |
951 i = i + len(s) | |
952 ints.append(float(s)) | |
953 continue | |
954 | |
955 ts_results = INT_PATTERN.match(st, i) | |
956 if ts_results: | |
957 s = ts_results.group(0) | |
958 | |
959 ls = len(s) | |
960 i = i + ls | |
961 if (ls == 4 and d and d in '+-' and | |
962 (len(ints) + (not not month) >= 3)): | |
963 tz = '{}{}'.format(d, s) | |
964 else: | |
965 v = int(s) | |
966 ints.append(v) | |
967 continue | |
968 | |
969 ts_results = NAME_PATTERN.match(st, i) | |
970 if ts_results: | |
971 s = ts_results.group(0).lower() | |
972 i = i + len(s) | |
973 if i < len_st and st[i] == '.': | |
974 i += 1 | |
975 # Check for month name: | |
976 _v = _MONTHMAP.get(s) | |
977 if _v is not None: | |
978 if month is None: | |
979 month = _v | |
980 else: | |
981 raise SyntaxError(st) | |
982 continue | |
983 # Check for time modifier: | |
984 if s in TimeModifiers: | |
985 if tm is None: | |
986 tm = s | |
987 else: | |
988 raise SyntaxError(st) | |
989 continue | |
990 # Check for and skip day of week: | |
991 if s in _DAYMAP: | |
992 continue | |
993 | |
994 raise SyntaxError(st) | |
995 | |
996 day = None | |
997 if ints[-1] > 60 and d not in ('.', ':', '/') and len(ints) > 2: | |
998 year = ints[-1] | |
999 del ints[-1] | |
1000 if month: | |
1001 day = ints[0] | |
1002 del ints[:1] | |
1003 else: | |
1004 if datefmt == "us": | |
1005 month = ints[0] | |
1006 day = ints[1] | |
1007 else: | |
1008 month = ints[1] | |
1009 day = ints[0] | |
1010 del ints[:2] | |
1011 elif month: | |
1012 if len(ints) > 1: | |
1013 if ints[0] > 31: | |
1014 year = ints[0] | |
1015 day = ints[1] | |
1016 else: | |
1017 year = ints[1] | |
1018 day = ints[0] | |
1019 del ints[:2] | |
1020 elif len(ints) > 2: | |
1021 if ints[0] > 31: | |
1022 year = ints[0] | |
1023 if ints[1] > 12: | |
1024 day = ints[1] | |
1025 month = ints[2] | |
1026 else: | |
1027 day = ints[2] | |
1028 month = ints[1] | |
1029 if ints[1] > 31: | |
1030 year = ints[1] | |
1031 if ints[0] > 12 and ints[2] <= 12: | |
1032 day = ints[0] | |
1033 month = ints[2] | |
1034 elif ints[2] > 12 and ints[0] <= 12: | |
1035 day = ints[2] | |
1036 month = ints[0] | |
1037 elif ints[2] > 31: | |
1038 year = ints[2] | |
1039 if ints[0] > 12: | |
1040 day = ints[0] | |
1041 month = ints[1] | |
1042 else: | |
1043 if datefmt == "us": | |
1044 day = ints[1] | |
1045 month = ints[0] | |
1046 else: | |
1047 day = ints[0] | |
1048 month = ints[1] | |
1049 | |
1050 elif ints[0] <= 12: | |
1051 month = ints[0] | |
1052 day = ints[1] | |
1053 year = ints[2] | |
1054 del ints[:3] | |
1055 | |
1056 if day is None: | |
1057 # Use today's date. | |
1058 year, month, day = localtime(time())[:3] | |
1059 | |
1060 year = _correctYear(year) | |
1061 if year < 1000: | |
1062 raise SyntaxError(st) | |
1063 | |
1064 leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) | |
1065 try: | |
1066 if not day or day > _MONTH_LEN[leap][month]: | |
1067 raise DateError(st) | |
1068 except IndexError: | |
1069 raise DateError(st) | |
1070 | |
1071 tod = 0 | |
1072 if ints: | |
1073 i = ints[0] | |
1074 # Modify hour to reflect am/pm | |
1075 if tm and (tm == 'pm') and i < 12: | |
1076 i += 12 | |
1077 if tm and (tm == 'am') and i == 12: | |
1078 i = 0 | |
1079 if i > 24: | |
1080 raise TimeError(st) | |
1081 tod = tod + int(i) * 3600 | |
1082 del ints[0] | |
1083 if ints: | |
1084 i = ints[0] | |
1085 if i > 60: | |
1086 raise TimeError(st) | |
1087 tod = tod + int(i) * 60 | |
1088 del ints[0] | |
1089 if ints: | |
1090 i = ints[0] | |
1091 if i > 60: | |
1092 raise TimeError(st) | |
1093 tod = tod + i | |
1094 del ints[0] | |
1095 if ints: | |
1096 raise SyntaxError(st) | |
1097 | |
1098 tod_int = int(math.floor(tod)) | |
1099 ms = tod - tod_int | |
1100 hr, mn, sc = _calcHMS(tod_int, ms) | |
1101 if not tz: | |
1102 # Figure out what time zone it is in the local area | |
1103 # on the given date. | |
1104 x = _calcDependentSecond2(year, month, day, hr, mn, sc) | |
1105 tz = self._calcTimezoneName(x, ms) | |
1106 | |
1107 return year, month, day, hr, mn, sc, tz | |
1108 | |
1109 # Internal methods | |
1110 def _validDate(self, y, m, d): | |
1111 if m < 1 or m > 12 or y < 0 or d < 1 or d > 31: | |
1112 return 0 | |
1113 return d <= _MONTH_LEN[ | |
1114 (y % 4 == 0 and (y % 100 != 0 or y % 400 == 0))][m] | |
1115 | |
1116 def _validTime(self, h, m, s): | |
1117 return h >= 0 and h <= 23 and m >= 0 and m <= 59 and s >= 0 and s < 60 | |
1118 | |
1119 def __getattr__(self, name): | |
1120 if '%' in name: | |
1121 return strftimeFormatter(self, name) | |
1122 raise AttributeError(name) | |
1123 | |
1124 # Conversion and comparison methods | |
1125 | |
1126 def timeTime(self): | |
1127 """Return the date/time as a floating-point number in UTC, | |
1128 in the format used by the Python time module. | |
1129 | |
1130 Note that it is possible to create date/time values with | |
1131 DateTime that have no meaningful value to the time module. | |
1132 """ | |
1133 return self._micros / 1000000.0 | |
1134 | |
1135 def toZone(self, z): | |
1136 """Return a DateTime with the value as the current | |
1137 object, represented in the indicated timezone. | |
1138 """ | |
1139 t, tz = self._t, _TZINFO._zmap[z.lower()] | |
1140 micros = self.micros() | |
1141 tznaive = False # you're performing a timzone change, can't be naive | |
1142 | |
1143 try: | |
1144 # Try to use time module for speed. | |
1145 yr, mo, dy, hr, mn, sc = safegmtime(t + _tzoffset(tz, t))[:6] | |
1146 sc = self._second | |
1147 return self.__class__(yr, mo, dy, hr, mn, sc, tz, t, | |
1148 self._d, self.time, micros, tznaive) | |
1149 except Exception: | |
1150 # gmtime can't perform the calculation in the given range. | |
1151 # Calculate the difference between the two time zones. | |
1152 tzdiff = _tzoffset(tz, t) - _tzoffset(self._tz, t) | |
1153 if tzdiff == 0: | |
1154 return self | |
1155 sc = self._second | |
1156 ms = sc - math.floor(sc) | |
1157 x = _calcDependentSecond2(self._year, self._month, self._day, | |
1158 self._hour, self._minute, sc) | |
1159 x_new = x + tzdiff | |
1160 yr, mo, dy, hr, mn, sc = _calcYMDHMS(x_new, ms) | |
1161 return self.__class__(yr, mo, dy, hr, mn, sc, tz, t, | |
1162 self._d, self.time, micros, tznaive) | |
1163 | |
1164 def isFuture(self): | |
1165 """Return true if this object represents a date/time | |
1166 later than the time of the call. | |
1167 """ | |
1168 return (self._t > time()) | |
1169 | |
1170 def isPast(self): | |
1171 """Return true if this object represents a date/time | |
1172 earlier than the time of the call. | |
1173 """ | |
1174 return (self._t < time()) | |
1175 | |
1176 def isCurrentYear(self): | |
1177 """Return true if this object represents a date/time | |
1178 that falls within the current year, in the context | |
1179 of this object's timezone representation. | |
1180 """ | |
1181 t = time() | |
1182 return safegmtime(t + _tzoffset(self._tz, t))[0] == self._year | |
1183 | |
1184 def isCurrentMonth(self): | |
1185 """Return true if this object represents a date/time | |
1186 that falls within the current month, in the context | |
1187 of this object's timezone representation. | |
1188 """ | |
1189 t = time() | |
1190 gmt = safegmtime(t + _tzoffset(self._tz, t)) | |
1191 return gmt[0] == self._year and gmt[1] == self._month | |
1192 | |
1193 def isCurrentDay(self): | |
1194 """Return true if this object represents a date/time | |
1195 that falls within the current day, in the context | |
1196 of this object's timezone representation. | |
1197 """ | |
1198 t = time() | |
1199 gmt = safegmtime(t + _tzoffset(self._tz, t)) | |
1200 return (gmt[0] == self._year and gmt[1] == self._month and | |
1201 gmt[2] == self._day) | |
1202 | |
1203 def isCurrentHour(self): | |
1204 """Return true if this object represents a date/time | |
1205 that falls within the current hour, in the context | |
1206 of this object's timezone representation. | |
1207 """ | |
1208 t = time() | |
1209 gmt = safegmtime(t + _tzoffset(self._tz, t)) | |
1210 return (gmt[0] == self._year and gmt[1] == self._month and | |
1211 gmt[2] == self._day and gmt[3] == self._hour) | |
1212 | |
1213 def isCurrentMinute(self): | |
1214 """Return true if this object represents a date/time | |
1215 that falls within the current minute, in the context | |
1216 of this object's timezone representation. | |
1217 """ | |
1218 t = time() | |
1219 gmt = safegmtime(t + _tzoffset(self._tz, t)) | |
1220 return (gmt[0] == self._year and gmt[1] == self._month and | |
1221 gmt[2] == self._day and gmt[3] == self._hour and | |
1222 gmt[4] == self._minute) | |
1223 | |
1224 def earliestTime(self): | |
1225 """Return a new DateTime object that represents the earliest | |
1226 possible time (in whole seconds) that still falls within | |
1227 the current object's day, in the object's timezone context. | |
1228 """ | |
1229 return self.__class__( | |
1230 self._year, self._month, self._day, 0, 0, 0, self._tz) | |
1231 | |
1232 def latestTime(self): | |
1233 """Return a new DateTime object that represents the latest | |
1234 possible time (in whole seconds) that still falls within | |
1235 the current object's day, in the object's timezone context. | |
1236 """ | |
1237 return self.__class__( | |
1238 self._year, self._month, self._day, 23, 59, 59, self._tz) | |
1239 | |
1240 def greaterThan(self, t): | |
1241 """Compare this DateTime object to another DateTime object | |
1242 OR a floating point number such as that which is returned | |
1243 by the Python time module. | |
1244 | |
1245 Returns true if the object represents a date/time greater | |
1246 than the specified DateTime or time module style time. | |
1247 | |
1248 Revised to give more correct results through comparison of | |
1249 long integer microseconds. | |
1250 """ | |
1251 if t is None: | |
1252 return True | |
1253 if isinstance(t, (float, int)): | |
1254 return self._micros > long(t * 1000000) | |
1255 try: | |
1256 return self._micros > t._micros | |
1257 except AttributeError: | |
1258 return self._micros > t | |
1259 | |
1260 __gt__ = greaterThan | |
1261 | |
1262 def greaterThanEqualTo(self, t): | |
1263 """Compare this DateTime object to another DateTime object | |
1264 OR a floating point number such as that which is returned | |
1265 by the Python time module. | |
1266 | |
1267 Returns true if the object represents a date/time greater | |
1268 than or equal to the specified DateTime or time module style | |
1269 time. | |
1270 | |
1271 Revised to give more correct results through comparison of | |
1272 long integer microseconds. | |
1273 """ | |
1274 if t is None: | |
1275 return True | |
1276 if isinstance(t, (float, int)): | |
1277 return self._micros >= long(t * 1000000) | |
1278 try: | |
1279 return self._micros >= t._micros | |
1280 except AttributeError: | |
1281 return self._micros >= t | |
1282 | |
1283 __ge__ = greaterThanEqualTo | |
1284 | |
1285 def equalTo(self, t): | |
1286 """Compare this DateTime object to another DateTime object | |
1287 OR a floating point number such as that which is returned | |
1288 by the Python time module. | |
1289 | |
1290 Returns true if the object represents a date/time equal to | |
1291 the specified DateTime or time module style time. | |
1292 | |
1293 Revised to give more correct results through comparison of | |
1294 long integer microseconds. | |
1295 """ | |
1296 if t is None: | |
1297 return False | |
1298 if isinstance(t, (float, int)): | |
1299 return self._micros == long(t * 1000000) | |
1300 try: | |
1301 return self._micros == t._micros | |
1302 except AttributeError: | |
1303 return self._micros == t | |
1304 | |
1305 def notEqualTo(self, t): | |
1306 """Compare this DateTime object to another DateTime object | |
1307 OR a floating point number such as that which is returned | |
1308 by the Python time module. | |
1309 | |
1310 Returns true if the object represents a date/time not equal | |
1311 to the specified DateTime or time module style time. | |
1312 | |
1313 Revised to give more correct results through comparison of | |
1314 long integer microseconds. | |
1315 """ | |
1316 return not self.equalTo(t) | |
1317 | |
1318 def __eq__(self, t): | |
1319 """Compare this DateTime object to another DateTime object. | |
1320 Return True if their internal state is the same. Two objects | |
1321 representing the same time in different timezones are regared as | |
1322 unequal. Use the equalTo method if you are only interested in them | |
1323 referring to the same moment in time. | |
1324 """ | |
1325 if not isinstance(t, DateTime): | |
1326 return False | |
1327 return (self._micros, self._tz) == (t._micros, t._tz) | |
1328 | |
1329 def __ne__(self, t): | |
1330 return not self.__eq__(t) | |
1331 | |
1332 def lessThan(self, t): | |
1333 """Compare this DateTime object to another DateTime object | |
1334 OR a floating point number such as that which is returned | |
1335 by the Python time module. | |
1336 | |
1337 Returns true if the object represents a date/time less than | |
1338 the specified DateTime or time module style time. | |
1339 | |
1340 Revised to give more correct results through comparison of | |
1341 long integer microseconds. | |
1342 """ | |
1343 if t is None: | |
1344 return False | |
1345 if isinstance(t, (float, int)): | |
1346 return self._micros < long(t * 1000000) | |
1347 try: | |
1348 return self._micros < t._micros | |
1349 except AttributeError: | |
1350 return self._micros < t | |
1351 | |
1352 __lt__ = lessThan | |
1353 | |
1354 def lessThanEqualTo(self, t): | |
1355 """Compare this DateTime object to another DateTime object | |
1356 OR a floating point number such as that which is returned | |
1357 by the Python time module. | |
1358 | |
1359 Returns true if the object represents a date/time less than | |
1360 or equal to the specified DateTime or time module style time. | |
1361 | |
1362 Revised to give more correct results through comparison of | |
1363 long integer microseconds. | |
1364 """ | |
1365 if t is None: | |
1366 return False | |
1367 if isinstance(t, (float, int)): | |
1368 return self._micros <= long(t * 1000000) | |
1369 try: | |
1370 return self._micros <= t._micros | |
1371 except AttributeError: | |
1372 return self._micros <= t | |
1373 | |
1374 __le__ = lessThanEqualTo | |
1375 | |
1376 def isLeapYear(self): | |
1377 """Return true if the current year (in the context of the | |
1378 object's timezone) is a leap year. | |
1379 """ | |
1380 return (self._year % 4 == 0 and | |
1381 (self._year % 100 != 0 or self._year % 400 == 0)) | |
1382 | |
1383 def dayOfYear(self): | |
1384 """Return the day of the year, in context of the timezone | |
1385 representation of the object. | |
1386 """ | |
1387 d = int(self._d + (_tzoffset(self._tz, self._t) / 86400.0)) | |
1388 return int((d + jd1901) - _julianday(self._year, 1, 0)) | |
1389 | |
1390 # Component access | |
1391 def parts(self): | |
1392 """Return a tuple containing the calendar year, month, | |
1393 day, hour, minute second and timezone of the object. | |
1394 """ | |
1395 return (self._year, self._month, self._day, self._hour, | |
1396 self._minute, self._second, self._tz) | |
1397 | |
1398 def timezone(self): | |
1399 """Return the timezone in which the object is represented.""" | |
1400 return self._tz | |
1401 | |
1402 def tzoffset(self): | |
1403 """Return the timezone offset for the objects timezone.""" | |
1404 return _tzoffset(self._tz, self._t) | |
1405 | |
1406 def year(self): | |
1407 """Return the calendar year of the object.""" | |
1408 return self._year | |
1409 | |
1410 def month(self): | |
1411 """Return the month of the object as an integer.""" | |
1412 return self._month | |
1413 | |
1414 @property | |
1415 def _fmon(self): | |
1416 return _MONTHS[self._month] | |
1417 | |
1418 def Month(self): | |
1419 """Return the full month name.""" | |
1420 return self._fmon | |
1421 | |
1422 @property | |
1423 def _amon(self): | |
1424 return _MONTHS_A[self._month] | |
1425 | |
1426 def aMonth(self): | |
1427 """Return the abbreviated month name.""" | |
1428 return self._amon | |
1429 | |
1430 def Mon(self): | |
1431 """Compatibility: see aMonth.""" | |
1432 return self._amon | |
1433 | |
1434 @property | |
1435 def _pmon(self): | |
1436 return _MONTHS_P[self._month] | |
1437 | |
1438 def pMonth(self): | |
1439 """Return the abbreviated (with period) month name.""" | |
1440 return self._pmon | |
1441 | |
1442 def Mon_(self): | |
1443 """Compatibility: see pMonth.""" | |
1444 return self._pmon | |
1445 | |
1446 def day(self): | |
1447 """Return the integer day.""" | |
1448 return self._day | |
1449 | |
1450 @property | |
1451 def _fday(self): | |
1452 return _DAYS[self._dayoffset] | |
1453 | |
1454 def Day(self): | |
1455 """Return the full name of the day of the week.""" | |
1456 return self._fday | |
1457 | |
1458 def DayOfWeek(self): | |
1459 """Compatibility: see Day.""" | |
1460 return self._fday | |
1461 | |
1462 @property | |
1463 def _aday(self): | |
1464 return _DAYS_A[self._dayoffset] | |
1465 | |
1466 def aDay(self): | |
1467 """Return the abbreviated name of the day of the week.""" | |
1468 return self._aday | |
1469 | |
1470 @property | |
1471 def _pday(self): | |
1472 return _DAYS_P[self._dayoffset] | |
1473 | |
1474 def pDay(self): | |
1475 """Return the abbreviated (with period) name of the day of the week.""" | |
1476 return self._pday | |
1477 | |
1478 def Day_(self): | |
1479 """Compatibility: see pDay.""" | |
1480 return self._pday | |
1481 | |
1482 def dow(self): | |
1483 """Return the integer day of the week, where Sunday is 0.""" | |
1484 return self._dayoffset | |
1485 | |
1486 def dow_1(self): | |
1487 """Return the integer day of the week, where Sunday is 1.""" | |
1488 return self._dayoffset + 1 | |
1489 | |
1490 @property | |
1491 def _pmhour(self): | |
1492 hr = self._hour | |
1493 if hr > 12: | |
1494 return hr - 12 | |
1495 return hr or 12 | |
1496 | |
1497 def h_12(self): | |
1498 """Return the 12-hour clock representation of the hour.""" | |
1499 return self._pmhour | |
1500 | |
1501 def h_24(self): | |
1502 """Return the 24-hour clock representation of the hour.""" | |
1503 return self._hour | |
1504 | |
1505 @property | |
1506 def _pm(self): | |
1507 hr = self._hour | |
1508 if hr >= 12: | |
1509 return 'pm' | |
1510 return 'am' | |
1511 | |
1512 def ampm(self): | |
1513 """Return the appropriate time modifier (am or pm).""" | |
1514 return self._pm | |
1515 | |
1516 def hour(self): | |
1517 """Return the 24-hour clock representation of the hour.""" | |
1518 return self._hour | |
1519 | |
1520 def minute(self): | |
1521 """Return the minute.""" | |
1522 return self._minute | |
1523 | |
1524 def second(self): | |
1525 """Return the second.""" | |
1526 return self._second | |
1527 | |
1528 def millis(self): | |
1529 """Return the millisecond since the epoch in GMT.""" | |
1530 return self._micros // 1000 | |
1531 | |
1532 def micros(self): | |
1533 """Return the microsecond since the epoch in GMT.""" | |
1534 return self._micros | |
1535 | |
1536 def timezoneNaive(self): | |
1537 """The Python datetime module introduces the idea of distinguishing | |
1538 between timezone aware and timezone naive datetime values. For lossless | |
1539 conversion to and from datetime.datetime we record this | |
1540 information using True / False. DateTime makes no distinction, if we | |
1541 don't have any information we return None here. | |
1542 """ | |
1543 try: | |
1544 return self._timezone_naive | |
1545 except AttributeError: | |
1546 return None | |
1547 | |
1548 def strftime(self, format): | |
1549 """Format the date/time using the *current timezone representation*.""" | |
1550 x = _calcDependentSecond2(self._year, self._month, self._day, | |
1551 self._hour, self._minute, self._second) | |
1552 ltz = self._calcTimezoneName(x, 0) | |
1553 tzdiff = _tzoffset(ltz, self._t) - _tzoffset(self._tz, self._t) | |
1554 zself = self + tzdiff / 86400.0 | |
1555 microseconds = int((zself._second - zself._nearsec) * 1000000) | |
1556 unicode_format = False | |
1557 if isinstance(format, explicit_unicode_type): | |
1558 format = format.encode('utf-8') | |
1559 unicode_format = True | |
1560 ds = datetime(zself._year, zself._month, zself._day, zself._hour, | |
1561 zself._minute, int(zself._nearsec), | |
1562 microseconds).strftime(format) | |
1563 if unicode_format: | |
1564 return ds.decode('utf-8') | |
1565 return ds | |
1566 | |
1567 # General formats from previous DateTime | |
1568 def Date(self): | |
1569 """Return the date string for the object.""" | |
1570 return "%s/%2.2d/%2.2d" % (self._year, self._month, self._day) | |
1571 | |
1572 def Time(self): | |
1573 """Return the time string for an object to the nearest second.""" | |
1574 return '%2.2d:%2.2d:%2.2d' % (self._hour, self._minute, self._nearsec) | |
1575 | |
1576 def TimeMinutes(self): | |
1577 """Return the time string for an object not showing seconds.""" | |
1578 return '%2.2d:%2.2d' % (self._hour, self._minute) | |
1579 | |
1580 def AMPM(self): | |
1581 """Return the time string for an object to the nearest second.""" | |
1582 return '%2.2d:%2.2d:%2.2d %s' % ( | |
1583 self._pmhour, self._minute, self._nearsec, self._pm) | |
1584 | |
1585 def AMPMMinutes(self): | |
1586 """Return the time string for an object not showing seconds.""" | |
1587 return '%2.2d:%2.2d %s' % (self._pmhour, self._minute, self._pm) | |
1588 | |
1589 def PreciseTime(self): | |
1590 """Return the time string for the object.""" | |
1591 return '%2.2d:%2.2d:%06.3f' % (self._hour, self._minute, self._second) | |
1592 | |
1593 def PreciseAMPM(self): | |
1594 """Return the time string for the object.""" | |
1595 return '%2.2d:%2.2d:%06.3f %s' % ( | |
1596 self._pmhour, self._minute, self._second, self._pm) | |
1597 | |
1598 def yy(self): | |
1599 """Return calendar year as a 2 digit string.""" | |
1600 return str(self._year)[-2:] | |
1601 | |
1602 def mm(self): | |
1603 """Return month as a 2 digit string.""" | |
1604 return '%02d' % self._month | |
1605 | |
1606 def dd(self): | |
1607 """Return day as a 2 digit string.""" | |
1608 return '%02d' % self._day | |
1609 | |
1610 def rfc822(self): | |
1611 """Return the date in RFC 822 format.""" | |
1612 tzoffset = _tzoffset2rfc822zone(_tzoffset(self._tz, self._t)) | |
1613 return '%s, %2.2d %s %d %2.2d:%2.2d:%2.2d %s' % ( | |
1614 self._aday, self._day, self._amon, self._year, | |
1615 self._hour, self._minute, self._nearsec, tzoffset) | |
1616 | |
1617 # New formats | |
1618 def fCommon(self): | |
1619 """Return a string representing the object's value | |
1620 in the format: March 1, 1997 1:45 pm. | |
1621 """ | |
1622 return '%s %s, %4.4d %s:%2.2d %s' % ( | |
1623 self._fmon, self._day, self._year, self._pmhour, | |
1624 self._minute, self._pm) | |
1625 | |
1626 def fCommonZ(self): | |
1627 """Return a string representing the object's value | |
1628 in the format: March 1, 1997 1:45 pm US/Eastern. | |
1629 """ | |
1630 return '%s %s, %4.4d %d:%2.2d %s %s' % ( | |
1631 self._fmon, self._day, self._year, self._pmhour, | |
1632 self._minute, self._pm, self._tz) | |
1633 | |
1634 def aCommon(self): | |
1635 """Return a string representing the object's value | |
1636 in the format: Mar 1, 1997 1:45 pm. | |
1637 """ | |
1638 return '%s %s, %4.4d %s:%2.2d %s' % ( | |
1639 self._amon, self._day, self._year, self._pmhour, | |
1640 self._minute, self._pm) | |
1641 | |
1642 def aCommonZ(self): | |
1643 """Return a string representing the object's value | |
1644 in the format: Mar 1, 1997 1:45 pm US/Eastern. | |
1645 """ | |
1646 return '%s %s, %4.4d %d:%2.2d %s %s' % ( | |
1647 self._amon, self._day, self._year, self._pmhour, | |
1648 self._minute, self._pm, self._tz) | |
1649 | |
1650 def pCommon(self): | |
1651 """Return a string representing the object's value | |
1652 in the format: Mar. 1, 1997 1:45 pm. | |
1653 """ | |
1654 return '%s %s, %4.4d %s:%2.2d %s' % ( | |
1655 self._pmon, self._day, self._year, self._pmhour, | |
1656 self._minute, self._pm) | |
1657 | |
1658 def pCommonZ(self): | |
1659 """Return a string representing the object's value | |
1660 in the format: Mar. 1, 1997 1:45 pm US/Eastern. | |
1661 """ | |
1662 return '%s %s, %4.4d %d:%2.2d %s %s' % ( | |
1663 self._pmon, self._day, self._year, self._pmhour, | |
1664 self._minute, self._pm, self._tz) | |
1665 | |
1666 def ISO(self): | |
1667 """Return the object in ISO standard format. | |
1668 | |
1669 Note: this is *not* ISO 8601-format! See the ISO8601 and | |
1670 HTML4 methods below for ISO 8601-compliant output. | |
1671 | |
1672 Dates are output as: YYYY-MM-DD HH:MM:SS | |
1673 """ | |
1674 return "%.4d-%.2d-%.2d %.2d:%.2d:%.2d" % ( | |
1675 self._year, self._month, self._day, | |
1676 self._hour, self._minute, self._second) | |
1677 | |
1678 def ISO8601(self): | |
1679 """Return the object in ISO 8601-compatible format containing the | |
1680 date, time with seconds-precision and the time zone identifier. | |
1681 | |
1682 See: http://www.w3.org/TR/NOTE-datetime | |
1683 | |
1684 Dates are output as: YYYY-MM-DDTHH:MM:SSTZD | |
1685 T is a literal character. | |
1686 TZD is Time Zone Designator, format +HH:MM or -HH:MM | |
1687 | |
1688 If the instance is timezone naive (it was not specified with a timezone | |
1689 when it was constructed) then the timezone is omitted. | |
1690 | |
1691 The HTML4 method below offers the same formatting, but converts | |
1692 to UTC before returning the value and sets the TZD "Z". | |
1693 """ | |
1694 if self.timezoneNaive(): | |
1695 return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d" % ( | |
1696 self._year, self._month, self._day, | |
1697 self._hour, self._minute, self._second) | |
1698 tzoffset = _tzoffset2iso8601zone(_tzoffset(self._tz, self._t)) | |
1699 return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2d%s" % ( | |
1700 self._year, self._month, self._day, | |
1701 self._hour, self._minute, self._second, tzoffset) | |
1702 | |
1703 def HTML4(self): | |
1704 """Return the object in the format used in the HTML4.0 specification, | |
1705 one of the standard forms in ISO8601. | |
1706 | |
1707 See: http://www.w3.org/TR/NOTE-datetime | |
1708 | |
1709 Dates are output as: YYYY-MM-DDTHH:MM:SSZ | |
1710 T, Z are literal characters. | |
1711 The time is in UTC. | |
1712 """ | |
1713 newdate = self.toZone('UTC') | |
1714 return "%0.4d-%0.2d-%0.2dT%0.2d:%0.2d:%0.2dZ" % ( | |
1715 newdate._year, newdate._month, newdate._day, | |
1716 newdate._hour, newdate._minute, newdate._second) | |
1717 | |
1718 def asdatetime(self): | |
1719 """Return a standard library datetime.datetime | |
1720 """ | |
1721 tznaive = self.timezoneNaive() | |
1722 if tznaive: | |
1723 tzinfo = None | |
1724 else: | |
1725 tzinfo = _TZINFO[self._tz].tzinfo | |
1726 second = int(self._second) | |
1727 microsec = self.micros() % 1000000 | |
1728 dt = datetime(self._year, self._month, self._day, self._hour, | |
1729 self._minute, second, microsec, tzinfo) | |
1730 return dt | |
1731 | |
1732 def utcdatetime(self): | |
1733 """Convert the time to UTC and return a timezone naive datetime object | |
1734 """ | |
1735 utc = self.toZone('UTC') | |
1736 second = int(utc._second) | |
1737 microsec = utc.micros() % 1000000 | |
1738 dt = datetime(utc._year, utc._month, utc._day, utc._hour, | |
1739 utc._minute, second, microsec) | |
1740 return dt | |
1741 | |
1742 def __add__(self, other): | |
1743 """A DateTime may be added to a number and a number may be | |
1744 added to a DateTime; two DateTimes cannot be added. | |
1745 """ | |
1746 if hasattr(other, '_t'): | |
1747 raise DateTimeError('Cannot add two DateTimes') | |
1748 o = float(other) | |
1749 tz = self._tz | |
1750 omicros = round(o * 86400000000) | |
1751 tmicros = self.micros() + omicros | |
1752 t = tmicros / 1000000.0 | |
1753 d = (tmicros + long(EPOCH * 1000000)) / 86400000000.0 | |
1754 s = d - math.floor(d) | |
1755 ms = t - math.floor(t) | |
1756 x = _calcDependentSecond(tz, t) | |
1757 yr, mo, dy, hr, mn, sc = _calcYMDHMS(x, ms) | |
1758 return self.__class__(yr, mo, dy, hr, mn, sc, self._tz, | |
1759 t, d, s, tmicros, self.timezoneNaive()) | |
1760 | |
1761 __radd__ = __add__ | |
1762 | |
1763 def __sub__(self, other): | |
1764 """Either a DateTime or a number may be subtracted from a | |
1765 DateTime, however, a DateTime may not be subtracted from | |
1766 a number. | |
1767 """ | |
1768 if hasattr(other, '_d'): | |
1769 return (self.micros() - other.micros()) / 86400000000.0 | |
1770 else: | |
1771 return self.__add__(-(other)) | |
1772 | |
1773 def __repr__(self): | |
1774 """Convert a DateTime to a string that looks like a Python | |
1775 expression. | |
1776 """ | |
1777 return '{}(\'{}\')'.format(self.__class__.__name__, str(self)) | |
1778 | |
1779 def __str__(self): | |
1780 """Convert a DateTime to a string.""" | |
1781 y, m, d = self._year, self._month, self._day | |
1782 h, mn, s, t = self._hour, self._minute, self._second, self._tz | |
1783 if s == int(s): | |
1784 # A whole number of seconds -- suppress milliseconds. | |
1785 return '%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%2.2d %s' % ( | |
1786 y, m, d, h, mn, s, t) | |
1787 else: | |
1788 # s is already rounded to the nearest microsecond, and | |
1789 # it's not a whole number of seconds. Be sure to print | |
1790 # 2 digits before the decimal point. | |
1791 return '%4.4d/%2.2d/%2.2d %2.2d:%2.2d:%06.6f %s' % ( | |
1792 y, m, d, h, mn, s, t) | |
1793 | |
1794 def __format__(self, fmt): | |
1795 """Render a DateTime in an f-string.""" | |
1796 if not isinstance(fmt, str): | |
1797 raise TypeError("must be str, not %s" % type(fmt).__name__) | |
1798 if len(fmt) != 0: | |
1799 return self.strftime(fmt) | |
1800 return str(self) | |
1801 | |
1802 def __hash__(self): | |
1803 """Compute a hash value for a DateTime.""" | |
1804 return int(((self._year % 100 * 12 + self._month) * 31 + | |
1805 self._day + self.time) * 100) | |
1806 | |
1807 def __int__(self): | |
1808 """Convert to an integer number of seconds since the epoch (gmt).""" | |
1809 return int(self.micros() // 1000000) | |
1810 | |
1811 def __long__(self): | |
1812 """Convert to a long-int number of seconds since the epoch (gmt).""" | |
1813 return long(self.micros() // 1000000) # pragma: PY2 | |
1814 | |
1815 def __float__(self): | |
1816 """Convert to floating-point number of seconds since the epoch (gmt). | |
1817 """ | |
1818 return self.micros() / 1000000.0 | |
1819 | |
1820 @property | |
1821 def _t(self): | |
1822 return self._micros / 1000000.0 | |
1823 | |
1824 def _parse_iso8601(self, s): | |
1825 # preserve the previously implied contract | |
1826 # who knows where this could be used... | |
1827 return self._parse_iso8601_preserving_tznaive(s)[:7] | |
1828 | |
1829 def _parse_iso8601_preserving_tznaive(self, s): | |
1830 try: | |
1831 return self.__parse_iso8601(s) | |
1832 except IndexError: | |
1833 raise SyntaxError( | |
1834 'Not an ISO 8601 compliant date string: "%s"' % s) | |
1835 | |
1836 def __parse_iso8601(self, s): | |
1837 """Parse an ISO 8601 compliant date. | |
1838 | |
1839 See: http://en.wikipedia.org/wiki/ISO_8601 | |
1840 """ | |
1841 month = day = week_day = 1 | |
1842 year = hour = minute = seconds = hour_off = min_off = 0 | |
1843 tznaive = True | |
1844 | |
1845 iso8601 = iso8601Match(s.strip()) | |
1846 fields = iso8601 and iso8601.groupdict() or {} | |
1847 if not iso8601 or fields.get('garbage'): | |
1848 raise IndexError | |
1849 | |
1850 if fields['year']: | |
1851 year = int(fields['year']) | |
1852 if fields['month']: | |
1853 month = int(fields['month']) | |
1854 if fields['day']: | |
1855 day = int(fields['day']) | |
1856 | |
1857 if fields['year_day']: | |
1858 d = DateTime('%s-01-01' % year) + int(fields['year_day']) - 1 | |
1859 month = d.month() | |
1860 day = d.day() | |
1861 | |
1862 if fields['week']: | |
1863 week = int(fields['week']) | |
1864 if fields['week_day']: | |
1865 week_day = int(fields['week_day']) | |
1866 d = DateTime('%s-01-04' % year) | |
1867 d = d - (d.dow() + 6) % 7 + week * 7 + week_day - 8 | |
1868 month = d.month() | |
1869 day = d.day() | |
1870 | |
1871 if fields['hour']: | |
1872 hour = int(fields['hour']) | |
1873 | |
1874 if fields['minute']: | |
1875 minute = int(fields['minute']) | |
1876 elif fields['fraction']: | |
1877 minute = 60.0 * float('0.%s' % fields['fraction']) | |
1878 seconds, minute = math.modf(minute) | |
1879 minute = int(minute) | |
1880 seconds = 60.0 * seconds | |
1881 # Avoid reprocess when handling seconds, bellow | |
1882 fields['fraction'] = None | |
1883 | |
1884 if fields['second']: | |
1885 seconds = int(fields['second']) | |
1886 if fields['fraction']: | |
1887 seconds = seconds + float('0.%s' % fields['fraction']) | |
1888 elif fields['fraction']: | |
1889 seconds = 60.0 * float('0.%s' % fields['fraction']) | |
1890 | |
1891 if fields['hour_off']: | |
1892 hour_off = int(fields['hour_off']) | |
1893 if fields['signal'] == '-': | |
1894 hour_off *= -1 | |
1895 | |
1896 if fields['min_off']: | |
1897 min_off = int(fields['min_off']) | |
1898 | |
1899 if fields['signal'] or fields['Z']: | |
1900 tznaive = False | |
1901 else: | |
1902 tznaive = True | |
1903 | |
1904 # Differ from the specification here. To preserve backwards | |
1905 # compatibility assume a default timezone == UTC. | |
1906 tz = 'GMT%+03d%02d' % (hour_off, min_off) | |
1907 | |
1908 return year, month, day, hour, minute, seconds, tz, tznaive | |
1909 | |
1910 def JulianDay(self): | |
1911 """Return the Julian day. | |
1912 | |
1913 See: https://www.tondering.dk/claus/cal/julperiod.php#formula | |
1914 """ | |
1915 a = (14 - self._month) // 12 | |
1916 y = self._year + 4800 - a | |
1917 m = self._month + (12 * a) - 3 | |
1918 return (self._day + (153 * m + 2) // 5 + 365 * y + | |
1919 y // 4 - y // 100 + y // 400 - 32045) | |
1920 | |
1921 def week(self): | |
1922 """Return the week number according to ISO. | |
1923 | |
1924 See: https://www.tondering.dk/claus/cal/week.php#weekno | |
1925 """ | |
1926 J = self.JulianDay() | |
1927 d4 = (J + 31741 - (J % 7)) % 146097 % 36524 % 1461 | |
1928 L = d4 // 1460 | |
1929 d1 = ((d4 - L) % 365) + L | |
1930 return d1 // 7 + 1 | |
1931 | |
1932 def encode(self, out): | |
1933 """Encode value for XML-RPC.""" | |
1934 out.write('<value><dateTime.iso8601>') | |
1935 out.write(self.ISO8601()) | |
1936 out.write('</dateTime.iso8601></value>\n') | |
1937 | |
1938 | |
1939 # Provide the _dt_reconstructor function here, in case something | |
1940 # accidentally creates a reference to this function | |
1941 | |
1942 orig_reconstructor = copy_reg._reconstructor | |
1943 | |
1944 | |
1945 def _dt_reconstructor(cls, base, state): | |
1946 if cls is DateTime: | |
1947 return cls(state) | |
1948 return orig_reconstructor(cls, base, state) |