DateTime.py 69 KB

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