test_datetime.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. ##############################################################################
  2. #
  3. # Copyright (c) 2003 Zope Foundation and Contributors.
  4. # All Rights Reserved.
  5. #
  6. # This software is subject to the provisions of the Zope Public License,
  7. # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
  8. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  9. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  10. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  11. # FOR A PARTICULAR PURPOSE.
  12. #
  13. ##############################################################################
  14. from datetime import date, datetime, tzinfo, timedelta
  15. import math
  16. import platform
  17. import os
  18. import sys
  19. import time
  20. import unittest
  21. import pytz
  22. from DateTime.DateTime import _findLocalTimeZoneName
  23. from DateTime import DateTime
  24. if sys.version_info > (3, ):
  25. import pickle
  26. unicode = str
  27. PY3K = True
  28. else:
  29. import cPickle as pickle
  30. PY3K = False
  31. try:
  32. __file__
  33. except NameError:
  34. f = sys.argv[0]
  35. else:
  36. f = __file__
  37. IS_PYPY = getattr(platform, 'python_implementation', lambda: None)() == 'PyPy'
  38. DATADIR = os.path.dirname(os.path.abspath(f))
  39. del f
  40. ZERO = timedelta(0)
  41. class FixedOffset(tzinfo):
  42. """Fixed offset in minutes east from UTC."""
  43. def __init__(self, offset, name):
  44. self.__offset = timedelta(minutes=offset)
  45. self.__name = name
  46. def utcoffset(self, dt):
  47. return self.__offset
  48. def tzname(self, dt):
  49. return self.__name
  50. def dst(self, dt):
  51. return ZERO
  52. class DateTimeTests(unittest.TestCase):
  53. def _compare(self, dt1, dt2):
  54. '''Compares the internal representation of dt1 with
  55. the representation in dt2. Allows sub-millisecond variations.
  56. Primarily for testing.'''
  57. self.assertEqual(round(dt1._t, 3), round(dt2._t, 3))
  58. self.assertEqual(round(dt1._d, 9), round(dt2._d, 9))
  59. self.assertEqual(round(dt1.time, 9), round(dt2.time, 9))
  60. self.assertEqual(dt1.millis(), dt2.millis())
  61. self.assertEqual(dt1._micros, dt2._micros)
  62. def testBug1203(self):
  63. # 01:59:60 occurred in old DateTime
  64. dt = DateTime(7200, 'GMT')
  65. self.assertTrue(str(dt).find('60') < 0, dt)
  66. def testDSTInEffect(self):
  67. # Checks GMT offset for a DST date in the US/Eastern time zone
  68. dt = DateTime(2000, 5, 9, 15, 0, 0, 'US/Eastern')
  69. self.assertEqual(dt.toZone('GMT').hour(), 19,
  70. (dt, dt.toZone('GMT')))
  71. def testDSTNotInEffect(self):
  72. # Checks GMT offset for a non-DST date in the US/Eastern time zone
  73. dt = DateTime(2000, 11, 9, 15, 0, 0, 'US/Eastern')
  74. self.assertEqual(dt.toZone('GMT').hour(), 20,
  75. (dt, dt.toZone('GMT')))
  76. def testAddPrecision(self):
  77. # Precision of serial additions
  78. dt = DateTime()
  79. self.assertEqual(str(dt + 0.10 + 3.14 + 6.76 - 10), str(dt),
  80. dt)
  81. def testConstructor3(self):
  82. # Constructor from date/time string
  83. dt = DateTime()
  84. dt1s = '%d/%d/%d %d:%d:%f %s' % (
  85. dt.year(),
  86. dt.month(),
  87. dt.day(),
  88. dt.hour(),
  89. dt.minute(),
  90. dt.second(),
  91. dt.timezone())
  92. dt1 = DateTime(dt1s)
  93. # Compare representations as it's the
  94. # only way to compare the dates to the same accuracy
  95. self.assertEqual(repr(dt), repr(dt1))
  96. def testConstructor4(self):
  97. # Constructor from time float
  98. dt = DateTime()
  99. dt1 = DateTime(float(dt))
  100. self._compare(dt, dt1)
  101. def testConstructor5(self):
  102. # Constructor from time float and timezone
  103. dt = DateTime()
  104. dt1 = DateTime(float(dt), dt.timezone())
  105. self.assertEqual(str(dt), str(dt1), (dt, dt1))
  106. dt1 = DateTime(float(dt), unicode(dt.timezone()))
  107. self.assertEqual(str(dt), str(dt1), (dt, dt1))
  108. def testConstructor6(self):
  109. # Constructor from year and julian date
  110. # This test must normalize the time zone, or it *will* break when
  111. # DST changes!
  112. dt1 = DateTime(2000, 5.500000578705)
  113. dt = DateTime('2000/1/5 12:00:00.050 pm %s' % dt1.localZone())
  114. self._compare(dt, dt1)
  115. def testConstructor7(self):
  116. # Constructor from parts
  117. dt = DateTime()
  118. dt1 = DateTime(
  119. dt.year(),
  120. dt.month(),
  121. dt.day(),
  122. dt.hour(),
  123. dt.minute(),
  124. dt.second(),
  125. dt.timezone())
  126. # Compare representations as it's the
  127. # only way to compare the dates to the same accuracy
  128. self.assertEqual(repr(dt), repr(dt1))
  129. def testDayOfWeek(self):
  130. # Compare to the datetime.date value to make it locale independent
  131. expected = date(2000, 6, 16).strftime('%A')
  132. # strftime() used to always be passed a day of week of 0
  133. dt = DateTime('2000/6/16')
  134. s = dt.strftime('%A')
  135. self.assertEqual(s, expected, (dt, s))
  136. def testOldDate(self):
  137. # Fails when an 1800 date is displayed with negative signs
  138. dt = DateTime('1830/5/6 12:31:46.213 pm')
  139. dt1 = dt.toZone('GMT+6')
  140. self.assertTrue(str(dt1).find('-') < 0, (dt, dt1))
  141. def testSubtraction(self):
  142. # Reconstruction of a DateTime from its parts, with subtraction
  143. # this also tests the accuracy of addition and reconstruction
  144. dt = DateTime()
  145. dt1 = dt - 3.141592653
  146. dt2 = DateTime(
  147. dt.year(),
  148. dt.month(),
  149. dt.day(),
  150. dt.hour(),
  151. dt.minute(),
  152. dt.second())
  153. dt3 = dt2 - 3.141592653
  154. self.assertEqual(dt1, dt3, (dt, dt1, dt2, dt3))
  155. def testTZ1add(self):
  156. # Time zone manipulation: add to a date
  157. dt = DateTime('1997/3/8 1:45am GMT-4')
  158. dt1 = DateTime('1997/3/9 1:45pm GMT+8')
  159. self.assertTrue((dt + 1.0).equalTo(dt1))
  160. def testTZ1sub(self):
  161. # Time zone manipulation: subtract from a date
  162. dt = DateTime('1997/3/8 1:45am GMT-4')
  163. dt1 = DateTime('1997/3/9 1:45pm GMT+8')
  164. self.assertTrue((dt1 - 1.0).equalTo(dt))
  165. def testTZ1diff(self):
  166. # Time zone manipulation: diff two dates
  167. dt = DateTime('1997/3/8 1:45am GMT-4')
  168. dt1 = DateTime('1997/3/9 1:45pm GMT+8')
  169. self.assertEqual(dt1 - dt, 1.0, (dt, dt1))
  170. def test_compare_methods(self):
  171. # Compare two dates using several methods
  172. dt = DateTime('1997/1/1')
  173. dt1 = DateTime('1997/2/2')
  174. self.assertTrue(dt1.greaterThan(dt))
  175. self.assertTrue(dt1.greaterThanEqualTo(dt))
  176. self.assertTrue(dt.lessThan(dt1))
  177. self.assertTrue(dt.lessThanEqualTo(dt1))
  178. self.assertTrue(dt.notEqualTo(dt1))
  179. self.assertFalse(dt.equalTo(dt1))
  180. def test_compare_methods_none(self):
  181. # Compare a date to None
  182. dt = DateTime('1997/1/1')
  183. self.assertTrue(dt.greaterThan(None))
  184. self.assertTrue(dt.greaterThanEqualTo(None))
  185. self.assertFalse(dt.lessThan(None))
  186. self.assertFalse(dt.lessThanEqualTo(None))
  187. self.assertTrue(dt.notEqualTo(None))
  188. self.assertFalse(dt.equalTo(None))
  189. def test_pickle(self):
  190. dt = DateTime()
  191. data = pickle.dumps(dt, 1)
  192. new = pickle.loads(data)
  193. for key in DateTime.__slots__:
  194. self.assertEqual(getattr(dt, key), getattr(new, key))
  195. def test_pickle_with_tz(self):
  196. dt = DateTime('2002/5/2 8:00am GMT+8')
  197. data = pickle.dumps(dt, 1)
  198. new = pickle.loads(data)
  199. for key in DateTime.__slots__:
  200. self.assertEqual(getattr(dt, key), getattr(new, key))
  201. def test_pickle_with_numerical_tz(self):
  202. for dt_str in ('2007/01/02 12:34:56.789 +0300',
  203. '2007/01/02 12:34:56.789 +0430',
  204. '2007/01/02 12:34:56.789 -1234'):
  205. dt = DateTime(dt_str)
  206. data = pickle.dumps(dt, 1)
  207. new = pickle.loads(data)
  208. for key in DateTime.__slots__:
  209. self.assertEqual(getattr(dt, key), getattr(new, key))
  210. def test_pickle_with_micros(self):
  211. dt = DateTime('2002/5/2 8:00:14.123 GMT+8')
  212. data = pickle.dumps(dt, 1)
  213. new = pickle.loads(data)
  214. for key in DateTime.__slots__:
  215. self.assertEqual(getattr(dt, key), getattr(new, key))
  216. def test_pickle_old(self):
  217. dt = DateTime('2002/5/2 8:00am GMT+0')
  218. data = ('(cDateTime.DateTime\nDateTime\nq\x01Noq\x02}q\x03(U\x05'
  219. '_amonq\x04U\x03Mayq\x05U\x05_adayq\x06U\x03Thuq\x07U\x05_pmonq'
  220. '\x08h\x05U\x05_hourq\tK\x08U\x05_fmonq\nh\x05U\x05_pdayq\x0bU'
  221. '\x04Thu.q\x0cU\x05_fdayq\rU\x08Thursdayq\x0eU\x03_pmq\x0fU\x02amq'
  222. '\x10U\x02_tq\x11GA\xcehy\x00\x00\x00\x00U\x07_minuteq\x12K\x00U'
  223. '\x07_microsq\x13L1020326400000000L\nU\x02_dq\x14G@\xe2\x12j\xaa'
  224. '\xaa\xaa\xabU\x07_secondq\x15G\x00\x00\x00\x00\x00\x00\x00\x00U'
  225. '\x03_tzq\x16U\x05GMT+0q\x17U\x06_monthq\x18K\x05U'
  226. '\x0f_timezone_naiveq\x19I00\nU\x04_dayq\x1aK\x02U\x05_yearq'
  227. '\x1bM\xd2\x07U\x08_nearsecq\x1cG\x00\x00\x00\x00\x00\x00\x00'
  228. '\x00U\x07_pmhourq\x1dK\x08U\n_dayoffsetq\x1eK\x04U\x04timeq'
  229. '\x1fG?\xd5UUUV\x00\x00ub.')
  230. if PY3K:
  231. data = data.encode('latin-1')
  232. new = pickle.loads(data)
  233. for key in DateTime.__slots__:
  234. self.assertEqual(getattr(dt, key), getattr(new, key))
  235. def test_pickle_old_without_micros(self):
  236. dt = DateTime('2002/5/2 8:00am GMT+0')
  237. data = ('(cDateTime.DateTime\nDateTime\nq\x01Noq\x02}q\x03(U\x05'
  238. '_amonq\x04U\x03Mayq\x05U\x05_adayq\x06U\x03Thuq\x07U\x05_pmonq'
  239. '\x08h\x05U\x05_hourq\tK\x08U\x05_fmonq\nh\x05U\x05_pdayq\x0bU'
  240. '\x04Thu.q\x0cU\x05_fdayq\rU\x08Thursdayq\x0eU\x03_pmq\x0fU'
  241. '\x02amq\x10U\x02_tq\x11GA\xcehy\x00\x00\x00\x00U\x07_minuteq'
  242. '\x12K\x00U\x02_dq\x13G@\xe2\x12j\xaa\xaa\xaa\xabU\x07_secondq'
  243. '\x14G\x00\x00\x00\x00\x00\x00\x00\x00U\x03_tzq\x15U\x05GMT+0q'
  244. '\x16U\x06_monthq\x17K\x05U\x0f_timezone_naiveq\x18I00\nU'
  245. '\x04_dayq\x19K\x02U\x05_yearq\x1aM\xd2\x07U\x08_nearsecq'
  246. '\x1bG\x00\x00\x00\x00\x00\x00\x00\x00U\x07_pmhourq\x1cK\x08U'
  247. '\n_dayoffsetq\x1dK\x04U\x04timeq\x1eG?\xd5UUUV\x00\x00ub.')
  248. if PY3K:
  249. data = data.encode('latin-1')
  250. new = pickle.loads(data)
  251. for key in DateTime.__slots__:
  252. self.assertEqual(getattr(dt, key), getattr(new, key))
  253. def testTZ2(self):
  254. # Time zone manipulation test 2
  255. dt = DateTime()
  256. dt1 = dt.toZone('GMT')
  257. s = dt.second()
  258. s1 = dt1.second()
  259. self.assertEqual(s, s1, (dt, dt1, s, s1))
  260. def testTZDiffDaylight(self):
  261. # Diff dates across daylight savings dates
  262. dt = DateTime('2000/6/8 1:45am US/Eastern')
  263. dt1 = DateTime('2000/12/8 12:45am US/Eastern')
  264. self.assertEqual(dt1 - dt, 183, (dt, dt1, dt1 - dt))
  265. def testY10KDate(self):
  266. # Comparison of a Y10K date and a Y2K date
  267. dt = DateTime('10213/09/21')
  268. dt1 = DateTime(2000, 1, 1)
  269. dsec = (dt.millis() - dt1.millis()) / 1000.0
  270. ddays = math.floor((dsec / 86400.0) + 0.5)
  271. self.assertEqual(ddays, 3000000, ddays)
  272. def test_tzoffset(self):
  273. # Test time-zone given as an offset
  274. # GMT
  275. dt = DateTime('Tue, 10 Sep 2001 09:41:03 GMT')
  276. self.assertEqual(dt.tzoffset(), 0)
  277. # Timezone by name, a timezone that hasn't got daylightsaving.
  278. dt = DateTime('Tue, 2 Mar 2001 09:41:03 GMT+3')
  279. self.assertEqual(dt.tzoffset(), 10800)
  280. # Timezone by name, has daylightsaving but is not in effect.
  281. dt = DateTime('Tue, 21 Jan 2001 09:41:03 PST')
  282. self.assertEqual(dt.tzoffset(), -28800)
  283. # Timezone by name, with daylightsaving in effect
  284. dt = DateTime('Tue, 24 Aug 2001 09:41:03 PST')
  285. self.assertEqual(dt.tzoffset(), -25200)
  286. # A negative numerical timezone
  287. dt = DateTime('Tue, 24 Jul 2001 09:41:03 -0400')
  288. self.assertEqual(dt.tzoffset(), -14400)
  289. # A positive numerical timzone
  290. dt = DateTime('Tue, 6 Dec 1966 01:41:03 +0200')
  291. self.assertEqual(dt.tzoffset(), 7200)
  292. # A negative numerical timezone with minutes.
  293. dt = DateTime('Tue, 24 Jul 2001 09:41:03 -0637')
  294. self.assertEqual(dt.tzoffset(), -23820)
  295. # A positive numerical timezone with minutes.
  296. dt = DateTime('Tue, 24 Jul 2001 09:41:03 +0425')
  297. self.assertEqual(dt.tzoffset(), 15900)
  298. def testISO8601(self):
  299. # ISO8601 reference dates
  300. ref0 = DateTime('2002/5/2 8:00am GMT')
  301. ref1 = DateTime('2002/5/2 8:00am US/Eastern')
  302. ref2 = DateTime('2006/11/6 10:30 GMT')
  303. ref3 = DateTime('2004/06/14 14:30:15 GMT-3')
  304. ref4 = DateTime('2006/01/01 GMT')
  305. # Basic tests
  306. # Though this is timezone naive and according to specification should
  307. # be interpreted in the local timezone, to preserve backwards
  308. # compatibility with previously expected behaviour.
  309. isoDt = DateTime('2002-05-02T08:00:00')
  310. self.assertTrue(ref0.equalTo(isoDt))
  311. isoDt = DateTime('2002-05-02T08:00:00Z')
  312. self.assertTrue(ref0.equalTo(isoDt))
  313. isoDt = DateTime('2002-05-02T08:00:00+00:00')
  314. self.assertTrue(ref0.equalTo(isoDt))
  315. isoDt = DateTime('2002-05-02T08:00:00-04:00')
  316. self.assertTrue(ref1.equalTo(isoDt))
  317. isoDt = DateTime('2002-05-02 08:00:00-04:00')
  318. self.assertTrue(ref1.equalTo(isoDt))
  319. # Bug 1386: the colon in the timezone offset is optional
  320. isoDt = DateTime('2002-05-02T08:00:00-0400')
  321. self.assertTrue(ref1.equalTo(isoDt))
  322. # Bug 2191: date reduced formats
  323. isoDt = DateTime('2006-01-01')
  324. self.assertTrue(ref4.equalTo(isoDt))
  325. isoDt = DateTime('200601-01')
  326. self.assertTrue(ref4.equalTo(isoDt))
  327. isoDt = DateTime('20060101')
  328. self.assertTrue(ref4.equalTo(isoDt))
  329. isoDt = DateTime('2006-01')
  330. self.assertTrue(ref4.equalTo(isoDt))
  331. isoDt = DateTime('200601')
  332. self.assertTrue(ref4.equalTo(isoDt))
  333. isoDt = DateTime('2006')
  334. self.assertTrue(ref4.equalTo(isoDt))
  335. # Bug 2191: date/time separators are also optional
  336. isoDt = DateTime('20020502T08:00:00')
  337. self.assertTrue(ref0.equalTo(isoDt))
  338. isoDt = DateTime('2002-05-02T080000')
  339. self.assertTrue(ref0.equalTo(isoDt))
  340. isoDt = DateTime('20020502T080000')
  341. self.assertTrue(ref0.equalTo(isoDt))
  342. # Bug 2191: timezones with only one digit for hour
  343. isoDt = DateTime('20020502T080000+0')
  344. self.assertTrue(ref0.equalTo(isoDt))
  345. isoDt = DateTime('20020502 080000-4')
  346. self.assertTrue(ref1.equalTo(isoDt))
  347. isoDt = DateTime('20020502T080000-400')
  348. self.assertTrue(ref1.equalTo(isoDt))
  349. isoDt = DateTime('20020502T080000-4:00')
  350. self.assertTrue(ref1.equalTo(isoDt))
  351. # Bug 2191: optional seconds/minutes
  352. isoDt = DateTime('2002-05-02T0800')
  353. self.assertTrue(ref0.equalTo(isoDt))
  354. isoDt = DateTime('2002-05-02T08')
  355. self.assertTrue(ref0.equalTo(isoDt))
  356. # Bug 2191: week format
  357. isoDt = DateTime('2002-W18-4T0800')
  358. self.assertTrue(ref0.equalTo(isoDt))
  359. isoDt = DateTime('2002-W184T0800')
  360. self.assertTrue(ref0.equalTo(isoDt))
  361. isoDt = DateTime('2002W18-4T0800')
  362. self.assertTrue(ref0.equalTo(isoDt))
  363. isoDt = DateTime('2002W184T08')
  364. self.assertTrue(ref0.equalTo(isoDt))
  365. isoDt = DateTime('2004-W25-1T14:30:15-03:00')
  366. self.assertTrue(ref3.equalTo(isoDt))
  367. isoDt = DateTime('2004-W25T14:30:15-03:00')
  368. self.assertTrue(ref3.equalTo(isoDt))
  369. # Bug 2191: day of year format
  370. isoDt = DateTime('2002-122T0800')
  371. self.assertTrue(ref0.equalTo(isoDt))
  372. isoDt = DateTime('2002122T0800')
  373. self.assertTrue(ref0.equalTo(isoDt))
  374. # Bug 2191: hours/minutes fractions
  375. isoDt = DateTime('2006-11-06T10.5')
  376. self.assertTrue(ref2.equalTo(isoDt))
  377. isoDt = DateTime('2006-11-06T10,5')
  378. self.assertTrue(ref2.equalTo(isoDt))
  379. isoDt = DateTime('20040614T1430.25-3')
  380. self.assertTrue(ref3.equalTo(isoDt))
  381. isoDt = DateTime('2004-06-14T1430,25-3')
  382. self.assertTrue(ref3.equalTo(isoDt))
  383. isoDt = DateTime('2004-06-14T14:30.25-3')
  384. self.assertTrue(ref3.equalTo(isoDt))
  385. isoDt = DateTime('20040614T14:30,25-3')
  386. self.assertTrue(ref3.equalTo(isoDt))
  387. # ISO8601 standard format
  388. iso8601_string = '2002-05-02T08:00:00-04:00'
  389. iso8601DT = DateTime(iso8601_string)
  390. self.assertEqual(iso8601_string, iso8601DT.ISO8601())
  391. # ISO format with no timezone
  392. isoDt = DateTime('2006-01-01 00:00:00')
  393. self.assertTrue(ref4.equalTo(isoDt))
  394. def testJulianWeek(self):
  395. # Check JulianDayWeek function
  396. fn = os.path.join(DATADIR, 'julian_testdata.txt')
  397. with open(fn, 'r') as fd:
  398. lines = fd.readlines()
  399. for line in lines:
  400. d = DateTime(line[:10])
  401. result_from_mx = tuple(map(int, line[12:-2].split(',')))
  402. self.assertEqual(result_from_mx[1], d.week())
  403. def testCopyConstructor(self):
  404. d = DateTime('2004/04/04')
  405. self.assertEqual(DateTime(d), d)
  406. self.assertEqual(str(DateTime(d)), str(d))
  407. d2 = DateTime('1999/04/12 01:00:00')
  408. self.assertEqual(DateTime(d2), d2)
  409. self.assertEqual(str(DateTime(d2)), str(d2))
  410. def testCopyConstructorPreservesTimezone(self):
  411. # test for https://bugs.launchpad.net/zope2/+bug/200007
  412. # This always worked in the local timezone, so we need at least
  413. # two tests with different zones to be sure at least one of them
  414. # is not local.
  415. d = DateTime('2004/04/04')
  416. self.assertEqual(DateTime(d).timezone(), d.timezone())
  417. d2 = DateTime('2008/04/25 12:00:00 EST')
  418. self.assertEqual(DateTime(d2).timezone(), d2.timezone())
  419. self.assertEqual(str(DateTime(d2)), str(d2))
  420. d3 = DateTime('2008/04/25 12:00:00 PST')
  421. self.assertEqual(DateTime(d3).timezone(), d3.timezone())
  422. self.assertEqual(str(DateTime(d3)), str(d3))
  423. def testRFC822(self):
  424. # rfc822 conversion
  425. dt = DateTime('2002-05-02T08:00:00+00:00')
  426. self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 +0000')
  427. dt = DateTime('2002-05-02T08:00:00+02:00')
  428. self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 +0200')
  429. dt = DateTime('2002-05-02T08:00:00-02:00')
  430. self.assertEqual(dt.rfc822(), 'Thu, 02 May 2002 08:00:00 -0200')
  431. # Checking that conversion from local time is working.
  432. dt = DateTime()
  433. dts = dt.rfc822().split(' ')
  434. times = dts[4].split(':')
  435. _isDST = time.localtime(time.time())[8]
  436. if _isDST:
  437. offset = time.altzone
  438. else:
  439. offset = time.timezone
  440. self.assertEqual(dts[0], dt.aDay() + ',')
  441. self.assertEqual(int(dts[1]), dt.day())
  442. self.assertEqual(dts[2], dt.aMonth())
  443. self.assertEqual(int(dts[3]), dt.year())
  444. self.assertEqual(int(times[0]), dt.h_24())
  445. self.assertEqual(int(times[1]), dt.minute())
  446. self.assertEqual(int(times[2]), int(dt.second()))
  447. self.assertEqual(dts[5], "%+03d%02d" % divmod((-offset / 60), 60))
  448. def testInternationalDateformat(self):
  449. for year in (1990, 2001, 2020):
  450. for month in (1, 12):
  451. for day in (1, 12, 28, 31):
  452. try:
  453. d_us = DateTime("%d/%d/%d" % (year, month, day))
  454. except Exception:
  455. continue
  456. d_int = DateTime("%d.%d.%d" % (day, month, year),
  457. datefmt="international")
  458. self.assertEqual(d_us, d_int)
  459. d_int = DateTime("%d/%d/%d" % (day, month, year),
  460. datefmt="international")
  461. self.assertEqual(d_us, d_int)
  462. def test_intl_format_hyphen(self):
  463. d_jan = DateTime('2011-01-11 GMT')
  464. d_nov = DateTime('2011-11-01 GMT')
  465. d_us = DateTime('11-01-2011 GMT')
  466. d_int = DateTime('11-01-2011 GMT', datefmt="international")
  467. self.assertNotEqual(d_us, d_int)
  468. self.assertEqual(d_us, d_nov)
  469. self.assertEqual(d_int, d_jan)
  470. def test_calcTimezoneName(self):
  471. from DateTime.interfaces import TimeError
  472. timezone_dependent_epoch = 2177452800
  473. try:
  474. DateTime()._calcTimezoneName(timezone_dependent_epoch, 0)
  475. except TimeError:
  476. self.fail('Zope Collector issue #484 (negative time bug): '
  477. 'TimeError raised')
  478. def testStrftimeTZhandling(self):
  479. # strftime timezone testing
  480. # This is a test for collector issue #1127
  481. format = '%Y-%m-%d %H:%M %Z'
  482. dt = DateTime('Wed, 19 Nov 2003 18:32:07 -0215')
  483. dt_string = dt.strftime(format)
  484. dt_local = dt.toZone(_findLocalTimeZoneName(0))
  485. dt_localstring = dt_local.strftime(format)
  486. self.assertEqual(dt_string, dt_localstring)
  487. def testStrftimeFarDates(self):
  488. # Checks strftime in dates <= 1900 or >= 2038
  489. dt = DateTime('1900/01/30')
  490. self.assertEqual(dt.strftime('%d/%m/%Y'), '30/01/1900')
  491. dt = DateTime('2040/01/30')
  492. self.assertEqual(dt.strftime('%d/%m/%Y'), '30/01/2040')
  493. def testZoneInFarDates(self):
  494. # Checks time zone in dates <= 1900 or >= 2038
  495. dt1 = DateTime('2040/01/30 14:33 GMT+1')
  496. dt2 = DateTime('2040/01/30 11:33 GMT-2')
  497. self.assertEqual(dt1.strftime('%d/%m/%Y %H:%M'),
  498. dt2.strftime('%d/%m/%Y %H:%M'))
  499. def testStrftimeUnicode(self):
  500. if IS_PYPY:
  501. # Using Non-Ascii characters for strftime doesn't work in PyPy
  502. # https://bitbucket.org/pypy/pypy/issues/2161/pypy3-strftime-does-not-accept-unicode
  503. return
  504. dt = DateTime('2002-05-02T08:00:00+00:00')
  505. uchar = b'\xc3\xa0'.decode('utf-8')
  506. ok = dt.strftime('Le %d/%m/%Y a %Hh%M').replace('a', uchar)
  507. ustr = b'Le %d/%m/%Y \xc3\xa0 %Hh%M'.decode('utf-8')
  508. self.assertEqual(dt.strftime(ustr), ok)
  509. def testTimezoneNaiveHandling(self):
  510. # checks that we assign timezone naivity correctly
  511. dt = DateTime('2007-10-04T08:00:00+00:00')
  512. self.assertFalse(dt.timezoneNaive(),
  513. 'error with naivity handling in __parse_iso8601')
  514. dt = DateTime('2007-10-04T08:00:00Z')
  515. self.assertFalse(dt.timezoneNaive(),
  516. 'error with naivity handling in __parse_iso8601')
  517. dt = DateTime('2007-10-04T08:00:00')
  518. self.assertTrue(dt.timezoneNaive(),
  519. 'error with naivity handling in __parse_iso8601')
  520. dt = DateTime('2007/10/04 15:12:33.487618 GMT+1')
  521. self.assertFalse(dt.timezoneNaive(),
  522. 'error with naivity handling in _parse')
  523. dt = DateTime('2007/10/04 15:12:33.487618')
  524. self.assertTrue(dt.timezoneNaive(),
  525. 'error with naivity handling in _parse')
  526. dt = DateTime()
  527. self.assertFalse(dt.timezoneNaive(),
  528. 'error with naivity for current time')
  529. s = '2007-10-04T08:00:00'
  530. dt = DateTime(s)
  531. self.assertEqual(s, dt.ISO8601())
  532. s = '2007-10-04T08:00:00+00:00'
  533. dt = DateTime(s)
  534. self.assertEqual(s, dt.ISO8601())
  535. def testConversions(self):
  536. sdt0 = datetime.now() # this is a timezone naive datetime
  537. dt0 = DateTime(sdt0)
  538. self.assertTrue(dt0.timezoneNaive(), (sdt0, dt0))
  539. sdt1 = datetime(2007, 10, 4, 18, 14, 42, 580, pytz.utc)
  540. dt1 = DateTime(sdt1)
  541. self.assertFalse(dt1.timezoneNaive(), (sdt1, dt1))
  542. # convert back
  543. sdt2 = dt0.asdatetime()
  544. self.assertEqual(sdt0, sdt2)
  545. sdt3 = dt1.utcdatetime() # this returns a timezone naive datetime
  546. self.assertEqual(sdt1.hour, sdt3.hour)
  547. dt4 = DateTime('2007-10-04T10:00:00+05:00')
  548. sdt4 = datetime(2007, 10, 4, 5, 0)
  549. self.assertEqual(dt4.utcdatetime(), sdt4)
  550. self.assertEqual(dt4.asdatetime(), sdt4.replace(tzinfo=pytz.utc))
  551. dt5 = DateTime('2007-10-23 10:00:00 US/Eastern')
  552. tz = pytz.timezone('US/Eastern')
  553. sdt5 = datetime(2007, 10, 23, 10, 0, tzinfo=tz)
  554. dt6 = DateTime(sdt5)
  555. self.assertEqual(dt5.asdatetime(), sdt5)
  556. self.assertEqual(dt6.asdatetime(), sdt5)
  557. self.assertEqual(dt5, dt6)
  558. self.assertEqual(dt5.asdatetime().tzinfo, tz)
  559. self.assertEqual(dt6.asdatetime().tzinfo, tz)
  560. def testBasicTZ(self):
  561. # psycopg2 supplies it's own tzinfo instances, with no `zone` attribute
  562. tz = FixedOffset(60, 'GMT+1')
  563. dt1 = datetime(2008, 8, 5, 12, 0, tzinfo=tz)
  564. DT = DateTime(dt1)
  565. dt2 = DT.asdatetime()
  566. offset1 = dt1.tzinfo.utcoffset(dt1)
  567. offset2 = dt2.tzinfo.utcoffset(dt2)
  568. self.assertEqual(offset1, offset2)
  569. def testEDTTimezone(self):
  570. # should be able to parse EDT timezones: see lp:599856.
  571. dt = DateTime("Mon, 28 Jun 2010 10:12:25 EDT")
  572. self.assertEqual(dt.Day(), 'Monday')
  573. self.assertEqual(dt.day(), 28)
  574. self.assertEqual(dt.Month(), 'June')
  575. self.assertEqual(dt.timezone(), 'GMT-4')
  576. def testParseISO8601(self):
  577. parsed = DateTime()._parse_iso8601('2010-10-10')
  578. self.assertEqual(parsed, (2010, 10, 10, 0, 0, 0, 'GMT+0000'))
  579. def test_interface(self):
  580. from DateTime.interfaces import IDateTime
  581. self.assertTrue(IDateTime.providedBy(DateTime()))
  582. def test_security(self):
  583. dt = DateTime()
  584. self.assertEqual(dt.__roles__, None)
  585. self.assertEqual(dt.__allow_access_to_unprotected_subobjects__, 1)
  586. def test_suite():
  587. import doctest
  588. return unittest.TestSuite([
  589. unittest.makeSuite(DateTimeTests),
  590. doctest.DocFileSuite('DateTime.txt', package='DateTime'),
  591. doctest.DocFileSuite('pytz.txt', package='DateTime'),
  592. ])