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)