test_datetime.py 132 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368
  1. """Test date/time type.
  2. See http://www.zope.org/Members/fdrake/DateTimeWiki/TestCases
  3. """
  4. from __future__ import division
  5. import sys
  6. import pickle
  7. import cPickle
  8. import unittest
  9. from test import test_support
  10. from datetime import MINYEAR, MAXYEAR
  11. from datetime import timedelta
  12. from datetime import tzinfo
  13. from datetime import time
  14. from datetime import date, datetime
  15. pickle_choices = [(pickler, unpickler, proto)
  16. for pickler in pickle, cPickle
  17. for unpickler in pickle, cPickle
  18. for proto in range(3)]
  19. assert len(pickle_choices) == 2*2*3
  20. # An arbitrary collection of objects of non-datetime types, for testing
  21. # mixed-type comparisons.
  22. OTHERSTUFF = (10, 10L, 34.5, "abc", {}, [], ())
  23. #############################################################################
  24. # module tests
  25. class TestModule(unittest.TestCase):
  26. def test_constants(self):
  27. import datetime
  28. self.assertEqual(datetime.MINYEAR, 1)
  29. self.assertEqual(datetime.MAXYEAR, 9999)
  30. #############################################################################
  31. # tzinfo tests
  32. class FixedOffset(tzinfo):
  33. def __init__(self, offset, name, dstoffset=42):
  34. if isinstance(offset, int):
  35. offset = timedelta(minutes=offset)
  36. if isinstance(dstoffset, int):
  37. dstoffset = timedelta(minutes=dstoffset)
  38. self.__offset = offset
  39. self.__name = name
  40. self.__dstoffset = dstoffset
  41. def __repr__(self):
  42. return self.__name.lower()
  43. def utcoffset(self, dt):
  44. return self.__offset
  45. def tzname(self, dt):
  46. return self.__name
  47. def dst(self, dt):
  48. return self.__dstoffset
  49. class PicklableFixedOffset(FixedOffset):
  50. def __init__(self, offset=None, name=None, dstoffset=None):
  51. FixedOffset.__init__(self, offset, name, dstoffset)
  52. class TestTZInfo(unittest.TestCase):
  53. def test_non_abstractness(self):
  54. # In order to allow subclasses to get pickled, the C implementation
  55. # wasn't able to get away with having __init__ raise
  56. # NotImplementedError.
  57. useless = tzinfo()
  58. dt = datetime.max
  59. self.assertRaises(NotImplementedError, useless.tzname, dt)
  60. self.assertRaises(NotImplementedError, useless.utcoffset, dt)
  61. self.assertRaises(NotImplementedError, useless.dst, dt)
  62. def test_subclass_must_override(self):
  63. class NotEnough(tzinfo):
  64. def __init__(self, offset, name):
  65. self.__offset = offset
  66. self.__name = name
  67. self.assertTrue(issubclass(NotEnough, tzinfo))
  68. ne = NotEnough(3, "NotByALongShot")
  69. self.assertIsInstance(ne, tzinfo)
  70. dt = datetime.now()
  71. self.assertRaises(NotImplementedError, ne.tzname, dt)
  72. self.assertRaises(NotImplementedError, ne.utcoffset, dt)
  73. self.assertRaises(NotImplementedError, ne.dst, dt)
  74. def test_normal(self):
  75. fo = FixedOffset(3, "Three")
  76. self.assertIsInstance(fo, tzinfo)
  77. for dt in datetime.now(), None:
  78. self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
  79. self.assertEqual(fo.tzname(dt), "Three")
  80. self.assertEqual(fo.dst(dt), timedelta(minutes=42))
  81. def test_pickling_base(self):
  82. # There's no point to pickling tzinfo objects on their own (they
  83. # carry no data), but they need to be picklable anyway else
  84. # concrete subclasses can't be pickled.
  85. orig = tzinfo.__new__(tzinfo)
  86. self.assertIs(type(orig), tzinfo)
  87. for pickler, unpickler, proto in pickle_choices:
  88. green = pickler.dumps(orig, proto)
  89. derived = unpickler.loads(green)
  90. self.assertIs(type(derived), tzinfo)
  91. def test_pickling_subclass(self):
  92. # Make sure we can pickle/unpickle an instance of a subclass.
  93. offset = timedelta(minutes=-300)
  94. orig = PicklableFixedOffset(offset, 'cookie')
  95. self.assertIsInstance(orig, tzinfo)
  96. self.assertTrue(type(orig) is PicklableFixedOffset)
  97. self.assertEqual(orig.utcoffset(None), offset)
  98. self.assertEqual(orig.tzname(None), 'cookie')
  99. for pickler, unpickler, proto in pickle_choices:
  100. green = pickler.dumps(orig, proto)
  101. derived = unpickler.loads(green)
  102. self.assertIsInstance(derived, tzinfo)
  103. self.assertTrue(type(derived) is PicklableFixedOffset)
  104. self.assertEqual(derived.utcoffset(None), offset)
  105. self.assertEqual(derived.tzname(None), 'cookie')
  106. #############################################################################
  107. # Base class for testing a particular aspect of timedelta, time, date and
  108. # datetime comparisons.
  109. class HarmlessMixedComparison:
  110. # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
  111. # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
  112. # legit constructor.
  113. def test_harmless_mixed_comparison(self):
  114. me = self.theclass(1, 1, 1)
  115. self.assertFalse(me == ())
  116. self.assertTrue(me != ())
  117. self.assertFalse(() == me)
  118. self.assertTrue(() != me)
  119. self.assertIn(me, [1, 20L, [], me])
  120. self.assertIn([], [me, 1, 20L, []])
  121. def test_harmful_mixed_comparison(self):
  122. me = self.theclass(1, 1, 1)
  123. self.assertRaises(TypeError, lambda: me < ())
  124. self.assertRaises(TypeError, lambda: me <= ())
  125. self.assertRaises(TypeError, lambda: me > ())
  126. self.assertRaises(TypeError, lambda: me >= ())
  127. self.assertRaises(TypeError, lambda: () < me)
  128. self.assertRaises(TypeError, lambda: () <= me)
  129. self.assertRaises(TypeError, lambda: () > me)
  130. self.assertRaises(TypeError, lambda: () >= me)
  131. self.assertRaises(TypeError, cmp, (), me)
  132. self.assertRaises(TypeError, cmp, me, ())
  133. #############################################################################
  134. # timedelta tests
  135. class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
  136. theclass = timedelta
  137. def test_constructor(self):
  138. eq = self.assertEqual
  139. td = timedelta
  140. # Check keyword args to constructor
  141. eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
  142. milliseconds=0, microseconds=0))
  143. eq(td(1), td(days=1))
  144. eq(td(0, 1), td(seconds=1))
  145. eq(td(0, 0, 1), td(microseconds=1))
  146. eq(td(weeks=1), td(days=7))
  147. eq(td(days=1), td(hours=24))
  148. eq(td(hours=1), td(minutes=60))
  149. eq(td(minutes=1), td(seconds=60))
  150. eq(td(seconds=1), td(milliseconds=1000))
  151. eq(td(milliseconds=1), td(microseconds=1000))
  152. # Check float args to constructor
  153. eq(td(weeks=1.0/7), td(days=1))
  154. eq(td(days=1.0/24), td(hours=1))
  155. eq(td(hours=1.0/60), td(minutes=1))
  156. eq(td(minutes=1.0/60), td(seconds=1))
  157. eq(td(seconds=0.001), td(milliseconds=1))
  158. eq(td(milliseconds=0.001), td(microseconds=1))
  159. def test_computations(self):
  160. eq = self.assertEqual
  161. td = timedelta
  162. a = td(7) # One week
  163. b = td(0, 60) # One minute
  164. c = td(0, 0, 1000) # One millisecond
  165. eq(a+b+c, td(7, 60, 1000))
  166. eq(a-b, td(6, 24*3600 - 60))
  167. eq(-a, td(-7))
  168. eq(+a, td(7))
  169. eq(-b, td(-1, 24*3600 - 60))
  170. eq(-c, td(-1, 24*3600 - 1, 999000))
  171. eq(abs(a), a)
  172. eq(abs(-a), a)
  173. eq(td(6, 24*3600), a)
  174. eq(td(0, 0, 60*1000000), b)
  175. eq(a*10, td(70))
  176. eq(a*10, 10*a)
  177. eq(a*10L, 10*a)
  178. eq(b*10, td(0, 600))
  179. eq(10*b, td(0, 600))
  180. eq(b*10L, td(0, 600))
  181. eq(c*10, td(0, 0, 10000))
  182. eq(10*c, td(0, 0, 10000))
  183. eq(c*10L, td(0, 0, 10000))
  184. eq(a*-1, -a)
  185. eq(b*-2, -b-b)
  186. eq(c*-2, -c+-c)
  187. eq(b*(60*24), (b*60)*24)
  188. eq(b*(60*24), (60*b)*24)
  189. eq(c*1000, td(0, 1))
  190. eq(1000*c, td(0, 1))
  191. eq(a//7, td(1))
  192. eq(b//10, td(0, 6))
  193. eq(c//1000, td(0, 0, 1))
  194. eq(a//10, td(0, 7*24*360))
  195. eq(a//3600000, td(0, 0, 7*24*1000))
  196. # Issue #11576
  197. eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
  198. td(0, 0, 1))
  199. eq(td(999999999, 1, 1) - td(999999999, 1, 0),
  200. td(0, 0, 1))
  201. def test_disallowed_computations(self):
  202. a = timedelta(42)
  203. # Add/sub ints, longs, floats should be illegal
  204. for i in 1, 1L, 1.0:
  205. self.assertRaises(TypeError, lambda: a+i)
  206. self.assertRaises(TypeError, lambda: a-i)
  207. self.assertRaises(TypeError, lambda: i+a)
  208. self.assertRaises(TypeError, lambda: i-a)
  209. # Mul/div by float isn't supported.
  210. x = 2.3
  211. self.assertRaises(TypeError, lambda: a*x)
  212. self.assertRaises(TypeError, lambda: x*a)
  213. self.assertRaises(TypeError, lambda: a/x)
  214. self.assertRaises(TypeError, lambda: x/a)
  215. self.assertRaises(TypeError, lambda: a // x)
  216. self.assertRaises(TypeError, lambda: x // a)
  217. # Division of int by timedelta doesn't make sense.
  218. # Division by zero doesn't make sense.
  219. for zero in 0, 0L:
  220. self.assertRaises(TypeError, lambda: zero // a)
  221. self.assertRaises(ZeroDivisionError, lambda: a // zero)
  222. def test_basic_attributes(self):
  223. days, seconds, us = 1, 7, 31
  224. td = timedelta(days, seconds, us)
  225. self.assertEqual(td.days, days)
  226. self.assertEqual(td.seconds, seconds)
  227. self.assertEqual(td.microseconds, us)
  228. def test_total_seconds(self):
  229. td = timedelta(days=365)
  230. self.assertEqual(td.total_seconds(), 31536000.0)
  231. for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
  232. td = timedelta(seconds=total_seconds)
  233. self.assertEqual(td.total_seconds(), total_seconds)
  234. # Issue8644: Test that td.total_seconds() has the same
  235. # accuracy as td / timedelta(seconds=1).
  236. for ms in [-1, -2, -123]:
  237. td = timedelta(microseconds=ms)
  238. self.assertEqual(td.total_seconds(),
  239. ((24*3600*td.days + td.seconds)*10**6
  240. + td.microseconds)/10**6)
  241. def test_carries(self):
  242. t1 = timedelta(days=100,
  243. weeks=-7,
  244. hours=-24*(100-49),
  245. minutes=-3,
  246. seconds=12,
  247. microseconds=(3*60 - 12) * 1e6 + 1)
  248. t2 = timedelta(microseconds=1)
  249. self.assertEqual(t1, t2)
  250. def test_hash_equality(self):
  251. t1 = timedelta(days=100,
  252. weeks=-7,
  253. hours=-24*(100-49),
  254. minutes=-3,
  255. seconds=12,
  256. microseconds=(3*60 - 12) * 1000000)
  257. t2 = timedelta()
  258. self.assertEqual(hash(t1), hash(t2))
  259. t1 += timedelta(weeks=7)
  260. t2 += timedelta(days=7*7)
  261. self.assertEqual(t1, t2)
  262. self.assertEqual(hash(t1), hash(t2))
  263. d = {t1: 1}
  264. d[t2] = 2
  265. self.assertEqual(len(d), 1)
  266. self.assertEqual(d[t1], 2)
  267. def test_pickling(self):
  268. args = 12, 34, 56
  269. orig = timedelta(*args)
  270. for pickler, unpickler, proto in pickle_choices:
  271. green = pickler.dumps(orig, proto)
  272. derived = unpickler.loads(green)
  273. self.assertEqual(orig, derived)
  274. def test_compare(self):
  275. t1 = timedelta(2, 3, 4)
  276. t2 = timedelta(2, 3, 4)
  277. self.assertTrue(t1 == t2)
  278. self.assertTrue(t1 <= t2)
  279. self.assertTrue(t1 >= t2)
  280. self.assertFalse(t1 != t2)
  281. self.assertFalse(t1 < t2)
  282. self.assertFalse(t1 > t2)
  283. self.assertEqual(cmp(t1, t2), 0)
  284. self.assertEqual(cmp(t2, t1), 0)
  285. for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
  286. t2 = timedelta(*args) # this is larger than t1
  287. self.assertTrue(t1 < t2)
  288. self.assertTrue(t2 > t1)
  289. self.assertTrue(t1 <= t2)
  290. self.assertTrue(t2 >= t1)
  291. self.assertTrue(t1 != t2)
  292. self.assertTrue(t2 != t1)
  293. self.assertFalse(t1 == t2)
  294. self.assertFalse(t2 == t1)
  295. self.assertFalse(t1 > t2)
  296. self.assertFalse(t2 < t1)
  297. self.assertFalse(t1 >= t2)
  298. self.assertFalse(t2 <= t1)
  299. self.assertEqual(cmp(t1, t2), -1)
  300. self.assertEqual(cmp(t2, t1), 1)
  301. for badarg in OTHERSTUFF:
  302. self.assertEqual(t1 == badarg, False)
  303. self.assertEqual(t1 != badarg, True)
  304. self.assertEqual(badarg == t1, False)
  305. self.assertEqual(badarg != t1, True)
  306. self.assertRaises(TypeError, lambda: t1 <= badarg)
  307. self.assertRaises(TypeError, lambda: t1 < badarg)
  308. self.assertRaises(TypeError, lambda: t1 > badarg)
  309. self.assertRaises(TypeError, lambda: t1 >= badarg)
  310. self.assertRaises(TypeError, lambda: badarg <= t1)
  311. self.assertRaises(TypeError, lambda: badarg < t1)
  312. self.assertRaises(TypeError, lambda: badarg > t1)
  313. self.assertRaises(TypeError, lambda: badarg >= t1)
  314. def test_str(self):
  315. td = timedelta
  316. eq = self.assertEqual
  317. eq(str(td(1)), "1 day, 0:00:00")
  318. eq(str(td(-1)), "-1 day, 0:00:00")
  319. eq(str(td(2)), "2 days, 0:00:00")
  320. eq(str(td(-2)), "-2 days, 0:00:00")
  321. eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
  322. eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
  323. eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
  324. "-210 days, 23:12:34")
  325. eq(str(td(milliseconds=1)), "0:00:00.001000")
  326. eq(str(td(microseconds=3)), "0:00:00.000003")
  327. eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
  328. microseconds=999999)),
  329. "999999999 days, 23:59:59.999999")
  330. def test_roundtrip(self):
  331. for td in (timedelta(days=999999999, hours=23, minutes=59,
  332. seconds=59, microseconds=999999),
  333. timedelta(days=-999999999),
  334. timedelta(days=1, seconds=2, microseconds=3)):
  335. # Verify td -> string -> td identity.
  336. s = repr(td)
  337. self.assertTrue(s.startswith('datetime.'))
  338. s = s[9:]
  339. td2 = eval(s)
  340. self.assertEqual(td, td2)
  341. # Verify identity via reconstructing from pieces.
  342. td2 = timedelta(td.days, td.seconds, td.microseconds)
  343. self.assertEqual(td, td2)
  344. def test_resolution_info(self):
  345. self.assertIsInstance(timedelta.min, timedelta)
  346. self.assertIsInstance(timedelta.max, timedelta)
  347. self.assertIsInstance(timedelta.resolution, timedelta)
  348. self.assertTrue(timedelta.max > timedelta.min)
  349. self.assertEqual(timedelta.min, timedelta(-999999999))
  350. self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
  351. self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
  352. def test_overflow(self):
  353. tiny = timedelta.resolution
  354. td = timedelta.min + tiny
  355. td -= tiny # no problem
  356. self.assertRaises(OverflowError, td.__sub__, tiny)
  357. self.assertRaises(OverflowError, td.__add__, -tiny)
  358. td = timedelta.max - tiny
  359. td += tiny # no problem
  360. self.assertRaises(OverflowError, td.__add__, tiny)
  361. self.assertRaises(OverflowError, td.__sub__, -tiny)
  362. self.assertRaises(OverflowError, lambda: -timedelta.max)
  363. def test_microsecond_rounding(self):
  364. td = timedelta
  365. eq = self.assertEqual
  366. # Single-field rounding.
  367. eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
  368. eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
  369. eq(td(milliseconds=0.6/1000), td(microseconds=1))
  370. eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
  371. # Rounding due to contributions from more than one field.
  372. us_per_hour = 3600e6
  373. us_per_day = us_per_hour * 24
  374. eq(td(days=.4/us_per_day), td(0))
  375. eq(td(hours=.2/us_per_hour), td(0))
  376. eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
  377. eq(td(days=-.4/us_per_day), td(0))
  378. eq(td(hours=-.2/us_per_hour), td(0))
  379. eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
  380. def test_massive_normalization(self):
  381. td = timedelta(microseconds=-1)
  382. self.assertEqual((td.days, td.seconds, td.microseconds),
  383. (-1, 24*3600-1, 999999))
  384. def test_bool(self):
  385. self.assertTrue(timedelta(1))
  386. self.assertTrue(timedelta(0, 1))
  387. self.assertTrue(timedelta(0, 0, 1))
  388. self.assertTrue(timedelta(microseconds=1))
  389. self.assertFalse(timedelta(0))
  390. def test_subclass_timedelta(self):
  391. class T(timedelta):
  392. @staticmethod
  393. def from_td(td):
  394. return T(td.days, td.seconds, td.microseconds)
  395. def as_hours(self):
  396. sum = (self.days * 24 +
  397. self.seconds / 3600.0 +
  398. self.microseconds / 3600e6)
  399. return round(sum)
  400. t1 = T(days=1)
  401. self.assertIs(type(t1), T)
  402. self.assertEqual(t1.as_hours(), 24)
  403. t2 = T(days=-1, seconds=-3600)
  404. self.assertIs(type(t2), T)
  405. self.assertEqual(t2.as_hours(), -25)
  406. t3 = t1 + t2
  407. self.assertIs(type(t3), timedelta)
  408. t4 = T.from_td(t3)
  409. self.assertIs(type(t4), T)
  410. self.assertEqual(t3.days, t4.days)
  411. self.assertEqual(t3.seconds, t4.seconds)
  412. self.assertEqual(t3.microseconds, t4.microseconds)
  413. self.assertEqual(str(t3), str(t4))
  414. self.assertEqual(t4.as_hours(), -1)
  415. #############################################################################
  416. # date tests
  417. class TestDateOnly(unittest.TestCase):
  418. # Tests here won't pass if also run on datetime objects, so don't
  419. # subclass this to test datetimes too.
  420. def test_delta_non_days_ignored(self):
  421. dt = date(2000, 1, 2)
  422. delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
  423. microseconds=5)
  424. days = timedelta(delta.days)
  425. self.assertEqual(days, timedelta(1))
  426. dt2 = dt + delta
  427. self.assertEqual(dt2, dt + days)
  428. dt2 = delta + dt
  429. self.assertEqual(dt2, dt + days)
  430. dt2 = dt - delta
  431. self.assertEqual(dt2, dt - days)
  432. delta = -delta
  433. days = timedelta(delta.days)
  434. self.assertEqual(days, timedelta(-2))
  435. dt2 = dt + delta
  436. self.assertEqual(dt2, dt + days)
  437. dt2 = delta + dt
  438. self.assertEqual(dt2, dt + days)
  439. dt2 = dt - delta
  440. self.assertEqual(dt2, dt - days)
  441. class SubclassDate(date):
  442. sub_var = 1
  443. class TestDate(HarmlessMixedComparison, unittest.TestCase):
  444. # Tests here should pass for both dates and datetimes, except for a
  445. # few tests that TestDateTime overrides.
  446. theclass = date
  447. def test_basic_attributes(self):
  448. dt = self.theclass(2002, 3, 1)
  449. self.assertEqual(dt.year, 2002)
  450. self.assertEqual(dt.month, 3)
  451. self.assertEqual(dt.day, 1)
  452. def test_roundtrip(self):
  453. for dt in (self.theclass(1, 2, 3),
  454. self.theclass.today()):
  455. # Verify dt -> string -> date identity.
  456. s = repr(dt)
  457. self.assertTrue(s.startswith('datetime.'))
  458. s = s[9:]
  459. dt2 = eval(s)
  460. self.assertEqual(dt, dt2)
  461. # Verify identity via reconstructing from pieces.
  462. dt2 = self.theclass(dt.year, dt.month, dt.day)
  463. self.assertEqual(dt, dt2)
  464. def test_ordinal_conversions(self):
  465. # Check some fixed values.
  466. for y, m, d, n in [(1, 1, 1, 1), # calendar origin
  467. (1, 12, 31, 365),
  468. (2, 1, 1, 366),
  469. # first example from "Calendrical Calculations"
  470. (1945, 11, 12, 710347)]:
  471. d = self.theclass(y, m, d)
  472. self.assertEqual(n, d.toordinal())
  473. fromord = self.theclass.fromordinal(n)
  474. self.assertEqual(d, fromord)
  475. if hasattr(fromord, "hour"):
  476. # if we're checking something fancier than a date, verify
  477. # the extra fields have been zeroed out
  478. self.assertEqual(fromord.hour, 0)
  479. self.assertEqual(fromord.minute, 0)
  480. self.assertEqual(fromord.second, 0)
  481. self.assertEqual(fromord.microsecond, 0)
  482. # Check first and last days of year spottily across the whole
  483. # range of years supported.
  484. for year in xrange(MINYEAR, MAXYEAR+1, 7):
  485. # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
  486. d = self.theclass(year, 1, 1)
  487. n = d.toordinal()
  488. d2 = self.theclass.fromordinal(n)
  489. self.assertEqual(d, d2)
  490. # Verify that moving back a day gets to the end of year-1.
  491. if year > 1:
  492. d = self.theclass.fromordinal(n-1)
  493. d2 = self.theclass(year-1, 12, 31)
  494. self.assertEqual(d, d2)
  495. self.assertEqual(d2.toordinal(), n-1)
  496. # Test every day in a leap-year and a non-leap year.
  497. dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  498. for year, isleap in (2000, True), (2002, False):
  499. n = self.theclass(year, 1, 1).toordinal()
  500. for month, maxday in zip(range(1, 13), dim):
  501. if month == 2 and isleap:
  502. maxday += 1
  503. for day in range(1, maxday+1):
  504. d = self.theclass(year, month, day)
  505. self.assertEqual(d.toordinal(), n)
  506. self.assertEqual(d, self.theclass.fromordinal(n))
  507. n += 1
  508. def test_extreme_ordinals(self):
  509. a = self.theclass.min
  510. a = self.theclass(a.year, a.month, a.day) # get rid of time parts
  511. aord = a.toordinal()
  512. b = a.fromordinal(aord)
  513. self.assertEqual(a, b)
  514. self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
  515. b = a + timedelta(days=1)
  516. self.assertEqual(b.toordinal(), aord + 1)
  517. self.assertEqual(b, self.theclass.fromordinal(aord + 1))
  518. a = self.theclass.max
  519. a = self.theclass(a.year, a.month, a.day) # get rid of time parts
  520. aord = a.toordinal()
  521. b = a.fromordinal(aord)
  522. self.assertEqual(a, b)
  523. self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
  524. b = a - timedelta(days=1)
  525. self.assertEqual(b.toordinal(), aord - 1)
  526. self.assertEqual(b, self.theclass.fromordinal(aord - 1))
  527. def test_bad_constructor_arguments(self):
  528. # bad years
  529. self.theclass(MINYEAR, 1, 1) # no exception
  530. self.theclass(MAXYEAR, 1, 1) # no exception
  531. self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
  532. self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
  533. # bad months
  534. self.theclass(2000, 1, 1) # no exception
  535. self.theclass(2000, 12, 1) # no exception
  536. self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
  537. self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
  538. # bad days
  539. self.theclass(2000, 2, 29) # no exception
  540. self.theclass(2004, 2, 29) # no exception
  541. self.theclass(2400, 2, 29) # no exception
  542. self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
  543. self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
  544. self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
  545. self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
  546. self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
  547. self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
  548. def test_hash_equality(self):
  549. d = self.theclass(2000, 12, 31)
  550. # same thing
  551. e = self.theclass(2000, 12, 31)
  552. self.assertEqual(d, e)
  553. self.assertEqual(hash(d), hash(e))
  554. dic = {d: 1}
  555. dic[e] = 2
  556. self.assertEqual(len(dic), 1)
  557. self.assertEqual(dic[d], 2)
  558. self.assertEqual(dic[e], 2)
  559. d = self.theclass(2001, 1, 1)
  560. # same thing
  561. e = self.theclass(2001, 1, 1)
  562. self.assertEqual(d, e)
  563. self.assertEqual(hash(d), hash(e))
  564. dic = {d: 1}
  565. dic[e] = 2
  566. self.assertEqual(len(dic), 1)
  567. self.assertEqual(dic[d], 2)
  568. self.assertEqual(dic[e], 2)
  569. def test_computations(self):
  570. a = self.theclass(2002, 1, 31)
  571. b = self.theclass(1956, 1, 31)
  572. diff = a-b
  573. self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
  574. self.assertEqual(diff.seconds, 0)
  575. self.assertEqual(diff.microseconds, 0)
  576. day = timedelta(1)
  577. week = timedelta(7)
  578. a = self.theclass(2002, 3, 2)
  579. self.assertEqual(a + day, self.theclass(2002, 3, 3))
  580. self.assertEqual(day + a, self.theclass(2002, 3, 3))
  581. self.assertEqual(a - day, self.theclass(2002, 3, 1))
  582. self.assertEqual(-day + a, self.theclass(2002, 3, 1))
  583. self.assertEqual(a + week, self.theclass(2002, 3, 9))
  584. self.assertEqual(a - week, self.theclass(2002, 2, 23))
  585. self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
  586. self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
  587. self.assertEqual((a + week) - a, week)
  588. self.assertEqual((a + day) - a, day)
  589. self.assertEqual((a - week) - a, -week)
  590. self.assertEqual((a - day) - a, -day)
  591. self.assertEqual(a - (a + week), -week)
  592. self.assertEqual(a - (a + day), -day)
  593. self.assertEqual(a - (a - week), week)
  594. self.assertEqual(a - (a - day), day)
  595. # Add/sub ints, longs, floats should be illegal
  596. for i in 1, 1L, 1.0:
  597. self.assertRaises(TypeError, lambda: a+i)
  598. self.assertRaises(TypeError, lambda: a-i)
  599. self.assertRaises(TypeError, lambda: i+a)
  600. self.assertRaises(TypeError, lambda: i-a)
  601. # delta - date is senseless.
  602. self.assertRaises(TypeError, lambda: day - a)
  603. # mixing date and (delta or date) via * or // is senseless
  604. self.assertRaises(TypeError, lambda: day * a)
  605. self.assertRaises(TypeError, lambda: a * day)
  606. self.assertRaises(TypeError, lambda: day // a)
  607. self.assertRaises(TypeError, lambda: a // day)
  608. self.assertRaises(TypeError, lambda: a * a)
  609. self.assertRaises(TypeError, lambda: a // a)
  610. # date + date is senseless
  611. self.assertRaises(TypeError, lambda: a + a)
  612. def test_overflow(self):
  613. tiny = self.theclass.resolution
  614. for delta in [tiny, timedelta(1), timedelta(2)]:
  615. dt = self.theclass.min + delta
  616. dt -= delta # no problem
  617. self.assertRaises(OverflowError, dt.__sub__, delta)
  618. self.assertRaises(OverflowError, dt.__add__, -delta)
  619. dt = self.theclass.max - delta
  620. dt += delta # no problem
  621. self.assertRaises(OverflowError, dt.__add__, delta)
  622. self.assertRaises(OverflowError, dt.__sub__, -delta)
  623. def test_fromtimestamp(self):
  624. import time
  625. # Try an arbitrary fixed value.
  626. year, month, day = 1999, 9, 19
  627. ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
  628. d = self.theclass.fromtimestamp(ts)
  629. self.assertEqual(d.year, year)
  630. self.assertEqual(d.month, month)
  631. self.assertEqual(d.day, day)
  632. def test_insane_fromtimestamp(self):
  633. # It's possible that some platform maps time_t to double,
  634. # and that this test will fail there. This test should
  635. # exempt such platforms (provided they return reasonable
  636. # results!).
  637. for insane in -1e200, 1e200:
  638. self.assertRaises(ValueError, self.theclass.fromtimestamp,
  639. insane)
  640. def test_today(self):
  641. import time
  642. # We claim that today() is like fromtimestamp(time.time()), so
  643. # prove it.
  644. for dummy in range(3):
  645. today = self.theclass.today()
  646. ts = time.time()
  647. todayagain = self.theclass.fromtimestamp(ts)
  648. if today == todayagain:
  649. break
  650. # There are several legit reasons that could fail:
  651. # 1. It recently became midnight, between the today() and the
  652. # time() calls.
  653. # 2. The platform time() has such fine resolution that we'll
  654. # never get the same value twice.
  655. # 3. The platform time() has poor resolution, and we just
  656. # happened to call today() right before a resolution quantum
  657. # boundary.
  658. # 4. The system clock got fiddled between calls.
  659. # In any case, wait a little while and try again.
  660. time.sleep(0.1)
  661. # It worked or it didn't. If it didn't, assume it's reason #2, and
  662. # let the test pass if they're within half a second of each other.
  663. if today != todayagain:
  664. self.assertAlmostEqual(todayagain, today,
  665. delta=timedelta(seconds=0.5))
  666. def test_weekday(self):
  667. for i in range(7):
  668. # March 4, 2002 is a Monday
  669. self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
  670. self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
  671. # January 2, 1956 is a Monday
  672. self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
  673. self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
  674. def test_isocalendar(self):
  675. # Check examples from
  676. # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
  677. for i in range(7):
  678. d = self.theclass(2003, 12, 22+i)
  679. self.assertEqual(d.isocalendar(), (2003, 52, i+1))
  680. d = self.theclass(2003, 12, 29) + timedelta(i)
  681. self.assertEqual(d.isocalendar(), (2004, 1, i+1))
  682. d = self.theclass(2004, 1, 5+i)
  683. self.assertEqual(d.isocalendar(), (2004, 2, i+1))
  684. d = self.theclass(2009, 12, 21+i)
  685. self.assertEqual(d.isocalendar(), (2009, 52, i+1))
  686. d = self.theclass(2009, 12, 28) + timedelta(i)
  687. self.assertEqual(d.isocalendar(), (2009, 53, i+1))
  688. d = self.theclass(2010, 1, 4+i)
  689. self.assertEqual(d.isocalendar(), (2010, 1, i+1))
  690. def test_iso_long_years(self):
  691. # Calculate long ISO years and compare to table from
  692. # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
  693. ISO_LONG_YEARS_TABLE = """
  694. 4 32 60 88
  695. 9 37 65 93
  696. 15 43 71 99
  697. 20 48 76
  698. 26 54 82
  699. 105 133 161 189
  700. 111 139 167 195
  701. 116 144 172
  702. 122 150 178
  703. 128 156 184
  704. 201 229 257 285
  705. 207 235 263 291
  706. 212 240 268 296
  707. 218 246 274
  708. 224 252 280
  709. 303 331 359 387
  710. 308 336 364 392
  711. 314 342 370 398
  712. 320 348 376
  713. 325 353 381
  714. """
  715. iso_long_years = map(int, ISO_LONG_YEARS_TABLE.split())
  716. iso_long_years.sort()
  717. L = []
  718. for i in range(400):
  719. d = self.theclass(2000+i, 12, 31)
  720. d1 = self.theclass(1600+i, 12, 31)
  721. self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
  722. if d.isocalendar()[1] == 53:
  723. L.append(i)
  724. self.assertEqual(L, iso_long_years)
  725. def test_isoformat(self):
  726. t = self.theclass(2, 3, 2)
  727. self.assertEqual(t.isoformat(), "0002-03-02")
  728. def test_ctime(self):
  729. t = self.theclass(2002, 3, 2)
  730. self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
  731. def test_strftime(self):
  732. t = self.theclass(2005, 3, 2)
  733. self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
  734. self.assertEqual(t.strftime(""), "") # SF bug #761337
  735. self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
  736. self.assertRaises(TypeError, t.strftime) # needs an arg
  737. self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
  738. self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
  739. # test that unicode input is allowed (issue 2782)
  740. self.assertEqual(t.strftime(u"%m"), "03")
  741. # A naive object replaces %z and %Z w/ empty strings.
  742. self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
  743. #make sure that invalid format specifiers are handled correctly
  744. #self.assertRaises(ValueError, t.strftime, "%e")
  745. #self.assertRaises(ValueError, t.strftime, "%")
  746. #self.assertRaises(ValueError, t.strftime, "%#")
  747. #oh well, some systems just ignore those invalid ones.
  748. #at least, exercise them to make sure that no crashes
  749. #are generated
  750. for f in ["%e", "%", "%#"]:
  751. try:
  752. t.strftime(f)
  753. except ValueError:
  754. pass
  755. #check that this standard extension works
  756. t.strftime("%f")
  757. def test_format(self):
  758. dt = self.theclass(2007, 9, 10)
  759. self.assertEqual(dt.__format__(''), str(dt))
  760. # check that a derived class's __str__() gets called
  761. class A(self.theclass):
  762. def __str__(self):
  763. return 'A'
  764. a = A(2007, 9, 10)
  765. self.assertEqual(a.__format__(''), 'A')
  766. # check that a derived class's strftime gets called
  767. class B(self.theclass):
  768. def strftime(self, format_spec):
  769. return 'B'
  770. b = B(2007, 9, 10)
  771. self.assertEqual(b.__format__(''), str(dt))
  772. for fmt in ["m:%m d:%d y:%y",
  773. "m:%m d:%d y:%y H:%H M:%M S:%S",
  774. "%z %Z",
  775. ]:
  776. self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
  777. self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
  778. self.assertEqual(b.__format__(fmt), 'B')
  779. def test_resolution_info(self):
  780. self.assertIsInstance(self.theclass.min, self.theclass)
  781. self.assertIsInstance(self.theclass.max, self.theclass)
  782. self.assertIsInstance(self.theclass.resolution, timedelta)
  783. self.assertTrue(self.theclass.max > self.theclass.min)
  784. def test_extreme_timedelta(self):
  785. big = self.theclass.max - self.theclass.min
  786. # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
  787. n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
  788. # n == 315537897599999999 ~= 2**58.13
  789. justasbig = timedelta(0, 0, n)
  790. self.assertEqual(big, justasbig)
  791. self.assertEqual(self.theclass.min + big, self.theclass.max)
  792. self.assertEqual(self.theclass.max - big, self.theclass.min)
  793. def test_timetuple(self):
  794. for i in range(7):
  795. # January 2, 1956 is a Monday (0)
  796. d = self.theclass(1956, 1, 2+i)
  797. t = d.timetuple()
  798. self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
  799. # February 1, 1956 is a Wednesday (2)
  800. d = self.theclass(1956, 2, 1+i)
  801. t = d.timetuple()
  802. self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
  803. # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
  804. # of the year.
  805. d = self.theclass(1956, 3, 1+i)
  806. t = d.timetuple()
  807. self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
  808. self.assertEqual(t.tm_year, 1956)
  809. self.assertEqual(t.tm_mon, 3)
  810. self.assertEqual(t.tm_mday, 1+i)
  811. self.assertEqual(t.tm_hour, 0)
  812. self.assertEqual(t.tm_min, 0)
  813. self.assertEqual(t.tm_sec, 0)
  814. self.assertEqual(t.tm_wday, (3+i)%7)
  815. self.assertEqual(t.tm_yday, 61+i)
  816. self.assertEqual(t.tm_isdst, -1)
  817. def test_pickling(self):
  818. args = 6, 7, 23
  819. orig = self.theclass(*args)
  820. for pickler, unpickler, proto in pickle_choices:
  821. green = pickler.dumps(orig, proto)
  822. derived = unpickler.loads(green)
  823. self.assertEqual(orig, derived)
  824. def test_compare(self):
  825. t1 = self.theclass(2, 3, 4)
  826. t2 = self.theclass(2, 3, 4)
  827. self.assertTrue(t1 == t2)
  828. self.assertTrue(t1 <= t2)
  829. self.assertTrue(t1 >= t2)
  830. self.assertFalse(t1 != t2)
  831. self.assertFalse(t1 < t2)
  832. self.assertFalse(t1 > t2)
  833. self.assertEqual(cmp(t1, t2), 0)
  834. self.assertEqual(cmp(t2, t1), 0)
  835. for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
  836. t2 = self.theclass(*args) # this is larger than t1
  837. self.assertTrue(t1 < t2)
  838. self.assertTrue(t2 > t1)
  839. self.assertTrue(t1 <= t2)
  840. self.assertTrue(t2 >= t1)
  841. self.assertTrue(t1 != t2)
  842. self.assertTrue(t2 != t1)
  843. self.assertFalse(t1 == t2)
  844. self.assertFalse(t2 == t1)
  845. self.assertFalse(t1 > t2)
  846. self.assertFalse(t2 < t1)
  847. self.assertFalse(t1 >= t2)
  848. self.assertFalse(t2 <= t1)
  849. self.assertEqual(cmp(t1, t2), -1)
  850. self.assertEqual(cmp(t2, t1), 1)
  851. for badarg in OTHERSTUFF:
  852. self.assertEqual(t1 == badarg, False)
  853. self.assertEqual(t1 != badarg, True)
  854. self.assertEqual(badarg == t1, False)
  855. self.assertEqual(badarg != t1, True)
  856. self.assertRaises(TypeError, lambda: t1 < badarg)
  857. self.assertRaises(TypeError, lambda: t1 > badarg)
  858. self.assertRaises(TypeError, lambda: t1 >= badarg)
  859. self.assertRaises(TypeError, lambda: badarg <= t1)
  860. self.assertRaises(TypeError, lambda: badarg < t1)
  861. self.assertRaises(TypeError, lambda: badarg > t1)
  862. self.assertRaises(TypeError, lambda: badarg >= t1)
  863. def test_mixed_compare(self):
  864. our = self.theclass(2000, 4, 5)
  865. self.assertRaises(TypeError, cmp, our, 1)
  866. self.assertRaises(TypeError, cmp, 1, our)
  867. class AnotherDateTimeClass(object):
  868. def __cmp__(self, other):
  869. # Return "equal" so calling this can't be confused with
  870. # compare-by-address (which never says "equal" for distinct
  871. # objects).
  872. return 0
  873. __hash__ = None # Silence Py3k warning
  874. # This still errors, because date and datetime comparison raise
  875. # TypeError instead of NotImplemented when they don't know what to
  876. # do, in order to stop comparison from falling back to the default
  877. # compare-by-address.
  878. their = AnotherDateTimeClass()
  879. self.assertRaises(TypeError, cmp, our, their)
  880. # Oops: The next stab raises TypeError in the C implementation,
  881. # but not in the Python implementation of datetime. The difference
  882. # is due to that the Python implementation defines __cmp__ but
  883. # the C implementation defines tp_richcompare. This is more pain
  884. # to fix than it's worth, so commenting out the test.
  885. # self.assertEqual(cmp(their, our), 0)
  886. # But date and datetime comparison return NotImplemented instead if the
  887. # other object has a timetuple attr. This gives the other object a
  888. # chance to do the comparison.
  889. class Comparable(AnotherDateTimeClass):
  890. def timetuple(self):
  891. return ()
  892. their = Comparable()
  893. self.assertEqual(cmp(our, their), 0)
  894. self.assertEqual(cmp(their, our), 0)
  895. self.assertTrue(our == their)
  896. self.assertTrue(their == our)
  897. def test_bool(self):
  898. # All dates are considered true.
  899. self.assertTrue(self.theclass.min)
  900. self.assertTrue(self.theclass.max)
  901. def test_strftime_out_of_range(self):
  902. # For nasty technical reasons, we can't handle years before 1900.
  903. cls = self.theclass
  904. self.assertEqual(cls(1900, 1, 1).strftime("%Y"), "1900")
  905. for y in 1, 49, 51, 99, 100, 1000, 1899:
  906. self.assertRaises(ValueError, cls(y, 1, 1).strftime, "%Y")
  907. def test_replace(self):
  908. cls = self.theclass
  909. args = [1, 2, 3]
  910. base = cls(*args)
  911. self.assertEqual(base, base.replace())
  912. i = 0
  913. for name, newval in (("year", 2),
  914. ("month", 3),
  915. ("day", 4)):
  916. newargs = args[:]
  917. newargs[i] = newval
  918. expected = cls(*newargs)
  919. got = base.replace(**{name: newval})
  920. self.assertEqual(expected, got)
  921. i += 1
  922. # Out of bounds.
  923. base = cls(2000, 2, 29)
  924. self.assertRaises(ValueError, base.replace, year=2001)
  925. def test_subclass_date(self):
  926. class C(self.theclass):
  927. theAnswer = 42
  928. def __new__(cls, *args, **kws):
  929. temp = kws.copy()
  930. extra = temp.pop('extra')
  931. result = self.theclass.__new__(cls, *args, **temp)
  932. result.extra = extra
  933. return result
  934. def newmeth(self, start):
  935. return start + self.year + self.month
  936. args = 2003, 4, 14
  937. dt1 = self.theclass(*args)
  938. dt2 = C(*args, **{'extra': 7})
  939. self.assertEqual(dt2.__class__, C)
  940. self.assertEqual(dt2.theAnswer, 42)
  941. self.assertEqual(dt2.extra, 7)
  942. self.assertEqual(dt1.toordinal(), dt2.toordinal())
  943. self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
  944. def test_pickling_subclass_date(self):
  945. args = 6, 7, 23
  946. orig = SubclassDate(*args)
  947. for pickler, unpickler, proto in pickle_choices:
  948. green = pickler.dumps(orig, proto)
  949. derived = unpickler.loads(green)
  950. self.assertEqual(orig, derived)
  951. def test_backdoor_resistance(self):
  952. # For fast unpickling, the constructor accepts a pickle string.
  953. # This is a low-overhead backdoor. A user can (by intent or
  954. # mistake) pass a string directly, which (if it's the right length)
  955. # will get treated like a pickle, and bypass the normal sanity
  956. # checks in the constructor. This can create insane objects.
  957. # The constructor doesn't want to burn the time to validate all
  958. # fields, but does check the month field. This stops, e.g.,
  959. # datetime.datetime('1995-03-25') from yielding an insane object.
  960. base = '1995-03-25'
  961. if not issubclass(self.theclass, datetime):
  962. base = base[:4]
  963. for month_byte in '9', chr(0), chr(13), '\xff':
  964. self.assertRaises(TypeError, self.theclass,
  965. base[:2] + month_byte + base[3:])
  966. for ord_byte in range(1, 13):
  967. # This shouldn't blow up because of the month byte alone. If
  968. # the implementation changes to do more-careful checking, it may
  969. # blow up because other fields are insane.
  970. self.theclass(base[:2] + chr(ord_byte) + base[3:])
  971. #############################################################################
  972. # datetime tests
  973. class SubclassDatetime(datetime):
  974. sub_var = 1
  975. class TestDateTime(TestDate):
  976. theclass = datetime
  977. def test_basic_attributes(self):
  978. dt = self.theclass(2002, 3, 1, 12, 0)
  979. self.assertEqual(dt.year, 2002)
  980. self.assertEqual(dt.month, 3)
  981. self.assertEqual(dt.day, 1)
  982. self.assertEqual(dt.hour, 12)
  983. self.assertEqual(dt.minute, 0)
  984. self.assertEqual(dt.second, 0)
  985. self.assertEqual(dt.microsecond, 0)
  986. def test_basic_attributes_nonzero(self):
  987. # Make sure all attributes are non-zero so bugs in
  988. # bit-shifting access show up.
  989. dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
  990. self.assertEqual(dt.year, 2002)
  991. self.assertEqual(dt.month, 3)
  992. self.assertEqual(dt.day, 1)
  993. self.assertEqual(dt.hour, 12)
  994. self.assertEqual(dt.minute, 59)
  995. self.assertEqual(dt.second, 59)
  996. self.assertEqual(dt.microsecond, 8000)
  997. def test_roundtrip(self):
  998. for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
  999. self.theclass.now()):
  1000. # Verify dt -> string -> datetime identity.
  1001. s = repr(dt)
  1002. self.assertTrue(s.startswith('datetime.'))
  1003. s = s[9:]
  1004. dt2 = eval(s)
  1005. self.assertEqual(dt, dt2)
  1006. # Verify identity via reconstructing from pieces.
  1007. dt2 = self.theclass(dt.year, dt.month, dt.day,
  1008. dt.hour, dt.minute, dt.second,
  1009. dt.microsecond)
  1010. self.assertEqual(dt, dt2)
  1011. def test_isoformat(self):
  1012. t = self.theclass(2, 3, 2, 4, 5, 1, 123)
  1013. self.assertEqual(t.isoformat(), "0002-03-02T04:05:01.000123")
  1014. self.assertEqual(t.isoformat('T'), "0002-03-02T04:05:01.000123")
  1015. self.assertEqual(t.isoformat(' '), "0002-03-02 04:05:01.000123")
  1016. self.assertEqual(t.isoformat('\x00'), "0002-03-02\x0004:05:01.000123")
  1017. # str is ISO format with the separator forced to a blank.
  1018. self.assertEqual(str(t), "0002-03-02 04:05:01.000123")
  1019. t = self.theclass(2, 3, 2)
  1020. self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
  1021. self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
  1022. self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
  1023. # str is ISO format with the separator forced to a blank.
  1024. self.assertEqual(str(t), "0002-03-02 00:00:00")
  1025. def test_format(self):
  1026. dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
  1027. self.assertEqual(dt.__format__(''), str(dt))
  1028. # check that a derived class's __str__() gets called
  1029. class A(self.theclass):
  1030. def __str__(self):
  1031. return 'A'
  1032. a = A(2007, 9, 10, 4, 5, 1, 123)
  1033. self.assertEqual(a.__format__(''), 'A')
  1034. # check that a derived class's strftime gets called
  1035. class B(self.theclass):
  1036. def strftime(self, format_spec):
  1037. return 'B'
  1038. b = B(2007, 9, 10, 4, 5, 1, 123)
  1039. self.assertEqual(b.__format__(''), str(dt))
  1040. for fmt in ["m:%m d:%d y:%y",
  1041. "m:%m d:%d y:%y H:%H M:%M S:%S",
  1042. "%z %Z",
  1043. ]:
  1044. self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
  1045. self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
  1046. self.assertEqual(b.__format__(fmt), 'B')
  1047. def test_more_ctime(self):
  1048. # Test fields that TestDate doesn't touch.
  1049. import time
  1050. t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
  1051. self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
  1052. # Oops! The next line fails on Win2K under MSVC 6, so it's commented
  1053. # out. The difference is that t.ctime() produces " 2" for the day,
  1054. # but platform ctime() produces "02" for the day. According to
  1055. # C99, t.ctime() is correct here.
  1056. # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
  1057. # So test a case where that difference doesn't matter.
  1058. t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
  1059. self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
  1060. def test_tz_independent_comparing(self):
  1061. dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
  1062. dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
  1063. dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
  1064. self.assertEqual(dt1, dt3)
  1065. self.assertTrue(dt2 > dt3)
  1066. # Make sure comparison doesn't forget microseconds, and isn't done
  1067. # via comparing a float timestamp (an IEEE double doesn't have enough
  1068. # precision to span microsecond resolution across years 1 thru 9999,
  1069. # so comparing via timestamp necessarily calls some distinct values
  1070. # equal).
  1071. dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
  1072. us = timedelta(microseconds=1)
  1073. dt2 = dt1 + us
  1074. self.assertEqual(dt2 - dt1, us)
  1075. self.assertTrue(dt1 < dt2)
  1076. def test_strftime_with_bad_tzname_replace(self):
  1077. # verify ok if tzinfo.tzname().replace() returns a non-string
  1078. class MyTzInfo(FixedOffset):
  1079. def tzname(self, dt):
  1080. class MyStr(str):
  1081. def replace(self, *args):
  1082. return None
  1083. return MyStr('name')
  1084. t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
  1085. self.assertRaises(TypeError, t.strftime, '%Z')
  1086. def test_bad_constructor_arguments(self):
  1087. # bad years
  1088. self.theclass(MINYEAR, 1, 1) # no exception
  1089. self.theclass(MAXYEAR, 1, 1) # no exception
  1090. self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
  1091. self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
  1092. # bad months
  1093. self.theclass(2000, 1, 1) # no exception
  1094. self.theclass(2000, 12, 1) # no exception
  1095. self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
  1096. self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
  1097. # bad days
  1098. self.theclass(2000, 2, 29) # no exception
  1099. self.theclass(2004, 2, 29) # no exception
  1100. self.theclass(2400, 2, 29) # no exception
  1101. self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
  1102. self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
  1103. self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
  1104. self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
  1105. self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
  1106. self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
  1107. # bad hours
  1108. self.theclass(2000, 1, 31, 0) # no exception
  1109. self.theclass(2000, 1, 31, 23) # no exception
  1110. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
  1111. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
  1112. # bad minutes
  1113. self.theclass(2000, 1, 31, 23, 0) # no exception
  1114. self.theclass(2000, 1, 31, 23, 59) # no exception
  1115. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
  1116. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
  1117. # bad seconds
  1118. self.theclass(2000, 1, 31, 23, 59, 0) # no exception
  1119. self.theclass(2000, 1, 31, 23, 59, 59) # no exception
  1120. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
  1121. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
  1122. # bad microseconds
  1123. self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
  1124. self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
  1125. self.assertRaises(ValueError, self.theclass,
  1126. 2000, 1, 31, 23, 59, 59, -1)
  1127. self.assertRaises(ValueError, self.theclass,
  1128. 2000, 1, 31, 23, 59, 59,
  1129. 1000000)
  1130. def test_hash_equality(self):
  1131. d = self.theclass(2000, 12, 31, 23, 30, 17)
  1132. e = self.theclass(2000, 12, 31, 23, 30, 17)
  1133. self.assertEqual(d, e)
  1134. self.assertEqual(hash(d), hash(e))
  1135. dic = {d: 1}
  1136. dic[e] = 2
  1137. self.assertEqual(len(dic), 1)
  1138. self.assertEqual(dic[d], 2)
  1139. self.assertEqual(dic[e], 2)
  1140. d = self.theclass(2001, 1, 1, 0, 5, 17)
  1141. e = self.theclass(2001, 1, 1, 0, 5, 17)
  1142. self.assertEqual(d, e)
  1143. self.assertEqual(hash(d), hash(e))
  1144. dic = {d: 1}
  1145. dic[e] = 2
  1146. self.assertEqual(len(dic), 1)
  1147. self.assertEqual(dic[d], 2)
  1148. self.assertEqual(dic[e], 2)
  1149. def test_computations(self):
  1150. a = self.theclass(2002, 1, 31)
  1151. b = self.theclass(1956, 1, 31)
  1152. diff = a-b
  1153. self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
  1154. self.assertEqual(diff.seconds, 0)
  1155. self.assertEqual(diff.microseconds, 0)
  1156. a = self.theclass(2002, 3, 2, 17, 6)
  1157. millisec = timedelta(0, 0, 1000)
  1158. hour = timedelta(0, 3600)
  1159. day = timedelta(1)
  1160. week = timedelta(7)
  1161. self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
  1162. self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
  1163. self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
  1164. self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
  1165. self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
  1166. self.assertEqual(a - hour, a + -hour)
  1167. self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
  1168. self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
  1169. self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
  1170. self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
  1171. self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
  1172. self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
  1173. self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
  1174. self.assertEqual((a + week) - a, week)
  1175. self.assertEqual((a + day) - a, day)
  1176. self.assertEqual((a + hour) - a, hour)
  1177. self.assertEqual((a + millisec) - a, millisec)
  1178. self.assertEqual((a - week) - a, -week)
  1179. self.assertEqual((a - day) - a, -day)
  1180. self.assertEqual((a - hour) - a, -hour)
  1181. self.assertEqual((a - millisec) - a, -millisec)
  1182. self.assertEqual(a - (a + week), -week)
  1183. self.assertEqual(a - (a + day), -day)
  1184. self.assertEqual(a - (a + hour), -hour)
  1185. self.assertEqual(a - (a + millisec), -millisec)
  1186. self.assertEqual(a - (a - week), week)
  1187. self.assertEqual(a - (a - day), day)
  1188. self.assertEqual(a - (a - hour), hour)
  1189. self.assertEqual(a - (a - millisec), millisec)
  1190. self.assertEqual(a + (week + day + hour + millisec),
  1191. self.theclass(2002, 3, 10, 18, 6, 0, 1000))
  1192. self.assertEqual(a + (week + day + hour + millisec),
  1193. (((a + week) + day) + hour) + millisec)
  1194. self.assertEqual(a - (week + day + hour + millisec),
  1195. self.theclass(2002, 2, 22, 16, 5, 59, 999000))
  1196. self.assertEqual(a - (week + day + hour + millisec),
  1197. (((a - week) - day) - hour) - millisec)
  1198. # Add/sub ints, longs, floats should be illegal
  1199. for i in 1, 1L, 1.0:
  1200. self.assertRaises(TypeError, lambda: a+i)
  1201. self.assertRaises(TypeError, lambda: a-i)
  1202. self.assertRaises(TypeError, lambda: i+a)
  1203. self.assertRaises(TypeError, lambda: i-a)
  1204. # delta - datetime is senseless.
  1205. self.assertRaises(TypeError, lambda: day - a)
  1206. # mixing datetime and (delta or datetime) via * or // is senseless
  1207. self.assertRaises(TypeError, lambda: day * a)
  1208. self.assertRaises(TypeError, lambda: a * day)
  1209. self.assertRaises(TypeError, lambda: day // a)
  1210. self.assertRaises(TypeError, lambda: a // day)
  1211. self.assertRaises(TypeError, lambda: a * a)
  1212. self.assertRaises(TypeError, lambda: a // a)
  1213. # datetime + datetime is senseless
  1214. self.assertRaises(TypeError, lambda: a + a)
  1215. def test_pickling(self):
  1216. args = 6, 7, 23, 20, 59, 1, 64**2
  1217. orig = self.theclass(*args)
  1218. for pickler, unpickler, proto in pickle_choices:
  1219. green = pickler.dumps(orig, proto)
  1220. derived = unpickler.loads(green)
  1221. self.assertEqual(orig, derived)
  1222. def test_more_pickling(self):
  1223. a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
  1224. for proto in range(pickle.HIGHEST_PROTOCOL + 1):
  1225. s = pickle.dumps(a, proto)
  1226. b = pickle.loads(s)
  1227. self.assertEqual(b.year, 2003)
  1228. self.assertEqual(b.month, 2)
  1229. self.assertEqual(b.day, 7)
  1230. def test_pickling_subclass_datetime(self):
  1231. args = 6, 7, 23, 20, 59, 1, 64**2
  1232. orig = SubclassDatetime(*args)
  1233. for pickler, unpickler, proto in pickle_choices:
  1234. green = pickler.dumps(orig, proto)
  1235. derived = unpickler.loads(green)
  1236. self.assertEqual(orig, derived)
  1237. def test_more_compare(self):
  1238. # The test_compare() inherited from TestDate covers the error cases.
  1239. # We just want to test lexicographic ordering on the members datetime
  1240. # has that date lacks.
  1241. args = [2000, 11, 29, 20, 58, 16, 999998]
  1242. t1 = self.theclass(*args)
  1243. t2 = self.theclass(*args)
  1244. self.assertTrue(t1 == t2)
  1245. self.assertTrue(t1 <= t2)
  1246. self.assertTrue(t1 >= t2)
  1247. self.assertFalse(t1 != t2)
  1248. self.assertFalse(t1 < t2)
  1249. self.assertFalse(t1 > t2)
  1250. self.assertEqual(cmp(t1, t2), 0)
  1251. self.assertEqual(cmp(t2, t1), 0)
  1252. for i in range(len(args)):
  1253. newargs = args[:]
  1254. newargs[i] = args[i] + 1
  1255. t2 = self.theclass(*newargs) # this is larger than t1
  1256. self.assertTrue(t1 < t2)
  1257. self.assertTrue(t2 > t1)
  1258. self.assertTrue(t1 <= t2)
  1259. self.assertTrue(t2 >= t1)
  1260. self.assertTrue(t1 != t2)
  1261. self.assertTrue(t2 != t1)
  1262. self.assertFalse(t1 == t2)
  1263. self.assertFalse(t2 == t1)
  1264. self.assertFalse(t1 > t2)
  1265. self.assertFalse(t2 < t1)
  1266. self.assertFalse(t1 >= t2)
  1267. self.assertFalse(t2 <= t1)
  1268. self.assertEqual(cmp(t1, t2), -1)
  1269. self.assertEqual(cmp(t2, t1), 1)
  1270. # A helper for timestamp constructor tests.
  1271. def verify_field_equality(self, expected, got):
  1272. self.assertEqual(expected.tm_year, got.year)
  1273. self.assertEqual(expected.tm_mon, got.month)
  1274. self.assertEqual(expected.tm_mday, got.day)
  1275. self.assertEqual(expected.tm_hour, got.hour)
  1276. self.assertEqual(expected.tm_min, got.minute)
  1277. self.assertEqual(expected.tm_sec, got.second)
  1278. def test_fromtimestamp(self):
  1279. import time
  1280. ts = time.time()
  1281. expected = time.localtime(ts)
  1282. got = self.theclass.fromtimestamp(ts)
  1283. self.verify_field_equality(expected, got)
  1284. def test_utcfromtimestamp(self):
  1285. import time
  1286. ts = time.time()
  1287. expected = time.gmtime(ts)
  1288. got = self.theclass.utcfromtimestamp(ts)
  1289. self.verify_field_equality(expected, got)
  1290. def test_microsecond_rounding(self):
  1291. # Test whether fromtimestamp "rounds up" floats that are less
  1292. # than one microsecond smaller than an integer.
  1293. self.assertEqual(self.theclass.fromtimestamp(0.9999999),
  1294. self.theclass.fromtimestamp(1))
  1295. def test_insane_fromtimestamp(self):
  1296. # It's possible that some platform maps time_t to double,
  1297. # and that this test will fail there. This test should
  1298. # exempt such platforms (provided they return reasonable
  1299. # results!).
  1300. for insane in -1e200, 1e200:
  1301. self.assertRaises(ValueError, self.theclass.fromtimestamp,
  1302. insane)
  1303. def test_insane_utcfromtimestamp(self):
  1304. # It's possible that some platform maps time_t to double,
  1305. # and that this test will fail there. This test should
  1306. # exempt such platforms (provided they return reasonable
  1307. # results!).
  1308. for insane in -1e200, 1e200:
  1309. self.assertRaises(ValueError, self.theclass.utcfromtimestamp,
  1310. insane)
  1311. @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
  1312. def test_negative_float_fromtimestamp(self):
  1313. # The result is tz-dependent; at least test that this doesn't
  1314. # fail (like it did before bug 1646728 was fixed).
  1315. self.theclass.fromtimestamp(-1.05)
  1316. @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
  1317. def test_negative_float_utcfromtimestamp(self):
  1318. d = self.theclass.utcfromtimestamp(-1.05)
  1319. self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
  1320. def test_utcnow(self):
  1321. import time
  1322. # Call it a success if utcnow() and utcfromtimestamp() are within
  1323. # a second of each other.
  1324. tolerance = timedelta(seconds=1)
  1325. for dummy in range(3):
  1326. from_now = self.theclass.utcnow()
  1327. from_timestamp = self.theclass.utcfromtimestamp(time.time())
  1328. if abs(from_timestamp - from_now) <= tolerance:
  1329. break
  1330. # Else try again a few times.
  1331. self.assertLessEqual(abs(from_timestamp - from_now), tolerance)
  1332. def test_strptime(self):
  1333. import _strptime
  1334. string = '2004-12-01 13:02:47.197'
  1335. format = '%Y-%m-%d %H:%M:%S.%f'
  1336. result, frac = _strptime._strptime(string, format)
  1337. expected = self.theclass(*(result[0:6]+(frac,)))
  1338. got = self.theclass.strptime(string, format)
  1339. self.assertEqual(expected, got)
  1340. def test_more_timetuple(self):
  1341. # This tests fields beyond those tested by the TestDate.test_timetuple.
  1342. t = self.theclass(2004, 12, 31, 6, 22, 33)
  1343. self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
  1344. self.assertEqual(t.timetuple(),
  1345. (t.year, t.month, t.day,
  1346. t.hour, t.minute, t.second,
  1347. t.weekday(),
  1348. t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
  1349. -1))
  1350. tt = t.timetuple()
  1351. self.assertEqual(tt.tm_year, t.year)
  1352. self.assertEqual(tt.tm_mon, t.month)
  1353. self.assertEqual(tt.tm_mday, t.day)
  1354. self.assertEqual(tt.tm_hour, t.hour)
  1355. self.assertEqual(tt.tm_min, t.minute)
  1356. self.assertEqual(tt.tm_sec, t.second)
  1357. self.assertEqual(tt.tm_wday, t.weekday())
  1358. self.assertEqual(tt.tm_yday, t.toordinal() -
  1359. date(t.year, 1, 1).toordinal() + 1)
  1360. self.assertEqual(tt.tm_isdst, -1)
  1361. def test_more_strftime(self):
  1362. # This tests fields beyond those tested by the TestDate.test_strftime.
  1363. t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
  1364. self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
  1365. "12 31 04 000047 33 22 06 366")
  1366. def test_extract(self):
  1367. dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
  1368. self.assertEqual(dt.date(), date(2002, 3, 4))
  1369. self.assertEqual(dt.time(), time(18, 45, 3, 1234))
  1370. def test_combine(self):
  1371. d = date(2002, 3, 4)
  1372. t = time(18, 45, 3, 1234)
  1373. expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
  1374. combine = self.theclass.combine
  1375. dt = combine(d, t)
  1376. self.assertEqual(dt, expected)
  1377. dt = combine(time=t, date=d)
  1378. self.assertEqual(dt, expected)
  1379. self.assertEqual(d, dt.date())
  1380. self.assertEqual(t, dt.time())
  1381. self.assertEqual(dt, combine(dt.date(), dt.time()))
  1382. self.assertRaises(TypeError, combine) # need an arg
  1383. self.assertRaises(TypeError, combine, d) # need two args
  1384. self.assertRaises(TypeError, combine, t, d) # args reversed
  1385. self.assertRaises(TypeError, combine, d, t, 1) # too many args
  1386. self.assertRaises(TypeError, combine, "date", "time") # wrong types
  1387. def test_replace(self):
  1388. cls = self.theclass
  1389. args = [1, 2, 3, 4, 5, 6, 7]
  1390. base = cls(*args)
  1391. self.assertEqual(base, base.replace())
  1392. i = 0
  1393. for name, newval in (("year", 2),
  1394. ("month", 3),
  1395. ("day", 4),
  1396. ("hour", 5),
  1397. ("minute", 6),
  1398. ("second", 7),
  1399. ("microsecond", 8)):
  1400. newargs = args[:]
  1401. newargs[i] = newval
  1402. expected = cls(*newargs)
  1403. got = base.replace(**{name: newval})
  1404. self.assertEqual(expected, got)
  1405. i += 1
  1406. # Out of bounds.
  1407. base = cls(2000, 2, 29)
  1408. self.assertRaises(ValueError, base.replace, year=2001)
  1409. def test_astimezone(self):
  1410. # Pretty boring! The TZ test is more interesting here. astimezone()
  1411. # simply can't be applied to a naive object.
  1412. dt = self.theclass.now()
  1413. f = FixedOffset(44, "")
  1414. self.assertRaises(TypeError, dt.astimezone) # not enough args
  1415. self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
  1416. self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
  1417. self.assertRaises(ValueError, dt.astimezone, f) # naive
  1418. self.assertRaises(ValueError, dt.astimezone, tz=f) # naive
  1419. class Bogus(tzinfo):
  1420. def utcoffset(self, dt): return None
  1421. def dst(self, dt): return timedelta(0)
  1422. bog = Bogus()
  1423. self.assertRaises(ValueError, dt.astimezone, bog) # naive
  1424. class AlsoBogus(tzinfo):
  1425. def utcoffset(self, dt): return timedelta(0)
  1426. def dst(self, dt): return None
  1427. alsobog = AlsoBogus()
  1428. self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
  1429. def test_subclass_datetime(self):
  1430. class C(self.theclass):
  1431. theAnswer = 42
  1432. def __new__(cls, *args, **kws):
  1433. temp = kws.copy()
  1434. extra = temp.pop('extra')
  1435. result = self.theclass.__new__(cls, *args, **temp)
  1436. result.extra = extra
  1437. return result
  1438. def newmeth(self, start):
  1439. return start + self.year + self.month + self.second
  1440. args = 2003, 4, 14, 12, 13, 41
  1441. dt1 = self.theclass(*args)
  1442. dt2 = C(*args, **{'extra': 7})
  1443. self.assertEqual(dt2.__class__, C)
  1444. self.assertEqual(dt2.theAnswer, 42)
  1445. self.assertEqual(dt2.extra, 7)
  1446. self.assertEqual(dt1.toordinal(), dt2.toordinal())
  1447. self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
  1448. dt1.second - 7)
  1449. class SubclassTime(time):
  1450. sub_var = 1
  1451. class TestTime(HarmlessMixedComparison, unittest.TestCase):
  1452. theclass = time
  1453. def test_basic_attributes(self):
  1454. t = self.theclass(12, 0)
  1455. self.assertEqual(t.hour, 12)
  1456. self.assertEqual(t.minute, 0)
  1457. self.assertEqual(t.second, 0)
  1458. self.assertEqual(t.microsecond, 0)
  1459. def test_basic_attributes_nonzero(self):
  1460. # Make sure all attributes are non-zero so bugs in
  1461. # bit-shifting access show up.
  1462. t = self.theclass(12, 59, 59, 8000)
  1463. self.assertEqual(t.hour, 12)
  1464. self.assertEqual(t.minute, 59)
  1465. self.assertEqual(t.second, 59)
  1466. self.assertEqual(t.microsecond, 8000)
  1467. def test_roundtrip(self):
  1468. t = self.theclass(1, 2, 3, 4)
  1469. # Verify t -> string -> time identity.
  1470. s = repr(t)
  1471. self.assertTrue(s.startswith('datetime.'))
  1472. s = s[9:]
  1473. t2 = eval(s)
  1474. self.assertEqual(t, t2)
  1475. # Verify identity via reconstructing from pieces.
  1476. t2 = self.theclass(t.hour, t.minute, t.second,
  1477. t.microsecond)
  1478. self.assertEqual(t, t2)
  1479. def test_comparing(self):
  1480. args = [1, 2, 3, 4]
  1481. t1 = self.theclass(*args)
  1482. t2 = self.theclass(*args)
  1483. self.assertTrue(t1 == t2)
  1484. self.assertTrue(t1 <= t2)
  1485. self.assertTrue(t1 >= t2)
  1486. self.assertFalse(t1 != t2)
  1487. self.assertFalse(t1 < t2)
  1488. self.assertFalse(t1 > t2)
  1489. self.assertEqual(cmp(t1, t2), 0)
  1490. self.assertEqual(cmp(t2, t1), 0)
  1491. for i in range(len(args)):
  1492. newargs = args[:]
  1493. newargs[i] = args[i] + 1
  1494. t2 = self.theclass(*newargs) # this is larger than t1
  1495. self.assertTrue(t1 < t2)
  1496. self.assertTrue(t2 > t1)
  1497. self.assertTrue(t1 <= t2)
  1498. self.assertTrue(t2 >= t1)
  1499. self.assertTrue(t1 != t2)
  1500. self.assertTrue(t2 != t1)
  1501. self.assertFalse(t1 == t2)
  1502. self.assertFalse(t2 == t1)
  1503. self.assertFalse(t1 > t2)
  1504. self.assertFalse(t2 < t1)
  1505. self.assertFalse(t1 >= t2)
  1506. self.assertFalse(t2 <= t1)
  1507. self.assertEqual(cmp(t1, t2), -1)
  1508. self.assertEqual(cmp(t2, t1), 1)
  1509. for badarg in OTHERSTUFF:
  1510. self.assertEqual(t1 == badarg, False)
  1511. self.assertEqual(t1 != badarg, True)
  1512. self.assertEqual(badarg == t1, False)
  1513. self.assertEqual(badarg != t1, True)
  1514. self.assertRaises(TypeError, lambda: t1 <= badarg)
  1515. self.assertRaises(TypeError, lambda: t1 < badarg)
  1516. self.assertRaises(TypeError, lambda: t1 > badarg)
  1517. self.assertRaises(TypeError, lambda: t1 >= badarg)
  1518. self.assertRaises(TypeError, lambda: badarg <= t1)
  1519. self.assertRaises(TypeError, lambda: badarg < t1)
  1520. self.assertRaises(TypeError, lambda: badarg > t1)
  1521. self.assertRaises(TypeError, lambda: badarg >= t1)
  1522. def test_bad_constructor_arguments(self):
  1523. # bad hours
  1524. self.theclass(0, 0) # no exception
  1525. self.theclass(23, 0) # no exception
  1526. self.assertRaises(ValueError, self.theclass, -1, 0)
  1527. self.assertRaises(ValueError, self.theclass, 24, 0)
  1528. # bad minutes
  1529. self.theclass(23, 0) # no exception
  1530. self.theclass(23, 59) # no exception
  1531. self.assertRaises(ValueError, self.theclass, 23, -1)
  1532. self.assertRaises(ValueError, self.theclass, 23, 60)
  1533. # bad seconds
  1534. self.theclass(23, 59, 0) # no exception
  1535. self.theclass(23, 59, 59) # no exception
  1536. self.assertRaises(ValueError, self.theclass, 23, 59, -1)
  1537. self.assertRaises(ValueError, self.theclass, 23, 59, 60)
  1538. # bad microseconds
  1539. self.theclass(23, 59, 59, 0) # no exception
  1540. self.theclass(23, 59, 59, 999999) # no exception
  1541. self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
  1542. self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
  1543. def test_hash_equality(self):
  1544. d = self.theclass(23, 30, 17)
  1545. e = self.theclass(23, 30, 17)
  1546. self.assertEqual(d, e)
  1547. self.assertEqual(hash(d), hash(e))
  1548. dic = {d: 1}
  1549. dic[e] = 2
  1550. self.assertEqual(len(dic), 1)
  1551. self.assertEqual(dic[d], 2)
  1552. self.assertEqual(dic[e], 2)
  1553. d = self.theclass(0, 5, 17)
  1554. e = self.theclass(0, 5, 17)
  1555. self.assertEqual(d, e)
  1556. self.assertEqual(hash(d), hash(e))
  1557. dic = {d: 1}
  1558. dic[e] = 2
  1559. self.assertEqual(len(dic), 1)
  1560. self.assertEqual(dic[d], 2)
  1561. self.assertEqual(dic[e], 2)
  1562. def test_isoformat(self):
  1563. t = self.theclass(4, 5, 1, 123)
  1564. self.assertEqual(t.isoformat(), "04:05:01.000123")
  1565. self.assertEqual(t.isoformat(), str(t))
  1566. t = self.theclass()
  1567. self.assertEqual(t.isoformat(), "00:00:00")
  1568. self.assertEqual(t.isoformat(), str(t))
  1569. t = self.theclass(microsecond=1)
  1570. self.assertEqual(t.isoformat(), "00:00:00.000001")
  1571. self.assertEqual(t.isoformat(), str(t))
  1572. t = self.theclass(microsecond=10)
  1573. self.assertEqual(t.isoformat(), "00:00:00.000010")
  1574. self.assertEqual(t.isoformat(), str(t))
  1575. t = self.theclass(microsecond=100)
  1576. self.assertEqual(t.isoformat(), "00:00:00.000100")
  1577. self.assertEqual(t.isoformat(), str(t))
  1578. t = self.theclass(microsecond=1000)
  1579. self.assertEqual(t.isoformat(), "00:00:00.001000")
  1580. self.assertEqual(t.isoformat(), str(t))
  1581. t = self.theclass(microsecond=10000)
  1582. self.assertEqual(t.isoformat(), "00:00:00.010000")
  1583. self.assertEqual(t.isoformat(), str(t))
  1584. t = self.theclass(microsecond=100000)
  1585. self.assertEqual(t.isoformat(), "00:00:00.100000")
  1586. self.assertEqual(t.isoformat(), str(t))
  1587. def test_1653736(self):
  1588. # verify it doesn't accept extra keyword arguments
  1589. t = self.theclass(second=1)
  1590. self.assertRaises(TypeError, t.isoformat, foo=3)
  1591. def test_strftime(self):
  1592. t = self.theclass(1, 2, 3, 4)
  1593. self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
  1594. # A naive object replaces %z and %Z with empty strings.
  1595. self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
  1596. def test_format(self):
  1597. t = self.theclass(1, 2, 3, 4)
  1598. self.assertEqual(t.__format__(''), str(t))
  1599. # check that a derived class's __str__() gets called
  1600. class A(self.theclass):
  1601. def __str__(self):
  1602. return 'A'
  1603. a = A(1, 2, 3, 4)
  1604. self.assertEqual(a.__format__(''), 'A')
  1605. # check that a derived class's strftime gets called
  1606. class B(self.theclass):
  1607. def strftime(self, format_spec):
  1608. return 'B'
  1609. b = B(1, 2, 3, 4)
  1610. self.assertEqual(b.__format__(''), str(t))
  1611. for fmt in ['%H %M %S',
  1612. ]:
  1613. self.assertEqual(t.__format__(fmt), t.strftime(fmt))
  1614. self.assertEqual(a.__format__(fmt), t.strftime(fmt))
  1615. self.assertEqual(b.__format__(fmt), 'B')
  1616. def test_str(self):
  1617. self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
  1618. self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
  1619. self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
  1620. self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
  1621. self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
  1622. def test_repr(self):
  1623. name = 'datetime.' + self.theclass.__name__
  1624. self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
  1625. "%s(1, 2, 3, 4)" % name)
  1626. self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
  1627. "%s(10, 2, 3, 4000)" % name)
  1628. self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
  1629. "%s(0, 2, 3, 400000)" % name)
  1630. self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
  1631. "%s(12, 2, 3)" % name)
  1632. self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
  1633. "%s(23, 15)" % name)
  1634. def test_resolution_info(self):
  1635. self.assertIsInstance(self.theclass.min, self.theclass)
  1636. self.assertIsInstance(self.theclass.max, self.theclass)
  1637. self.assertIsInstance(self.theclass.resolution, timedelta)
  1638. self.assertTrue(self.theclass.max > self.theclass.min)
  1639. def test_pickling(self):
  1640. args = 20, 59, 16, 64**2
  1641. orig = self.theclass(*args)
  1642. for pickler, unpickler, proto in pickle_choices:
  1643. green = pickler.dumps(orig, proto)
  1644. derived = unpickler.loads(green)
  1645. self.assertEqual(orig, derived)
  1646. def test_pickling_subclass_time(self):
  1647. args = 20, 59, 16, 64**2
  1648. orig = SubclassTime(*args)
  1649. for pickler, unpickler, proto in pickle_choices:
  1650. green = pickler.dumps(orig, proto)
  1651. derived = unpickler.loads(green)
  1652. self.assertEqual(orig, derived)
  1653. def test_bool(self):
  1654. cls = self.theclass
  1655. self.assertTrue(cls(1))
  1656. self.assertTrue(cls(0, 1))
  1657. self.assertTrue(cls(0, 0, 1))
  1658. self.assertTrue(cls(0, 0, 0, 1))
  1659. self.assertFalse(cls(0))
  1660. self.assertFalse(cls())
  1661. def test_replace(self):
  1662. cls = self.theclass
  1663. args = [1, 2, 3, 4]
  1664. base = cls(*args)
  1665. self.assertEqual(base, base.replace())
  1666. i = 0
  1667. for name, newval in (("hour", 5),
  1668. ("minute", 6),
  1669. ("second", 7),
  1670. ("microsecond", 8)):
  1671. newargs = args[:]
  1672. newargs[i] = newval
  1673. expected = cls(*newargs)
  1674. got = base.replace(**{name: newval})
  1675. self.assertEqual(expected, got)
  1676. i += 1
  1677. # Out of bounds.
  1678. base = cls(1)
  1679. self.assertRaises(ValueError, base.replace, hour=24)
  1680. self.assertRaises(ValueError, base.replace, minute=-1)
  1681. self.assertRaises(ValueError, base.replace, second=100)
  1682. self.assertRaises(ValueError, base.replace, microsecond=1000000)
  1683. def test_subclass_time(self):
  1684. class C(self.theclass):
  1685. theAnswer = 42
  1686. def __new__(cls, *args, **kws):
  1687. temp = kws.copy()
  1688. extra = temp.pop('extra')
  1689. result = self.theclass.__new__(cls, *args, **temp)
  1690. result.extra = extra
  1691. return result
  1692. def newmeth(self, start):
  1693. return start + self.hour + self.second
  1694. args = 4, 5, 6
  1695. dt1 = self.theclass(*args)
  1696. dt2 = C(*args, **{'extra': 7})
  1697. self.assertEqual(dt2.__class__, C)
  1698. self.assertEqual(dt2.theAnswer, 42)
  1699. self.assertEqual(dt2.extra, 7)
  1700. self.assertEqual(dt1.isoformat(), dt2.isoformat())
  1701. self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
  1702. def test_backdoor_resistance(self):
  1703. # see TestDate.test_backdoor_resistance().
  1704. base = '2:59.0'
  1705. for hour_byte in ' ', '9', chr(24), '\xff':
  1706. self.assertRaises(TypeError, self.theclass,
  1707. hour_byte + base[1:])
  1708. # A mixin for classes with a tzinfo= argument. Subclasses must define
  1709. # theclass as a class attribute, and theclass(1, 1, 1, tzinfo=whatever)
  1710. # must be legit (which is true for time and datetime).
  1711. class TZInfoBase:
  1712. def test_argument_passing(self):
  1713. cls = self.theclass
  1714. # A datetime passes itself on, a time passes None.
  1715. class introspective(tzinfo):
  1716. def tzname(self, dt): return dt and "real" or "none"
  1717. def utcoffset(self, dt):
  1718. return timedelta(minutes = dt and 42 or -42)
  1719. dst = utcoffset
  1720. obj = cls(1, 2, 3, tzinfo=introspective())
  1721. expected = cls is time and "none" or "real"
  1722. self.assertEqual(obj.tzname(), expected)
  1723. expected = timedelta(minutes=(cls is time and -42 or 42))
  1724. self.assertEqual(obj.utcoffset(), expected)
  1725. self.assertEqual(obj.dst(), expected)
  1726. def test_bad_tzinfo_classes(self):
  1727. cls = self.theclass
  1728. self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
  1729. class NiceTry(object):
  1730. def __init__(self): pass
  1731. def utcoffset(self, dt): pass
  1732. self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
  1733. class BetterTry(tzinfo):
  1734. def __init__(self): pass
  1735. def utcoffset(self, dt): pass
  1736. b = BetterTry()
  1737. t = cls(1, 1, 1, tzinfo=b)
  1738. self.assertIs(t.tzinfo, b)
  1739. def test_utc_offset_out_of_bounds(self):
  1740. class Edgy(tzinfo):
  1741. def __init__(self, offset):
  1742. self.offset = timedelta(minutes=offset)
  1743. def utcoffset(self, dt):
  1744. return self.offset
  1745. cls = self.theclass
  1746. for offset, legit in ((-1440, False),
  1747. (-1439, True),
  1748. (1439, True),
  1749. (1440, False)):
  1750. if cls is time:
  1751. t = cls(1, 2, 3, tzinfo=Edgy(offset))
  1752. elif cls is datetime:
  1753. t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
  1754. else:
  1755. assert 0, "impossible"
  1756. if legit:
  1757. aofs = abs(offset)
  1758. h, m = divmod(aofs, 60)
  1759. tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
  1760. if isinstance(t, datetime):
  1761. t = t.timetz()
  1762. self.assertEqual(str(t), "01:02:03" + tag)
  1763. else:
  1764. self.assertRaises(ValueError, str, t)
  1765. def test_tzinfo_classes(self):
  1766. cls = self.theclass
  1767. class C1(tzinfo):
  1768. def utcoffset(self, dt): return None
  1769. def dst(self, dt): return None
  1770. def tzname(self, dt): return None
  1771. for t in (cls(1, 1, 1),
  1772. cls(1, 1, 1, tzinfo=None),
  1773. cls(1, 1, 1, tzinfo=C1())):
  1774. self.assertIsNone(t.utcoffset())
  1775. self.assertIsNone(t.dst())
  1776. self.assertIsNone(t.tzname())
  1777. class C3(tzinfo):
  1778. def utcoffset(self, dt): return timedelta(minutes=-1439)
  1779. def dst(self, dt): return timedelta(minutes=1439)
  1780. def tzname(self, dt): return "aname"
  1781. t = cls(1, 1, 1, tzinfo=C3())
  1782. self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
  1783. self.assertEqual(t.dst(), timedelta(minutes=1439))
  1784. self.assertEqual(t.tzname(), "aname")
  1785. # Wrong types.
  1786. class C4(tzinfo):
  1787. def utcoffset(self, dt): return "aname"
  1788. def dst(self, dt): return 7
  1789. def tzname(self, dt): return 0
  1790. t = cls(1, 1, 1, tzinfo=C4())
  1791. self.assertRaises(TypeError, t.utcoffset)
  1792. self.assertRaises(TypeError, t.dst)
  1793. self.assertRaises(TypeError, t.tzname)
  1794. # Offset out of range.
  1795. class C6(tzinfo):
  1796. def utcoffset(self, dt): return timedelta(hours=-24)
  1797. def dst(self, dt): return timedelta(hours=24)
  1798. t = cls(1, 1, 1, tzinfo=C6())
  1799. self.assertRaises(ValueError, t.utcoffset)
  1800. self.assertRaises(ValueError, t.dst)
  1801. # Not a whole number of minutes.
  1802. class C7(tzinfo):
  1803. def utcoffset(self, dt): return timedelta(seconds=61)
  1804. def dst(self, dt): return timedelta(microseconds=-81)
  1805. t = cls(1, 1, 1, tzinfo=C7())
  1806. self.assertRaises(ValueError, t.utcoffset)
  1807. self.assertRaises(ValueError, t.dst)
  1808. def test_aware_compare(self):
  1809. cls = self.theclass
  1810. # Ensure that utcoffset() gets ignored if the comparands have
  1811. # the same tzinfo member.
  1812. class OperandDependentOffset(tzinfo):
  1813. def utcoffset(self, t):
  1814. if t.minute < 10:
  1815. # d0 and d1 equal after adjustment
  1816. return timedelta(minutes=t.minute)
  1817. else:
  1818. # d2 off in the weeds
  1819. return timedelta(minutes=59)
  1820. base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
  1821. d0 = base.replace(minute=3)
  1822. d1 = base.replace(minute=9)
  1823. d2 = base.replace(minute=11)
  1824. for x in d0, d1, d2:
  1825. for y in d0, d1, d2:
  1826. got = cmp(x, y)
  1827. expected = cmp(x.minute, y.minute)
  1828. self.assertEqual(got, expected)
  1829. # However, if they're different members, uctoffset is not ignored.
  1830. # Note that a time can't actually have an operand-depedent offset,
  1831. # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
  1832. # so skip this test for time.
  1833. if cls is not time:
  1834. d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
  1835. d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
  1836. d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
  1837. for x in d0, d1, d2:
  1838. for y in d0, d1, d2:
  1839. got = cmp(x, y)
  1840. if (x is d0 or x is d1) and (y is d0 or y is d1):
  1841. expected = 0
  1842. elif x is y is d2:
  1843. expected = 0
  1844. elif x is d2:
  1845. expected = -1
  1846. else:
  1847. assert y is d2
  1848. expected = 1
  1849. self.assertEqual(got, expected)
  1850. # Testing time objects with a non-None tzinfo.
  1851. class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
  1852. theclass = time
  1853. def test_empty(self):
  1854. t = self.theclass()
  1855. self.assertEqual(t.hour, 0)
  1856. self.assertEqual(t.minute, 0)
  1857. self.assertEqual(t.second, 0)
  1858. self.assertEqual(t.microsecond, 0)
  1859. self.assertIsNone(t.tzinfo)
  1860. def test_zones(self):
  1861. est = FixedOffset(-300, "EST", 1)
  1862. utc = FixedOffset(0, "UTC", -2)
  1863. met = FixedOffset(60, "MET", 3)
  1864. t1 = time( 7, 47, tzinfo=est)
  1865. t2 = time(12, 47, tzinfo=utc)
  1866. t3 = time(13, 47, tzinfo=met)
  1867. t4 = time(microsecond=40)
  1868. t5 = time(microsecond=40, tzinfo=utc)
  1869. self.assertEqual(t1.tzinfo, est)
  1870. self.assertEqual(t2.tzinfo, utc)
  1871. self.assertEqual(t3.tzinfo, met)
  1872. self.assertIsNone(t4.tzinfo)
  1873. self.assertEqual(t5.tzinfo, utc)
  1874. self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
  1875. self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
  1876. self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
  1877. self.assertIsNone(t4.utcoffset())
  1878. self.assertRaises(TypeError, t1.utcoffset, "no args")
  1879. self.assertEqual(t1.tzname(), "EST")
  1880. self.assertEqual(t2.tzname(), "UTC")
  1881. self.assertEqual(t3.tzname(), "MET")
  1882. self.assertIsNone(t4.tzname())
  1883. self.assertRaises(TypeError, t1.tzname, "no args")
  1884. self.assertEqual(t1.dst(), timedelta(minutes=1))
  1885. self.assertEqual(t2.dst(), timedelta(minutes=-2))
  1886. self.assertEqual(t3.dst(), timedelta(minutes=3))
  1887. self.assertIsNone(t4.dst())
  1888. self.assertRaises(TypeError, t1.dst, "no args")
  1889. self.assertEqual(hash(t1), hash(t2))
  1890. self.assertEqual(hash(t1), hash(t3))
  1891. self.assertEqual(hash(t2), hash(t3))
  1892. self.assertEqual(t1, t2)
  1893. self.assertEqual(t1, t3)
  1894. self.assertEqual(t2, t3)
  1895. self.assertRaises(TypeError, lambda: t4 == t5) # mixed tz-aware & naive
  1896. self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
  1897. self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
  1898. self.assertEqual(str(t1), "07:47:00-05:00")
  1899. self.assertEqual(str(t2), "12:47:00+00:00")
  1900. self.assertEqual(str(t3), "13:47:00+01:00")
  1901. self.assertEqual(str(t4), "00:00:00.000040")
  1902. self.assertEqual(str(t5), "00:00:00.000040+00:00")
  1903. self.assertEqual(t1.isoformat(), "07:47:00-05:00")
  1904. self.assertEqual(t2.isoformat(), "12:47:00+00:00")
  1905. self.assertEqual(t3.isoformat(), "13:47:00+01:00")
  1906. self.assertEqual(t4.isoformat(), "00:00:00.000040")
  1907. self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
  1908. d = 'datetime.time'
  1909. self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
  1910. self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
  1911. self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
  1912. self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
  1913. self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
  1914. self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
  1915. "07:47:00 %Z=EST %z=-0500")
  1916. self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
  1917. self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
  1918. yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
  1919. t1 = time(23, 59, tzinfo=yuck)
  1920. self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
  1921. "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
  1922. # Check that an invalid tzname result raises an exception.
  1923. class Badtzname(tzinfo):
  1924. def tzname(self, dt): return 42
  1925. t = time(2, 3, 4, tzinfo=Badtzname())
  1926. self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
  1927. self.assertRaises(TypeError, t.strftime, "%Z")
  1928. def test_hash_edge_cases(self):
  1929. # Offsets that overflow a basic time.
  1930. t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
  1931. t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
  1932. self.assertEqual(hash(t1), hash(t2))
  1933. t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
  1934. t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
  1935. self.assertEqual(hash(t1), hash(t2))
  1936. def test_pickling(self):
  1937. # Try one without a tzinfo.
  1938. args = 20, 59, 16, 64**2
  1939. orig = self.theclass(*args)
  1940. for pickler, unpickler, proto in pickle_choices:
  1941. green = pickler.dumps(orig, proto)
  1942. derived = unpickler.loads(green)
  1943. self.assertEqual(orig, derived)
  1944. # Try one with a tzinfo.
  1945. tinfo = PicklableFixedOffset(-300, 'cookie')
  1946. orig = self.theclass(5, 6, 7, tzinfo=tinfo)
  1947. for pickler, unpickler, proto in pickle_choices:
  1948. green = pickler.dumps(orig, proto)
  1949. derived = unpickler.loads(green)
  1950. self.assertEqual(orig, derived)
  1951. self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
  1952. self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
  1953. self.assertEqual(derived.tzname(), 'cookie')
  1954. def test_more_bool(self):
  1955. # Test cases with non-None tzinfo.
  1956. cls = self.theclass
  1957. t = cls(0, tzinfo=FixedOffset(-300, ""))
  1958. self.assertTrue(t)
  1959. t = cls(5, tzinfo=FixedOffset(-300, ""))
  1960. self.assertTrue(t)
  1961. t = cls(5, tzinfo=FixedOffset(300, ""))
  1962. self.assertFalse(t)
  1963. t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
  1964. self.assertFalse(t)
  1965. # Mostly ensuring this doesn't overflow internally.
  1966. t = cls(0, tzinfo=FixedOffset(23*60 + 59, ""))
  1967. self.assertTrue(t)
  1968. # But this should yield a value error -- the utcoffset is bogus.
  1969. t = cls(0, tzinfo=FixedOffset(24*60, ""))
  1970. self.assertRaises(ValueError, lambda: bool(t))
  1971. # Likewise.
  1972. t = cls(0, tzinfo=FixedOffset(-24*60, ""))
  1973. self.assertRaises(ValueError, lambda: bool(t))
  1974. def test_replace(self):
  1975. cls = self.theclass
  1976. z100 = FixedOffset(100, "+100")
  1977. zm200 = FixedOffset(timedelta(minutes=-200), "-200")
  1978. args = [1, 2, 3, 4, z100]
  1979. base = cls(*args)
  1980. self.assertEqual(base, base.replace())
  1981. i = 0
  1982. for name, newval in (("hour", 5),
  1983. ("minute", 6),
  1984. ("second", 7),
  1985. ("microsecond", 8),
  1986. ("tzinfo", zm200)):
  1987. newargs = args[:]
  1988. newargs[i] = newval
  1989. expected = cls(*newargs)
  1990. got = base.replace(**{name: newval})
  1991. self.assertEqual(expected, got)
  1992. i += 1
  1993. # Ensure we can get rid of a tzinfo.
  1994. self.assertEqual(base.tzname(), "+100")
  1995. base2 = base.replace(tzinfo=None)
  1996. self.assertIsNone(base2.tzinfo)
  1997. self.assertIsNone(base2.tzname())
  1998. # Ensure we can add one.
  1999. base3 = base2.replace(tzinfo=z100)
  2000. self.assertEqual(base, base3)
  2001. self.assertIs(base.tzinfo, base3.tzinfo)
  2002. # Out of bounds.
  2003. base = cls(1)
  2004. self.assertRaises(ValueError, base.replace, hour=24)
  2005. self.assertRaises(ValueError, base.replace, minute=-1)
  2006. self.assertRaises(ValueError, base.replace, second=100)
  2007. self.assertRaises(ValueError, base.replace, microsecond=1000000)
  2008. def test_mixed_compare(self):
  2009. t1 = time(1, 2, 3)
  2010. t2 = time(1, 2, 3)
  2011. self.assertEqual(t1, t2)
  2012. t2 = t2.replace(tzinfo=None)
  2013. self.assertEqual(t1, t2)
  2014. t2 = t2.replace(tzinfo=FixedOffset(None, ""))
  2015. self.assertEqual(t1, t2)
  2016. t2 = t2.replace(tzinfo=FixedOffset(0, ""))
  2017. self.assertRaises(TypeError, lambda: t1 == t2)
  2018. # In time w/ identical tzinfo objects, utcoffset is ignored.
  2019. class Varies(tzinfo):
  2020. def __init__(self):
  2021. self.offset = timedelta(minutes=22)
  2022. def utcoffset(self, t):
  2023. self.offset += timedelta(minutes=1)
  2024. return self.offset
  2025. v = Varies()
  2026. t1 = t2.replace(tzinfo=v)
  2027. t2 = t2.replace(tzinfo=v)
  2028. self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
  2029. self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
  2030. self.assertEqual(t1, t2)
  2031. # But if they're not identical, it isn't ignored.
  2032. t2 = t2.replace(tzinfo=Varies())
  2033. self.assertTrue(t1 < t2) # t1's offset counter still going up
  2034. def test_subclass_timetz(self):
  2035. class C(self.theclass):
  2036. theAnswer = 42
  2037. def __new__(cls, *args, **kws):
  2038. temp = kws.copy()
  2039. extra = temp.pop('extra')
  2040. result = self.theclass.__new__(cls, *args, **temp)
  2041. result.extra = extra
  2042. return result
  2043. def newmeth(self, start):
  2044. return start + self.hour + self.second
  2045. args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
  2046. dt1 = self.theclass(*args)
  2047. dt2 = C(*args, **{'extra': 7})
  2048. self.assertEqual(dt2.__class__, C)
  2049. self.assertEqual(dt2.theAnswer, 42)
  2050. self.assertEqual(dt2.extra, 7)
  2051. self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
  2052. self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
  2053. # Testing datetime objects with a non-None tzinfo.
  2054. class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
  2055. theclass = datetime
  2056. def test_trivial(self):
  2057. dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
  2058. self.assertEqual(dt.year, 1)
  2059. self.assertEqual(dt.month, 2)
  2060. self.assertEqual(dt.day, 3)
  2061. self.assertEqual(dt.hour, 4)
  2062. self.assertEqual(dt.minute, 5)
  2063. self.assertEqual(dt.second, 6)
  2064. self.assertEqual(dt.microsecond, 7)
  2065. self.assertEqual(dt.tzinfo, None)
  2066. def test_even_more_compare(self):
  2067. # The test_compare() and test_more_compare() inherited from TestDate
  2068. # and TestDateTime covered non-tzinfo cases.
  2069. # Smallest possible after UTC adjustment.
  2070. t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
  2071. # Largest possible after UTC adjustment.
  2072. t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
  2073. tzinfo=FixedOffset(-1439, ""))
  2074. # Make sure those compare correctly, and w/o overflow.
  2075. self.assertTrue(t1 < t2)
  2076. self.assertTrue(t1 != t2)
  2077. self.assertTrue(t2 > t1)
  2078. self.assertTrue(t1 == t1)
  2079. self.assertTrue(t2 == t2)
  2080. # Equal afer adjustment.
  2081. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
  2082. t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
  2083. self.assertEqual(t1, t2)
  2084. # Change t1 not to subtract a minute, and t1 should be larger.
  2085. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
  2086. self.assertTrue(t1 > t2)
  2087. # Change t1 to subtract 2 minutes, and t1 should be smaller.
  2088. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
  2089. self.assertTrue(t1 < t2)
  2090. # Back to the original t1, but make seconds resolve it.
  2091. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
  2092. second=1)
  2093. self.assertTrue(t1 > t2)
  2094. # Likewise, but make microseconds resolve it.
  2095. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
  2096. microsecond=1)
  2097. self.assertTrue(t1 > t2)
  2098. # Make t2 naive and it should fail.
  2099. t2 = self.theclass.min
  2100. self.assertRaises(TypeError, lambda: t1 == t2)
  2101. self.assertEqual(t2, t2)
  2102. # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
  2103. class Naive(tzinfo):
  2104. def utcoffset(self, dt): return None
  2105. t2 = self.theclass(5, 6, 7, tzinfo=Naive())
  2106. self.assertRaises(TypeError, lambda: t1 == t2)
  2107. self.assertEqual(t2, t2)
  2108. # OTOH, it's OK to compare two of these mixing the two ways of being
  2109. # naive.
  2110. t1 = self.theclass(5, 6, 7)
  2111. self.assertEqual(t1, t2)
  2112. # Try a bogus uctoffset.
  2113. class Bogus(tzinfo):
  2114. def utcoffset(self, dt):
  2115. return timedelta(minutes=1440) # out of bounds
  2116. t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
  2117. t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
  2118. self.assertRaises(ValueError, lambda: t1 == t2)
  2119. def test_pickling(self):
  2120. # Try one without a tzinfo.
  2121. args = 6, 7, 23, 20, 59, 1, 64**2
  2122. orig = self.theclass(*args)
  2123. for pickler, unpickler, proto in pickle_choices:
  2124. green = pickler.dumps(orig, proto)
  2125. derived = unpickler.loads(green)
  2126. self.assertEqual(orig, derived)
  2127. # Try one with a tzinfo.
  2128. tinfo = PicklableFixedOffset(-300, 'cookie')
  2129. orig = self.theclass(*args, **{'tzinfo': tinfo})
  2130. derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
  2131. for pickler, unpickler, proto in pickle_choices:
  2132. green = pickler.dumps(orig, proto)
  2133. derived = unpickler.loads(green)
  2134. self.assertEqual(orig, derived)
  2135. self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
  2136. self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
  2137. self.assertEqual(derived.tzname(), 'cookie')
  2138. def test_extreme_hashes(self):
  2139. # If an attempt is made to hash these via subtracting the offset
  2140. # then hashing a datetime object, OverflowError results. The
  2141. # Python implementation used to blow up here.
  2142. t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
  2143. hash(t)
  2144. t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
  2145. tzinfo=FixedOffset(-1439, ""))
  2146. hash(t)
  2147. # OTOH, an OOB offset should blow up.
  2148. t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
  2149. self.assertRaises(ValueError, hash, t)
  2150. def test_zones(self):
  2151. est = FixedOffset(-300, "EST")
  2152. utc = FixedOffset(0, "UTC")
  2153. met = FixedOffset(60, "MET")
  2154. t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
  2155. t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
  2156. t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
  2157. self.assertEqual(t1.tzinfo, est)
  2158. self.assertEqual(t2.tzinfo, utc)
  2159. self.assertEqual(t3.tzinfo, met)
  2160. self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
  2161. self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
  2162. self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
  2163. self.assertEqual(t1.tzname(), "EST")
  2164. self.assertEqual(t2.tzname(), "UTC")
  2165. self.assertEqual(t3.tzname(), "MET")
  2166. self.assertEqual(hash(t1), hash(t2))
  2167. self.assertEqual(hash(t1), hash(t3))
  2168. self.assertEqual(hash(t2), hash(t3))
  2169. self.assertEqual(t1, t2)
  2170. self.assertEqual(t1, t3)
  2171. self.assertEqual(t2, t3)
  2172. self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
  2173. self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
  2174. self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
  2175. d = 'datetime.datetime(2002, 3, 19, '
  2176. self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
  2177. self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
  2178. self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
  2179. def test_combine(self):
  2180. met = FixedOffset(60, "MET")
  2181. d = date(2002, 3, 4)
  2182. tz = time(18, 45, 3, 1234, tzinfo=met)
  2183. dt = datetime.combine(d, tz)
  2184. self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
  2185. tzinfo=met))
  2186. def test_extract(self):
  2187. met = FixedOffset(60, "MET")
  2188. dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
  2189. self.assertEqual(dt.date(), date(2002, 3, 4))
  2190. self.assertEqual(dt.time(), time(18, 45, 3, 1234))
  2191. self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
  2192. def test_tz_aware_arithmetic(self):
  2193. import random
  2194. now = self.theclass.now()
  2195. tz55 = FixedOffset(-330, "west 5:30")
  2196. timeaware = now.time().replace(tzinfo=tz55)
  2197. nowaware = self.theclass.combine(now.date(), timeaware)
  2198. self.assertIs(nowaware.tzinfo, tz55)
  2199. self.assertEqual(nowaware.timetz(), timeaware)
  2200. # Can't mix aware and non-aware.
  2201. self.assertRaises(TypeError, lambda: now - nowaware)
  2202. self.assertRaises(TypeError, lambda: nowaware - now)
  2203. # And adding datetime's doesn't make sense, aware or not.
  2204. self.assertRaises(TypeError, lambda: now + nowaware)
  2205. self.assertRaises(TypeError, lambda: nowaware + now)
  2206. self.assertRaises(TypeError, lambda: nowaware + nowaware)
  2207. # Subtracting should yield 0.
  2208. self.assertEqual(now - now, timedelta(0))
  2209. self.assertEqual(nowaware - nowaware, timedelta(0))
  2210. # Adding a delta should preserve tzinfo.
  2211. delta = timedelta(weeks=1, minutes=12, microseconds=5678)
  2212. nowawareplus = nowaware + delta
  2213. self.assertIs(nowaware.tzinfo, tz55)
  2214. nowawareplus2 = delta + nowaware
  2215. self.assertIs(nowawareplus2.tzinfo, tz55)
  2216. self.assertEqual(nowawareplus, nowawareplus2)
  2217. # that - delta should be what we started with, and that - what we
  2218. # started with should be delta.
  2219. diff = nowawareplus - delta
  2220. self.assertIs(diff.tzinfo, tz55)
  2221. self.assertEqual(nowaware, diff)
  2222. self.assertRaises(TypeError, lambda: delta - nowawareplus)
  2223. self.assertEqual(nowawareplus - nowaware, delta)
  2224. # Make up a random timezone.
  2225. tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
  2226. # Attach it to nowawareplus.
  2227. nowawareplus = nowawareplus.replace(tzinfo=tzr)
  2228. self.assertIs(nowawareplus.tzinfo, tzr)
  2229. # Make sure the difference takes the timezone adjustments into account.
  2230. got = nowaware - nowawareplus
  2231. # Expected: (nowaware base - nowaware offset) -
  2232. # (nowawareplus base - nowawareplus offset) =
  2233. # (nowaware base - nowawareplus base) +
  2234. # (nowawareplus offset - nowaware offset) =
  2235. # -delta + nowawareplus offset - nowaware offset
  2236. expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
  2237. self.assertEqual(got, expected)
  2238. # Try max possible difference.
  2239. min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
  2240. max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
  2241. tzinfo=FixedOffset(-1439, "max"))
  2242. maxdiff = max - min
  2243. self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
  2244. timedelta(minutes=2*1439))
  2245. def test_tzinfo_now(self):
  2246. meth = self.theclass.now
  2247. # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
  2248. base = meth()
  2249. # Try with and without naming the keyword.
  2250. off42 = FixedOffset(42, "42")
  2251. another = meth(off42)
  2252. again = meth(tz=off42)
  2253. self.assertIs(another.tzinfo, again.tzinfo)
  2254. self.assertEqual(another.utcoffset(), timedelta(minutes=42))
  2255. # Bad argument with and w/o naming the keyword.
  2256. self.assertRaises(TypeError, meth, 16)
  2257. self.assertRaises(TypeError, meth, tzinfo=16)
  2258. # Bad keyword name.
  2259. self.assertRaises(TypeError, meth, tinfo=off42)
  2260. # Too many args.
  2261. self.assertRaises(TypeError, meth, off42, off42)
  2262. # We don't know which time zone we're in, and don't have a tzinfo
  2263. # class to represent it, so seeing whether a tz argument actually
  2264. # does a conversion is tricky.
  2265. weirdtz = FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0)
  2266. utc = FixedOffset(0, "utc", 0)
  2267. for dummy in range(3):
  2268. now = datetime.now(weirdtz)
  2269. self.assertIs(now.tzinfo, weirdtz)
  2270. utcnow = datetime.utcnow().replace(tzinfo=utc)
  2271. now2 = utcnow.astimezone(weirdtz)
  2272. if abs(now - now2) < timedelta(seconds=30):
  2273. break
  2274. # Else the code is broken, or more than 30 seconds passed between
  2275. # calls; assuming the latter, just try again.
  2276. else:
  2277. # Three strikes and we're out.
  2278. self.fail("utcnow(), now(tz), or astimezone() may be broken")
  2279. def test_tzinfo_fromtimestamp(self):
  2280. import time
  2281. meth = self.theclass.fromtimestamp
  2282. ts = time.time()
  2283. # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
  2284. base = meth(ts)
  2285. # Try with and without naming the keyword.
  2286. off42 = FixedOffset(42, "42")
  2287. another = meth(ts, off42)
  2288. again = meth(ts, tz=off42)
  2289. self.assertIs(another.tzinfo, again.tzinfo)
  2290. self.assertEqual(another.utcoffset(), timedelta(minutes=42))
  2291. # Bad argument with and w/o naming the keyword.
  2292. self.assertRaises(TypeError, meth, ts, 16)
  2293. self.assertRaises(TypeError, meth, ts, tzinfo=16)
  2294. # Bad keyword name.
  2295. self.assertRaises(TypeError, meth, ts, tinfo=off42)
  2296. # Too many args.
  2297. self.assertRaises(TypeError, meth, ts, off42, off42)
  2298. # Too few args.
  2299. self.assertRaises(TypeError, meth)
  2300. # Try to make sure tz= actually does some conversion.
  2301. timestamp = 1000000000
  2302. utcdatetime = datetime.utcfromtimestamp(timestamp)
  2303. # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
  2304. # But on some flavor of Mac, it's nowhere near that. So we can't have
  2305. # any idea here what time that actually is, we can only test that
  2306. # relative changes match.
  2307. utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
  2308. tz = FixedOffset(utcoffset, "tz", 0)
  2309. expected = utcdatetime + utcoffset
  2310. got = datetime.fromtimestamp(timestamp, tz)
  2311. self.assertEqual(expected, got.replace(tzinfo=None))
  2312. def test_tzinfo_utcnow(self):
  2313. meth = self.theclass.utcnow
  2314. # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
  2315. base = meth()
  2316. # Try with and without naming the keyword; for whatever reason,
  2317. # utcnow() doesn't accept a tzinfo argument.
  2318. off42 = FixedOffset(42, "42")
  2319. self.assertRaises(TypeError, meth, off42)
  2320. self.assertRaises(TypeError, meth, tzinfo=off42)
  2321. def test_tzinfo_utcfromtimestamp(self):
  2322. import time
  2323. meth = self.theclass.utcfromtimestamp
  2324. ts = time.time()
  2325. # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
  2326. base = meth(ts)
  2327. # Try with and without naming the keyword; for whatever reason,
  2328. # utcfromtimestamp() doesn't accept a tzinfo argument.
  2329. off42 = FixedOffset(42, "42")
  2330. self.assertRaises(TypeError, meth, ts, off42)
  2331. self.assertRaises(TypeError, meth, ts, tzinfo=off42)
  2332. def test_tzinfo_timetuple(self):
  2333. # TestDateTime tested most of this. datetime adds a twist to the
  2334. # DST flag.
  2335. class DST(tzinfo):
  2336. def __init__(self, dstvalue):
  2337. if isinstance(dstvalue, int):
  2338. dstvalue = timedelta(minutes=dstvalue)
  2339. self.dstvalue = dstvalue
  2340. def dst(self, dt):
  2341. return self.dstvalue
  2342. cls = self.theclass
  2343. for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
  2344. d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
  2345. t = d.timetuple()
  2346. self.assertEqual(1, t.tm_year)
  2347. self.assertEqual(1, t.tm_mon)
  2348. self.assertEqual(1, t.tm_mday)
  2349. self.assertEqual(10, t.tm_hour)
  2350. self.assertEqual(20, t.tm_min)
  2351. self.assertEqual(30, t.tm_sec)
  2352. self.assertEqual(0, t.tm_wday)
  2353. self.assertEqual(1, t.tm_yday)
  2354. self.assertEqual(flag, t.tm_isdst)
  2355. # dst() returns wrong type.
  2356. self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
  2357. # dst() at the edge.
  2358. self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
  2359. self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
  2360. # dst() out of range.
  2361. self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
  2362. self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
  2363. def test_utctimetuple(self):
  2364. class DST(tzinfo):
  2365. def __init__(self, dstvalue):
  2366. if isinstance(dstvalue, int):
  2367. dstvalue = timedelta(minutes=dstvalue)
  2368. self.dstvalue = dstvalue
  2369. def dst(self, dt):
  2370. return self.dstvalue
  2371. cls = self.theclass
  2372. # This can't work: DST didn't implement utcoffset.
  2373. self.assertRaises(NotImplementedError,
  2374. cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
  2375. class UOFS(DST):
  2376. def __init__(self, uofs, dofs=None):
  2377. DST.__init__(self, dofs)
  2378. self.uofs = timedelta(minutes=uofs)
  2379. def utcoffset(self, dt):
  2380. return self.uofs
  2381. # Ensure tm_isdst is 0 regardless of what dst() says: DST is never
  2382. # in effect for a UTC time.
  2383. for dstvalue in -33, 33, 0, None:
  2384. d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
  2385. t = d.utctimetuple()
  2386. self.assertEqual(d.year, t.tm_year)
  2387. self.assertEqual(d.month, t.tm_mon)
  2388. self.assertEqual(d.day, t.tm_mday)
  2389. self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
  2390. self.assertEqual(13, t.tm_min)
  2391. self.assertEqual(d.second, t.tm_sec)
  2392. self.assertEqual(d.weekday(), t.tm_wday)
  2393. self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
  2394. t.tm_yday)
  2395. self.assertEqual(0, t.tm_isdst)
  2396. # At the edges, UTC adjustment can normalize into years out-of-range
  2397. # for a datetime object. Ensure that a correct timetuple is
  2398. # created anyway.
  2399. tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
  2400. # That goes back 1 minute less than a full day.
  2401. t = tiny.utctimetuple()
  2402. self.assertEqual(t.tm_year, MINYEAR-1)
  2403. self.assertEqual(t.tm_mon, 12)
  2404. self.assertEqual(t.tm_mday, 31)
  2405. self.assertEqual(t.tm_hour, 0)
  2406. self.assertEqual(t.tm_min, 1)
  2407. self.assertEqual(t.tm_sec, 37)
  2408. self.assertEqual(t.tm_yday, 366) # "year 0" is a leap year
  2409. self.assertEqual(t.tm_isdst, 0)
  2410. huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
  2411. # That goes forward 1 minute less than a full day.
  2412. t = huge.utctimetuple()
  2413. self.assertEqual(t.tm_year, MAXYEAR+1)
  2414. self.assertEqual(t.tm_mon, 1)
  2415. self.assertEqual(t.tm_mday, 1)
  2416. self.assertEqual(t.tm_hour, 23)
  2417. self.assertEqual(t.tm_min, 58)
  2418. self.assertEqual(t.tm_sec, 37)
  2419. self.assertEqual(t.tm_yday, 1)
  2420. self.assertEqual(t.tm_isdst, 0)
  2421. def test_tzinfo_isoformat(self):
  2422. zero = FixedOffset(0, "+00:00")
  2423. plus = FixedOffset(220, "+03:40")
  2424. minus = FixedOffset(-231, "-03:51")
  2425. unknown = FixedOffset(None, "")
  2426. cls = self.theclass
  2427. datestr = '0001-02-03'
  2428. for ofs in None, zero, plus, minus, unknown:
  2429. for us in 0, 987001:
  2430. d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
  2431. timestr = '04:05:59' + (us and '.987001' or '')
  2432. ofsstr = ofs is not None and d.tzname() or ''
  2433. tailstr = timestr + ofsstr
  2434. iso = d.isoformat()
  2435. self.assertEqual(iso, datestr + 'T' + tailstr)
  2436. self.assertEqual(iso, d.isoformat('T'))
  2437. self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
  2438. self.assertEqual(str(d), datestr + ' ' + tailstr)
  2439. def test_replace(self):
  2440. cls = self.theclass
  2441. z100 = FixedOffset(100, "+100")
  2442. zm200 = FixedOffset(timedelta(minutes=-200), "-200")
  2443. args = [1, 2, 3, 4, 5, 6, 7, z100]
  2444. base = cls(*args)
  2445. self.assertEqual(base, base.replace())
  2446. i = 0
  2447. for name, newval in (("year", 2),
  2448. ("month", 3),
  2449. ("day", 4),
  2450. ("hour", 5),
  2451. ("minute", 6),
  2452. ("second", 7),
  2453. ("microsecond", 8),
  2454. ("tzinfo", zm200)):
  2455. newargs = args[:]
  2456. newargs[i] = newval
  2457. expected = cls(*newargs)
  2458. got = base.replace(**{name: newval})
  2459. self.assertEqual(expected, got)
  2460. i += 1
  2461. # Ensure we can get rid of a tzinfo.
  2462. self.assertEqual(base.tzname(), "+100")
  2463. base2 = base.replace(tzinfo=None)
  2464. self.assertIsNone(base2.tzinfo)
  2465. self.assertIsNone(base2.tzname())
  2466. # Ensure we can add one.
  2467. base3 = base2.replace(tzinfo=z100)
  2468. self.assertEqual(base, base3)
  2469. self.assertIs(base.tzinfo, base3.tzinfo)
  2470. # Out of bounds.
  2471. base = cls(2000, 2, 29)
  2472. self.assertRaises(ValueError, base.replace, year=2001)
  2473. def test_more_astimezone(self):
  2474. # The inherited test_astimezone covered some trivial and error cases.
  2475. fnone = FixedOffset(None, "None")
  2476. f44m = FixedOffset(44, "44")
  2477. fm5h = FixedOffset(-timedelta(hours=5), "m300")
  2478. dt = self.theclass.now(tz=f44m)
  2479. self.assertIs(dt.tzinfo, f44m)
  2480. # Replacing with degenerate tzinfo raises an exception.
  2481. self.assertRaises(ValueError, dt.astimezone, fnone)
  2482. # Ditto with None tz.
  2483. self.assertRaises(TypeError, dt.astimezone, None)
  2484. # Replacing with same tzinfo makes no change.
  2485. x = dt.astimezone(dt.tzinfo)
  2486. self.assertIs(x.tzinfo, f44m)
  2487. self.assertEqual(x.date(), dt.date())
  2488. self.assertEqual(x.time(), dt.time())
  2489. # Replacing with different tzinfo does adjust.
  2490. got = dt.astimezone(fm5h)
  2491. self.assertIs(got.tzinfo, fm5h)
  2492. self.assertEqual(got.utcoffset(), timedelta(hours=-5))
  2493. expected = dt - dt.utcoffset() # in effect, convert to UTC
  2494. expected += fm5h.utcoffset(dt) # and from there to local time
  2495. expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
  2496. self.assertEqual(got.date(), expected.date())
  2497. self.assertEqual(got.time(), expected.time())
  2498. self.assertEqual(got.timetz(), expected.timetz())
  2499. self.assertIs(got.tzinfo, expected.tzinfo)
  2500. self.assertEqual(got, expected)
  2501. def test_aware_subtract(self):
  2502. cls = self.theclass
  2503. # Ensure that utcoffset() is ignored when the operands have the
  2504. # same tzinfo member.
  2505. class OperandDependentOffset(tzinfo):
  2506. def utcoffset(self, t):
  2507. if t.minute < 10:
  2508. # d0 and d1 equal after adjustment
  2509. return timedelta(minutes=t.minute)
  2510. else:
  2511. # d2 off in the weeds
  2512. return timedelta(minutes=59)
  2513. base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
  2514. d0 = base.replace(minute=3)
  2515. d1 = base.replace(minute=9)
  2516. d2 = base.replace(minute=11)
  2517. for x in d0, d1, d2:
  2518. for y in d0, d1, d2:
  2519. got = x - y
  2520. expected = timedelta(minutes=x.minute - y.minute)
  2521. self.assertEqual(got, expected)
  2522. # OTOH, if the tzinfo members are distinct, utcoffsets aren't
  2523. # ignored.
  2524. base = cls(8, 9, 10, 11, 12, 13, 14)
  2525. d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
  2526. d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
  2527. d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
  2528. for x in d0, d1, d2:
  2529. for y in d0, d1, d2:
  2530. got = x - y
  2531. if (x is d0 or x is d1) and (y is d0 or y is d1):
  2532. expected = timedelta(0)
  2533. elif x is y is d2:
  2534. expected = timedelta(0)
  2535. elif x is d2:
  2536. expected = timedelta(minutes=(11-59)-0)
  2537. else:
  2538. assert y is d2
  2539. expected = timedelta(minutes=0-(11-59))
  2540. self.assertEqual(got, expected)
  2541. def test_mixed_compare(self):
  2542. t1 = datetime(1, 2, 3, 4, 5, 6, 7)
  2543. t2 = datetime(1, 2, 3, 4, 5, 6, 7)
  2544. self.assertEqual(t1, t2)
  2545. t2 = t2.replace(tzinfo=None)
  2546. self.assertEqual(t1, t2)
  2547. t2 = t2.replace(tzinfo=FixedOffset(None, ""))
  2548. self.assertEqual(t1, t2)
  2549. t2 = t2.replace(tzinfo=FixedOffset(0, ""))
  2550. self.assertRaises(TypeError, lambda: t1 == t2)
  2551. # In datetime w/ identical tzinfo objects, utcoffset is ignored.
  2552. class Varies(tzinfo):
  2553. def __init__(self):
  2554. self.offset = timedelta(minutes=22)
  2555. def utcoffset(self, t):
  2556. self.offset += timedelta(minutes=1)
  2557. return self.offset
  2558. v = Varies()
  2559. t1 = t2.replace(tzinfo=v)
  2560. t2 = t2.replace(tzinfo=v)
  2561. self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
  2562. self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
  2563. self.assertEqual(t1, t2)
  2564. # But if they're not identical, it isn't ignored.
  2565. t2 = t2.replace(tzinfo=Varies())
  2566. self.assertTrue(t1 < t2) # t1's offset counter still going up
  2567. def test_subclass_datetimetz(self):
  2568. class C(self.theclass):
  2569. theAnswer = 42
  2570. def __new__(cls, *args, **kws):
  2571. temp = kws.copy()
  2572. extra = temp.pop('extra')
  2573. result = self.theclass.__new__(cls, *args, **temp)
  2574. result.extra = extra
  2575. return result
  2576. def newmeth(self, start):
  2577. return start + self.hour + self.year
  2578. args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
  2579. dt1 = self.theclass(*args)
  2580. dt2 = C(*args, **{'extra': 7})
  2581. self.assertEqual(dt2.__class__, C)
  2582. self.assertEqual(dt2.theAnswer, 42)
  2583. self.assertEqual(dt2.extra, 7)
  2584. self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
  2585. self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
  2586. # Pain to set up DST-aware tzinfo classes.
  2587. def first_sunday_on_or_after(dt):
  2588. days_to_go = 6 - dt.weekday()
  2589. if days_to_go:
  2590. dt += timedelta(days_to_go)
  2591. return dt
  2592. ZERO = timedelta(0)
  2593. HOUR = timedelta(hours=1)
  2594. DAY = timedelta(days=1)
  2595. # In the US, DST starts at 2am (standard time) on the first Sunday in April.
  2596. DSTSTART = datetime(1, 4, 1, 2)
  2597. # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
  2598. # which is the first Sunday on or after Oct 25. Because we view 1:MM as
  2599. # being standard time on that day, there is no spelling in local time of
  2600. # the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
  2601. DSTEND = datetime(1, 10, 25, 1)
  2602. class USTimeZone(tzinfo):
  2603. def __init__(self, hours, reprname, stdname, dstname):
  2604. self.stdoffset = timedelta(hours=hours)
  2605. self.reprname = reprname
  2606. self.stdname = stdname
  2607. self.dstname = dstname
  2608. def __repr__(self):
  2609. return self.reprname
  2610. def tzname(self, dt):
  2611. if self.dst(dt):
  2612. return self.dstname
  2613. else:
  2614. return self.stdname
  2615. def utcoffset(self, dt):
  2616. return self.stdoffset + self.dst(dt)
  2617. def dst(self, dt):
  2618. if dt is None or dt.tzinfo is None:
  2619. # An exception instead may be sensible here, in one or more of
  2620. # the cases.
  2621. return ZERO
  2622. assert dt.tzinfo is self
  2623. # Find first Sunday in April.
  2624. start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
  2625. assert start.weekday() == 6 and start.month == 4 and start.day <= 7
  2626. # Find last Sunday in October.
  2627. end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
  2628. assert end.weekday() == 6 and end.month == 10 and end.day >= 25
  2629. # Can't compare naive to aware objects, so strip the timezone from
  2630. # dt first.
  2631. if start <= dt.replace(tzinfo=None) < end:
  2632. return HOUR
  2633. else:
  2634. return ZERO
  2635. Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
  2636. Central = USTimeZone(-6, "Central", "CST", "CDT")
  2637. Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
  2638. Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
  2639. utc_real = FixedOffset(0, "UTC", 0)
  2640. # For better test coverage, we want another flavor of UTC that's west of
  2641. # the Eastern and Pacific timezones.
  2642. utc_fake = FixedOffset(-12*60, "UTCfake", 0)
  2643. class TestTimezoneConversions(unittest.TestCase):
  2644. # The DST switch times for 2002, in std time.
  2645. dston = datetime(2002, 4, 7, 2)
  2646. dstoff = datetime(2002, 10, 27, 1)
  2647. theclass = datetime
  2648. # Check a time that's inside DST.
  2649. def checkinside(self, dt, tz, utc, dston, dstoff):
  2650. self.assertEqual(dt.dst(), HOUR)
  2651. # Conversion to our own timezone is always an identity.
  2652. self.assertEqual(dt.astimezone(tz), dt)
  2653. asutc = dt.astimezone(utc)
  2654. there_and_back = asutc.astimezone(tz)
  2655. # Conversion to UTC and back isn't always an identity here,
  2656. # because there are redundant spellings (in local time) of
  2657. # UTC time when DST begins: the clock jumps from 1:59:59
  2658. # to 3:00:00, and a local time of 2:MM:SS doesn't really
  2659. # make sense then. The classes above treat 2:MM:SS as
  2660. # daylight time then (it's "after 2am"), really an alias
  2661. # for 1:MM:SS standard time. The latter form is what
  2662. # conversion back from UTC produces.
  2663. if dt.date() == dston.date() and dt.hour == 2:
  2664. # We're in the redundant hour, and coming back from
  2665. # UTC gives the 1:MM:SS standard-time spelling.
  2666. self.assertEqual(there_and_back + HOUR, dt)
  2667. # Although during was considered to be in daylight
  2668. # time, there_and_back is not.
  2669. self.assertEqual(there_and_back.dst(), ZERO)
  2670. # They're the same times in UTC.
  2671. self.assertEqual(there_and_back.astimezone(utc),
  2672. dt.astimezone(utc))
  2673. else:
  2674. # We're not in the redundant hour.
  2675. self.assertEqual(dt, there_and_back)
  2676. # Because we have a redundant spelling when DST begins, there is
  2677. # (unfortunately) an hour when DST ends that can't be spelled at all in
  2678. # local time. When DST ends, the clock jumps from 1:59 back to 1:00
  2679. # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
  2680. # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
  2681. # daylight time. The hour 1:MM daylight == 0:MM standard can't be
  2682. # expressed in local time. Nevertheless, we want conversion back
  2683. # from UTC to mimic the local clock's "repeat an hour" behavior.
  2684. nexthour_utc = asutc + HOUR
  2685. nexthour_tz = nexthour_utc.astimezone(tz)
  2686. if dt.date() == dstoff.date() and dt.hour == 0:
  2687. # We're in the hour before the last DST hour. The last DST hour
  2688. # is ineffable. We want the conversion back to repeat 1:MM.
  2689. self.assertEqual(nexthour_tz, dt.replace(hour=1))
  2690. nexthour_utc += HOUR
  2691. nexthour_tz = nexthour_utc.astimezone(tz)
  2692. self.assertEqual(nexthour_tz, dt.replace(hour=1))
  2693. else:
  2694. self.assertEqual(nexthour_tz - dt, HOUR)
  2695. # Check a time that's outside DST.
  2696. def checkoutside(self, dt, tz, utc):
  2697. self.assertEqual(dt.dst(), ZERO)
  2698. # Conversion to our own timezone is always an identity.
  2699. self.assertEqual(dt.astimezone(tz), dt)
  2700. # Converting to UTC and back is an identity too.
  2701. asutc = dt.astimezone(utc)
  2702. there_and_back = asutc.astimezone(tz)
  2703. self.assertEqual(dt, there_and_back)
  2704. def convert_between_tz_and_utc(self, tz, utc):
  2705. dston = self.dston.replace(tzinfo=tz)
  2706. # Because 1:MM on the day DST ends is taken as being standard time,
  2707. # there is no spelling in tz for the last hour of daylight time.
  2708. # For purposes of the test, the last hour of DST is 0:MM, which is
  2709. # taken as being daylight time (and 1:MM is taken as being standard
  2710. # time).
  2711. dstoff = self.dstoff.replace(tzinfo=tz)
  2712. for delta in (timedelta(weeks=13),
  2713. DAY,
  2714. HOUR,
  2715. timedelta(minutes=1),
  2716. timedelta(microseconds=1)):
  2717. self.checkinside(dston, tz, utc, dston, dstoff)
  2718. for during in dston + delta, dstoff - delta:
  2719. self.checkinside(during, tz, utc, dston, dstoff)
  2720. self.checkoutside(dstoff, tz, utc)
  2721. for outside in dston - delta, dstoff + delta:
  2722. self.checkoutside(outside, tz, utc)
  2723. def test_easy(self):
  2724. # Despite the name of this test, the endcases are excruciating.
  2725. self.convert_between_tz_and_utc(Eastern, utc_real)
  2726. self.convert_between_tz_and_utc(Pacific, utc_real)
  2727. self.convert_between_tz_and_utc(Eastern, utc_fake)
  2728. self.convert_between_tz_and_utc(Pacific, utc_fake)
  2729. # The next is really dancing near the edge. It works because
  2730. # Pacific and Eastern are far enough apart that their "problem
  2731. # hours" don't overlap.
  2732. self.convert_between_tz_and_utc(Eastern, Pacific)
  2733. self.convert_between_tz_and_utc(Pacific, Eastern)
  2734. # OTOH, these fail! Don't enable them. The difficulty is that
  2735. # the edge case tests assume that every hour is representable in
  2736. # the "utc" class. This is always true for a fixed-offset tzinfo
  2737. # class (lke utc_real and utc_fake), but not for Eastern or Central.
  2738. # For these adjacent DST-aware time zones, the range of time offsets
  2739. # tested ends up creating hours in the one that aren't representable
  2740. # in the other. For the same reason, we would see failures in the
  2741. # Eastern vs Pacific tests too if we added 3*HOUR to the list of
  2742. # offset deltas in convert_between_tz_and_utc().
  2743. #
  2744. # self.convert_between_tz_and_utc(Eastern, Central) # can't work
  2745. # self.convert_between_tz_and_utc(Central, Eastern) # can't work
  2746. def test_tricky(self):
  2747. # 22:00 on day before daylight starts.
  2748. fourback = self.dston - timedelta(hours=4)
  2749. ninewest = FixedOffset(-9*60, "-0900", 0)
  2750. fourback = fourback.replace(tzinfo=ninewest)
  2751. # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
  2752. # 2", we should get the 3 spelling.
  2753. # If we plug 22:00 the day before into Eastern, it "looks like std
  2754. # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
  2755. # to 22:00 lands on 2:00, which makes no sense in local time (the
  2756. # local clock jumps from 1 to 3). The point here is to make sure we
  2757. # get the 3 spelling.
  2758. expected = self.dston.replace(hour=3)
  2759. got = fourback.astimezone(Eastern).replace(tzinfo=None)
  2760. self.assertEqual(expected, got)
  2761. # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
  2762. # case we want the 1:00 spelling.
  2763. sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
  2764. # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
  2765. # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
  2766. # spelling.
  2767. expected = self.dston.replace(hour=1)
  2768. got = sixutc.astimezone(Eastern).replace(tzinfo=None)
  2769. self.assertEqual(expected, got)
  2770. # Now on the day DST ends, we want "repeat an hour" behavior.
  2771. # UTC 4:MM 5:MM 6:MM 7:MM checking these
  2772. # EST 23:MM 0:MM 1:MM 2:MM
  2773. # EDT 0:MM 1:MM 2:MM 3:MM
  2774. # wall 0:MM 1:MM 1:MM 2:MM against these
  2775. for utc in utc_real, utc_fake:
  2776. for tz in Eastern, Pacific:
  2777. first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
  2778. # Convert that to UTC.
  2779. first_std_hour -= tz.utcoffset(None)
  2780. # Adjust for possibly fake UTC.
  2781. asutc = first_std_hour + utc.utcoffset(None)
  2782. # First UTC hour to convert; this is 4:00 when utc=utc_real &
  2783. # tz=Eastern.
  2784. asutcbase = asutc.replace(tzinfo=utc)
  2785. for tzhour in (0, 1, 1, 2):
  2786. expectedbase = self.dstoff.replace(hour=tzhour)
  2787. for minute in 0, 30, 59:
  2788. expected = expectedbase.replace(minute=minute)
  2789. asutc = asutcbase.replace(minute=minute)
  2790. astz = asutc.astimezone(tz)
  2791. self.assertEqual(astz.replace(tzinfo=None), expected)
  2792. asutcbase += HOUR
  2793. def test_bogus_dst(self):
  2794. class ok(tzinfo):
  2795. def utcoffset(self, dt): return HOUR
  2796. def dst(self, dt): return HOUR
  2797. now = self.theclass.now().replace(tzinfo=utc_real)
  2798. # Doesn't blow up.
  2799. now.astimezone(ok())
  2800. # Does blow up.
  2801. class notok(ok):
  2802. def dst(self, dt): return None
  2803. self.assertRaises(ValueError, now.astimezone, notok())
  2804. def test_fromutc(self):
  2805. self.assertRaises(TypeError, Eastern.fromutc) # not enough args
  2806. now = datetime.utcnow().replace(tzinfo=utc_real)
  2807. self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
  2808. now = now.replace(tzinfo=Eastern) # insert correct tzinfo
  2809. enow = Eastern.fromutc(now) # doesn't blow up
  2810. self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
  2811. self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
  2812. self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
  2813. # Always converts UTC to standard time.
  2814. class FauxUSTimeZone(USTimeZone):
  2815. def fromutc(self, dt):
  2816. return dt + self.stdoffset
  2817. FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
  2818. # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
  2819. # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
  2820. # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
  2821. # Check around DST start.
  2822. start = self.dston.replace(hour=4, tzinfo=Eastern)
  2823. fstart = start.replace(tzinfo=FEastern)
  2824. for wall in 23, 0, 1, 3, 4, 5:
  2825. expected = start.replace(hour=wall)
  2826. if wall == 23:
  2827. expected -= timedelta(days=1)
  2828. got = Eastern.fromutc(start)
  2829. self.assertEqual(expected, got)
  2830. expected = fstart + FEastern.stdoffset
  2831. got = FEastern.fromutc(fstart)
  2832. self.assertEqual(expected, got)
  2833. # Ensure astimezone() calls fromutc() too.
  2834. got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
  2835. self.assertEqual(expected, got)
  2836. start += HOUR
  2837. fstart += HOUR
  2838. # Check around DST end.
  2839. start = self.dstoff.replace(hour=4, tzinfo=Eastern)
  2840. fstart = start.replace(tzinfo=FEastern)
  2841. for wall in 0, 1, 1, 2, 3, 4:
  2842. expected = start.replace(hour=wall)
  2843. got = Eastern.fromutc(start)
  2844. self.assertEqual(expected, got)
  2845. expected = fstart + FEastern.stdoffset
  2846. got = FEastern.fromutc(fstart)
  2847. self.assertEqual(expected, got)
  2848. # Ensure astimezone() calls fromutc() too.
  2849. got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
  2850. self.assertEqual(expected, got)
  2851. start += HOUR
  2852. fstart += HOUR
  2853. #############################################################################
  2854. # oddballs
  2855. class Oddballs(unittest.TestCase):
  2856. def test_bug_1028306(self):
  2857. # Trying to compare a date to a datetime should act like a mixed-
  2858. # type comparison, despite that datetime is a subclass of date.
  2859. as_date = date.today()
  2860. as_datetime = datetime.combine(as_date, time())
  2861. self.assertTrue(as_date != as_datetime)
  2862. self.assertTrue(as_datetime != as_date)
  2863. self.assertFalse(as_date == as_datetime)
  2864. self.assertFalse(as_datetime == as_date)
  2865. self.assertRaises(TypeError, lambda: as_date < as_datetime)
  2866. self.assertRaises(TypeError, lambda: as_datetime < as_date)
  2867. self.assertRaises(TypeError, lambda: as_date <= as_datetime)
  2868. self.assertRaises(TypeError, lambda: as_datetime <= as_date)
  2869. self.assertRaises(TypeError, lambda: as_date > as_datetime)
  2870. self.assertRaises(TypeError, lambda: as_datetime > as_date)
  2871. self.assertRaises(TypeError, lambda: as_date >= as_datetime)
  2872. self.assertRaises(TypeError, lambda: as_datetime >= as_date)
  2873. # Neverthelss, comparison should work with the base-class (date)
  2874. # projection if use of a date method is forced.
  2875. self.assertTrue(as_date.__eq__(as_datetime))
  2876. different_day = (as_date.day + 1) % 20 + 1
  2877. self.assertFalse(as_date.__eq__(as_datetime.replace(day=different_day)))
  2878. # And date should compare with other subclasses of date. If a
  2879. # subclass wants to stop this, it's up to the subclass to do so.
  2880. date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
  2881. self.assertEqual(as_date, date_sc)
  2882. self.assertEqual(date_sc, as_date)
  2883. # Ditto for datetimes.
  2884. datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
  2885. as_date.day, 0, 0, 0)
  2886. self.assertEqual(as_datetime, datetime_sc)
  2887. self.assertEqual(datetime_sc, as_datetime)
  2888. def test_main():
  2889. test_support.run_unittest(__name__)
  2890. if __name__ == "__main__":
  2891. test_main()