datetimetester.py 258 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626
  1. """Test date/time type.
  2. See https://www.zope.dev/Members/fdrake/DateTimeWiki/TestCases
  3. """
  4. import io
  5. import itertools
  6. import bisect
  7. import copy
  8. import decimal
  9. import functools
  10. import sys
  11. import os
  12. import pickle
  13. import random
  14. import re
  15. import struct
  16. import unittest
  17. from array import array
  18. from operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod
  19. from test import support
  20. from test.support import is_resource_enabled, ALWAYS_EQ, LARGEST, SMALLEST
  21. import datetime as datetime_module
  22. from datetime import MINYEAR, MAXYEAR
  23. from datetime import timedelta
  24. from datetime import tzinfo
  25. from datetime import time
  26. from datetime import timezone
  27. from datetime import UTC
  28. from datetime import date, datetime
  29. import time as _time
  30. try:
  31. import _testcapi
  32. except ImportError:
  33. _testcapi = None
  34. # Needed by test_datetime
  35. import _strptime
  36. #
  37. pickle_loads = {pickle.loads, pickle._loads}
  38. pickle_choices = [(pickle, pickle, proto)
  39. for proto in range(pickle.HIGHEST_PROTOCOL + 1)]
  40. assert len(pickle_choices) == pickle.HIGHEST_PROTOCOL + 1
  41. # An arbitrary collection of objects of non-datetime types, for testing
  42. # mixed-type comparisons.
  43. OTHERSTUFF = (10, 34.5, "abc", {}, [], ())
  44. # XXX Copied from test_float.
  45. INF = float("inf")
  46. NAN = float("nan")
  47. #############################################################################
  48. # module tests
  49. class TestModule(unittest.TestCase):
  50. def test_constants(self):
  51. datetime = datetime_module
  52. self.assertEqual(datetime.MINYEAR, 1)
  53. self.assertEqual(datetime.MAXYEAR, 9999)
  54. def test_utc_alias(self):
  55. self.assertIs(UTC, timezone.utc)
  56. def test_all(self):
  57. """Test that __all__ only points to valid attributes."""
  58. all_attrs = dir(datetime_module)
  59. for attr in datetime_module.__all__:
  60. self.assertIn(attr, all_attrs)
  61. def test_name_cleanup(self):
  62. if '_Pure' in self.__class__.__name__:
  63. self.skipTest('Only run for Fast C implementation')
  64. datetime = datetime_module
  65. names = set(name for name in dir(datetime)
  66. if not name.startswith('__') and not name.endswith('__'))
  67. allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime',
  68. 'datetime_CAPI', 'time', 'timedelta', 'timezone',
  69. 'tzinfo', 'UTC', 'sys'])
  70. self.assertEqual(names - allowed, set([]))
  71. def test_divide_and_round(self):
  72. if '_Fast' in self.__class__.__name__:
  73. self.skipTest('Only run for Pure Python implementation')
  74. dar = datetime_module._divide_and_round
  75. self.assertEqual(dar(-10, -3), 3)
  76. self.assertEqual(dar(5, -2), -2)
  77. # four cases: (2 signs of a) x (2 signs of b)
  78. self.assertEqual(dar(7, 3), 2)
  79. self.assertEqual(dar(-7, 3), -2)
  80. self.assertEqual(dar(7, -3), -2)
  81. self.assertEqual(dar(-7, -3), 2)
  82. # ties to even - eight cases:
  83. # (2 signs of a) x (2 signs of b) x (even / odd quotient)
  84. self.assertEqual(dar(10, 4), 2)
  85. self.assertEqual(dar(-10, 4), -2)
  86. self.assertEqual(dar(10, -4), -2)
  87. self.assertEqual(dar(-10, -4), 2)
  88. self.assertEqual(dar(6, 4), 2)
  89. self.assertEqual(dar(-6, 4), -2)
  90. self.assertEqual(dar(6, -4), -2)
  91. self.assertEqual(dar(-6, -4), 2)
  92. #############################################################################
  93. # tzinfo tests
  94. class FixedOffset(tzinfo):
  95. def __init__(self, offset, name, dstoffset=42):
  96. if isinstance(offset, int):
  97. offset = timedelta(minutes=offset)
  98. if isinstance(dstoffset, int):
  99. dstoffset = timedelta(minutes=dstoffset)
  100. self.__offset = offset
  101. self.__name = name
  102. self.__dstoffset = dstoffset
  103. def __repr__(self):
  104. return self.__name.lower()
  105. def utcoffset(self, dt):
  106. return self.__offset
  107. def tzname(self, dt):
  108. return self.__name
  109. def dst(self, dt):
  110. return self.__dstoffset
  111. class PicklableFixedOffset(FixedOffset):
  112. def __init__(self, offset=None, name=None, dstoffset=None):
  113. FixedOffset.__init__(self, offset, name, dstoffset)
  114. class PicklableFixedOffsetWithSlots(PicklableFixedOffset):
  115. __slots__ = '_FixedOffset__offset', '_FixedOffset__name', 'spam'
  116. class _TZInfo(tzinfo):
  117. def utcoffset(self, datetime_module):
  118. return random.random()
  119. class TestTZInfo(unittest.TestCase):
  120. def test_refcnt_crash_bug_22044(self):
  121. tz1 = _TZInfo()
  122. dt1 = datetime(2014, 7, 21, 11, 32, 3, 0, tz1)
  123. with self.assertRaises(TypeError):
  124. dt1.utcoffset()
  125. def test_non_abstractness(self):
  126. # In order to allow subclasses to get pickled, the C implementation
  127. # wasn't able to get away with having __init__ raise
  128. # NotImplementedError.
  129. useless = tzinfo()
  130. dt = datetime.max
  131. self.assertRaises(NotImplementedError, useless.tzname, dt)
  132. self.assertRaises(NotImplementedError, useless.utcoffset, dt)
  133. self.assertRaises(NotImplementedError, useless.dst, dt)
  134. def test_subclass_must_override(self):
  135. class NotEnough(tzinfo):
  136. def __init__(self, offset, name):
  137. self.__offset = offset
  138. self.__name = name
  139. self.assertTrue(issubclass(NotEnough, tzinfo))
  140. ne = NotEnough(3, "NotByALongShot")
  141. self.assertIsInstance(ne, tzinfo)
  142. dt = datetime.now()
  143. self.assertRaises(NotImplementedError, ne.tzname, dt)
  144. self.assertRaises(NotImplementedError, ne.utcoffset, dt)
  145. self.assertRaises(NotImplementedError, ne.dst, dt)
  146. def test_normal(self):
  147. fo = FixedOffset(3, "Three")
  148. self.assertIsInstance(fo, tzinfo)
  149. for dt in datetime.now(), None:
  150. self.assertEqual(fo.utcoffset(dt), timedelta(minutes=3))
  151. self.assertEqual(fo.tzname(dt), "Three")
  152. self.assertEqual(fo.dst(dt), timedelta(minutes=42))
  153. def test_pickling_base(self):
  154. # There's no point to pickling tzinfo objects on their own (they
  155. # carry no data), but they need to be picklable anyway else
  156. # concrete subclasses can't be pickled.
  157. orig = tzinfo.__new__(tzinfo)
  158. self.assertIs(type(orig), tzinfo)
  159. for pickler, unpickler, proto in pickle_choices:
  160. green = pickler.dumps(orig, proto)
  161. derived = unpickler.loads(green)
  162. self.assertIs(type(derived), tzinfo)
  163. def test_pickling_subclass(self):
  164. # Make sure we can pickle/unpickle an instance of a subclass.
  165. offset = timedelta(minutes=-300)
  166. for otype, args in [
  167. (PicklableFixedOffset, (offset, 'cookie')),
  168. (PicklableFixedOffsetWithSlots, (offset, 'cookie')),
  169. (timezone, (offset,)),
  170. (timezone, (offset, "EST"))]:
  171. orig = otype(*args)
  172. oname = orig.tzname(None)
  173. self.assertIsInstance(orig, tzinfo)
  174. self.assertIs(type(orig), otype)
  175. self.assertEqual(orig.utcoffset(None), offset)
  176. self.assertEqual(orig.tzname(None), oname)
  177. for pickler, unpickler, proto in pickle_choices:
  178. green = pickler.dumps(orig, proto)
  179. derived = unpickler.loads(green)
  180. self.assertIsInstance(derived, tzinfo)
  181. self.assertIs(type(derived), otype)
  182. self.assertEqual(derived.utcoffset(None), offset)
  183. self.assertEqual(derived.tzname(None), oname)
  184. self.assertFalse(hasattr(derived, 'spam'))
  185. def test_issue23600(self):
  186. DSTDIFF = DSTOFFSET = timedelta(hours=1)
  187. class UKSummerTime(tzinfo):
  188. """Simple time zone which pretends to always be in summer time, since
  189. that's what shows the failure.
  190. """
  191. def utcoffset(self, dt):
  192. return DSTOFFSET
  193. def dst(self, dt):
  194. return DSTDIFF
  195. def tzname(self, dt):
  196. return 'UKSummerTime'
  197. tz = UKSummerTime()
  198. u = datetime(2014, 4, 26, 12, 1, tzinfo=tz)
  199. t = tz.fromutc(u)
  200. self.assertEqual(t - t.utcoffset(), u)
  201. class TestTimeZone(unittest.TestCase):
  202. def setUp(self):
  203. self.ACDT = timezone(timedelta(hours=9.5), 'ACDT')
  204. self.EST = timezone(-timedelta(hours=5), 'EST')
  205. self.DT = datetime(2010, 1, 1)
  206. def test_str(self):
  207. for tz in [self.ACDT, self.EST, timezone.utc,
  208. timezone.min, timezone.max]:
  209. self.assertEqual(str(tz), tz.tzname(None))
  210. def test_repr(self):
  211. datetime = datetime_module
  212. for tz in [self.ACDT, self.EST, timezone.utc,
  213. timezone.min, timezone.max]:
  214. # test round-trip
  215. tzrep = repr(tz)
  216. self.assertEqual(tz, eval(tzrep))
  217. def test_class_members(self):
  218. limit = timedelta(hours=23, minutes=59)
  219. self.assertEqual(timezone.utc.utcoffset(None), ZERO)
  220. self.assertEqual(timezone.min.utcoffset(None), -limit)
  221. self.assertEqual(timezone.max.utcoffset(None), limit)
  222. def test_constructor(self):
  223. self.assertIs(timezone.utc, timezone(timedelta(0)))
  224. self.assertIsNot(timezone.utc, timezone(timedelta(0), 'UTC'))
  225. self.assertEqual(timezone.utc, timezone(timedelta(0), 'UTC'))
  226. for subminute in [timedelta(microseconds=1), timedelta(seconds=1)]:
  227. tz = timezone(subminute)
  228. self.assertNotEqual(tz.utcoffset(None) % timedelta(minutes=1), 0)
  229. # invalid offsets
  230. for invalid in [timedelta(1, 1), timedelta(1)]:
  231. self.assertRaises(ValueError, timezone, invalid)
  232. self.assertRaises(ValueError, timezone, -invalid)
  233. with self.assertRaises(TypeError): timezone(None)
  234. with self.assertRaises(TypeError): timezone(42)
  235. with self.assertRaises(TypeError): timezone(ZERO, None)
  236. with self.assertRaises(TypeError): timezone(ZERO, 42)
  237. with self.assertRaises(TypeError): timezone(ZERO, 'ABC', 'extra')
  238. def test_inheritance(self):
  239. self.assertIsInstance(timezone.utc, tzinfo)
  240. self.assertIsInstance(self.EST, tzinfo)
  241. def test_utcoffset(self):
  242. dummy = self.DT
  243. for h in [0, 1.5, 12]:
  244. offset = h * HOUR
  245. self.assertEqual(offset, timezone(offset).utcoffset(dummy))
  246. self.assertEqual(-offset, timezone(-offset).utcoffset(dummy))
  247. with self.assertRaises(TypeError): self.EST.utcoffset('')
  248. with self.assertRaises(TypeError): self.EST.utcoffset(5)
  249. def test_dst(self):
  250. self.assertIsNone(timezone.utc.dst(self.DT))
  251. with self.assertRaises(TypeError): self.EST.dst('')
  252. with self.assertRaises(TypeError): self.EST.dst(5)
  253. def test_tzname(self):
  254. self.assertEqual('UTC', timezone.utc.tzname(None))
  255. self.assertEqual('UTC', UTC.tzname(None))
  256. self.assertEqual('UTC', timezone(ZERO).tzname(None))
  257. self.assertEqual('UTC-05:00', timezone(-5 * HOUR).tzname(None))
  258. self.assertEqual('UTC+09:30', timezone(9.5 * HOUR).tzname(None))
  259. self.assertEqual('UTC-00:01', timezone(timedelta(minutes=-1)).tzname(None))
  260. self.assertEqual('XYZ', timezone(-5 * HOUR, 'XYZ').tzname(None))
  261. # bpo-34482: Check that surrogates are handled properly.
  262. self.assertEqual('\ud800', timezone(ZERO, '\ud800').tzname(None))
  263. # Sub-minute offsets:
  264. self.assertEqual('UTC+01:06:40', timezone(timedelta(0, 4000)).tzname(None))
  265. self.assertEqual('UTC-01:06:40',
  266. timezone(-timedelta(0, 4000)).tzname(None))
  267. self.assertEqual('UTC+01:06:40.000001',
  268. timezone(timedelta(0, 4000, 1)).tzname(None))
  269. self.assertEqual('UTC-01:06:40.000001',
  270. timezone(-timedelta(0, 4000, 1)).tzname(None))
  271. with self.assertRaises(TypeError): self.EST.tzname('')
  272. with self.assertRaises(TypeError): self.EST.tzname(5)
  273. def test_fromutc(self):
  274. with self.assertRaises(ValueError):
  275. timezone.utc.fromutc(self.DT)
  276. with self.assertRaises(TypeError):
  277. timezone.utc.fromutc('not datetime')
  278. for tz in [self.EST, self.ACDT, Eastern]:
  279. utctime = self.DT.replace(tzinfo=tz)
  280. local = tz.fromutc(utctime)
  281. self.assertEqual(local - utctime, tz.utcoffset(local))
  282. self.assertEqual(local,
  283. self.DT.replace(tzinfo=timezone.utc))
  284. def test_comparison(self):
  285. self.assertNotEqual(timezone(ZERO), timezone(HOUR))
  286. self.assertEqual(timezone(HOUR), timezone(HOUR))
  287. self.assertEqual(timezone(-5 * HOUR), timezone(-5 * HOUR, 'EST'))
  288. with self.assertRaises(TypeError): timezone(ZERO) < timezone(ZERO)
  289. self.assertIn(timezone(ZERO), {timezone(ZERO)})
  290. self.assertTrue(timezone(ZERO) != None)
  291. self.assertFalse(timezone(ZERO) == None)
  292. tz = timezone(ZERO)
  293. self.assertTrue(tz == ALWAYS_EQ)
  294. self.assertFalse(tz != ALWAYS_EQ)
  295. self.assertTrue(tz < LARGEST)
  296. self.assertFalse(tz > LARGEST)
  297. self.assertTrue(tz <= LARGEST)
  298. self.assertFalse(tz >= LARGEST)
  299. self.assertFalse(tz < SMALLEST)
  300. self.assertTrue(tz > SMALLEST)
  301. self.assertFalse(tz <= SMALLEST)
  302. self.assertTrue(tz >= SMALLEST)
  303. def test_aware_datetime(self):
  304. # test that timezone instances can be used by datetime
  305. t = datetime(1, 1, 1)
  306. for tz in [timezone.min, timezone.max, timezone.utc]:
  307. self.assertEqual(tz.tzname(t),
  308. t.replace(tzinfo=tz).tzname())
  309. self.assertEqual(tz.utcoffset(t),
  310. t.replace(tzinfo=tz).utcoffset())
  311. self.assertEqual(tz.dst(t),
  312. t.replace(tzinfo=tz).dst())
  313. def test_pickle(self):
  314. for tz in self.ACDT, self.EST, timezone.min, timezone.max:
  315. for pickler, unpickler, proto in pickle_choices:
  316. tz_copy = unpickler.loads(pickler.dumps(tz, proto))
  317. self.assertEqual(tz_copy, tz)
  318. tz = timezone.utc
  319. for pickler, unpickler, proto in pickle_choices:
  320. tz_copy = unpickler.loads(pickler.dumps(tz, proto))
  321. self.assertIs(tz_copy, tz)
  322. def test_copy(self):
  323. for tz in self.ACDT, self.EST, timezone.min, timezone.max:
  324. tz_copy = copy.copy(tz)
  325. self.assertEqual(tz_copy, tz)
  326. tz = timezone.utc
  327. tz_copy = copy.copy(tz)
  328. self.assertIs(tz_copy, tz)
  329. def test_deepcopy(self):
  330. for tz in self.ACDT, self.EST, timezone.min, timezone.max:
  331. tz_copy = copy.deepcopy(tz)
  332. self.assertEqual(tz_copy, tz)
  333. tz = timezone.utc
  334. tz_copy = copy.deepcopy(tz)
  335. self.assertIs(tz_copy, tz)
  336. def test_offset_boundaries(self):
  337. # Test timedeltas close to the boundaries
  338. time_deltas = [
  339. timedelta(hours=23, minutes=59),
  340. timedelta(hours=23, minutes=59, seconds=59),
  341. timedelta(hours=23, minutes=59, seconds=59, microseconds=999999),
  342. ]
  343. time_deltas.extend([-delta for delta in time_deltas])
  344. for delta in time_deltas:
  345. with self.subTest(test_type='good', delta=delta):
  346. timezone(delta)
  347. # Test timedeltas on and outside the boundaries
  348. bad_time_deltas = [
  349. timedelta(hours=24),
  350. timedelta(hours=24, microseconds=1),
  351. ]
  352. bad_time_deltas.extend([-delta for delta in bad_time_deltas])
  353. for delta in bad_time_deltas:
  354. with self.subTest(test_type='bad', delta=delta):
  355. with self.assertRaises(ValueError):
  356. timezone(delta)
  357. def test_comparison_with_tzinfo(self):
  358. # Constructing tzinfo objects directly should not be done by users
  359. # and serves only to check the bug described in bpo-37915
  360. self.assertNotEqual(timezone.utc, tzinfo())
  361. self.assertNotEqual(timezone(timedelta(hours=1)), tzinfo())
  362. #############################################################################
  363. # Base class for testing a particular aspect of timedelta, time, date and
  364. # datetime comparisons.
  365. class HarmlessMixedComparison:
  366. # Test that __eq__ and __ne__ don't complain for mixed-type comparisons.
  367. # Subclasses must define 'theclass', and theclass(1, 1, 1) must be a
  368. # legit constructor.
  369. def test_harmless_mixed_comparison(self):
  370. me = self.theclass(1, 1, 1)
  371. self.assertFalse(me == ())
  372. self.assertTrue(me != ())
  373. self.assertFalse(() == me)
  374. self.assertTrue(() != me)
  375. self.assertIn(me, [1, 20, [], me])
  376. self.assertIn([], [me, 1, 20, []])
  377. # Comparison to objects of unsupported types should return
  378. # NotImplemented which falls back to the right hand side's __eq__
  379. # method. In this case, ALWAYS_EQ.__eq__ always returns True.
  380. # ALWAYS_EQ.__ne__ always returns False.
  381. self.assertTrue(me == ALWAYS_EQ)
  382. self.assertFalse(me != ALWAYS_EQ)
  383. # If the other class explicitly defines ordering
  384. # relative to our class, it is allowed to do so
  385. self.assertTrue(me < LARGEST)
  386. self.assertFalse(me > LARGEST)
  387. self.assertTrue(me <= LARGEST)
  388. self.assertFalse(me >= LARGEST)
  389. self.assertFalse(me < SMALLEST)
  390. self.assertTrue(me > SMALLEST)
  391. self.assertFalse(me <= SMALLEST)
  392. self.assertTrue(me >= SMALLEST)
  393. def test_harmful_mixed_comparison(self):
  394. me = self.theclass(1, 1, 1)
  395. self.assertRaises(TypeError, lambda: me < ())
  396. self.assertRaises(TypeError, lambda: me <= ())
  397. self.assertRaises(TypeError, lambda: me > ())
  398. self.assertRaises(TypeError, lambda: me >= ())
  399. self.assertRaises(TypeError, lambda: () < me)
  400. self.assertRaises(TypeError, lambda: () <= me)
  401. self.assertRaises(TypeError, lambda: () > me)
  402. self.assertRaises(TypeError, lambda: () >= me)
  403. #############################################################################
  404. # timedelta tests
  405. class TestTimeDelta(HarmlessMixedComparison, unittest.TestCase):
  406. theclass = timedelta
  407. def test_constructor(self):
  408. eq = self.assertEqual
  409. td = timedelta
  410. # Check keyword args to constructor
  411. eq(td(), td(weeks=0, days=0, hours=0, minutes=0, seconds=0,
  412. milliseconds=0, microseconds=0))
  413. eq(td(1), td(days=1))
  414. eq(td(0, 1), td(seconds=1))
  415. eq(td(0, 0, 1), td(microseconds=1))
  416. eq(td(weeks=1), td(days=7))
  417. eq(td(days=1), td(hours=24))
  418. eq(td(hours=1), td(minutes=60))
  419. eq(td(minutes=1), td(seconds=60))
  420. eq(td(seconds=1), td(milliseconds=1000))
  421. eq(td(milliseconds=1), td(microseconds=1000))
  422. # Check float args to constructor
  423. eq(td(weeks=1.0/7), td(days=1))
  424. eq(td(days=1.0/24), td(hours=1))
  425. eq(td(hours=1.0/60), td(minutes=1))
  426. eq(td(minutes=1.0/60), td(seconds=1))
  427. eq(td(seconds=0.001), td(milliseconds=1))
  428. eq(td(milliseconds=0.001), td(microseconds=1))
  429. def test_computations(self):
  430. eq = self.assertEqual
  431. td = timedelta
  432. a = td(7) # One week
  433. b = td(0, 60) # One minute
  434. c = td(0, 0, 1000) # One millisecond
  435. eq(a+b+c, td(7, 60, 1000))
  436. eq(a-b, td(6, 24*3600 - 60))
  437. eq(b.__rsub__(a), td(6, 24*3600 - 60))
  438. eq(-a, td(-7))
  439. eq(+a, td(7))
  440. eq(-b, td(-1, 24*3600 - 60))
  441. eq(-c, td(-1, 24*3600 - 1, 999000))
  442. eq(abs(a), a)
  443. eq(abs(-a), a)
  444. eq(td(6, 24*3600), a)
  445. eq(td(0, 0, 60*1000000), b)
  446. eq(a*10, td(70))
  447. eq(a*10, 10*a)
  448. eq(a*10, 10*a)
  449. eq(b*10, td(0, 600))
  450. eq(10*b, td(0, 600))
  451. eq(b*10, td(0, 600))
  452. eq(c*10, td(0, 0, 10000))
  453. eq(10*c, td(0, 0, 10000))
  454. eq(c*10, td(0, 0, 10000))
  455. eq(a*-1, -a)
  456. eq(b*-2, -b-b)
  457. eq(c*-2, -c+-c)
  458. eq(b*(60*24), (b*60)*24)
  459. eq(b*(60*24), (60*b)*24)
  460. eq(c*1000, td(0, 1))
  461. eq(1000*c, td(0, 1))
  462. eq(a//7, td(1))
  463. eq(b//10, td(0, 6))
  464. eq(c//1000, td(0, 0, 1))
  465. eq(a//10, td(0, 7*24*360))
  466. eq(a//3600000, td(0, 0, 7*24*1000))
  467. eq(a/0.5, td(14))
  468. eq(b/0.5, td(0, 120))
  469. eq(a/7, td(1))
  470. eq(b/10, td(0, 6))
  471. eq(c/1000, td(0, 0, 1))
  472. eq(a/10, td(0, 7*24*360))
  473. eq(a/3600000, td(0, 0, 7*24*1000))
  474. # Multiplication by float
  475. us = td(microseconds=1)
  476. eq((3*us) * 0.5, 2*us)
  477. eq((5*us) * 0.5, 2*us)
  478. eq(0.5 * (3*us), 2*us)
  479. eq(0.5 * (5*us), 2*us)
  480. eq((-3*us) * 0.5, -2*us)
  481. eq((-5*us) * 0.5, -2*us)
  482. # Issue #23521
  483. eq(td(seconds=1) * 0.123456, td(microseconds=123456))
  484. eq(td(seconds=1) * 0.6112295, td(microseconds=611229))
  485. # Division by int and float
  486. eq((3*us) / 2, 2*us)
  487. eq((5*us) / 2, 2*us)
  488. eq((-3*us) / 2.0, -2*us)
  489. eq((-5*us) / 2.0, -2*us)
  490. eq((3*us) / -2, -2*us)
  491. eq((5*us) / -2, -2*us)
  492. eq((3*us) / -2.0, -2*us)
  493. eq((5*us) / -2.0, -2*us)
  494. for i in range(-10, 10):
  495. eq((i*us/3)//us, round(i/3))
  496. for i in range(-10, 10):
  497. eq((i*us/-3)//us, round(i/-3))
  498. # Issue #23521
  499. eq(td(seconds=1) / (1 / 0.6112295), td(microseconds=611229))
  500. # Issue #11576
  501. eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998),
  502. td(0, 0, 1))
  503. eq(td(999999999, 1, 1) - td(999999999, 1, 0),
  504. td(0, 0, 1))
  505. def test_disallowed_computations(self):
  506. a = timedelta(42)
  507. # Add/sub ints or floats should be illegal
  508. for i in 1, 1.0:
  509. self.assertRaises(TypeError, lambda: a+i)
  510. self.assertRaises(TypeError, lambda: a-i)
  511. self.assertRaises(TypeError, lambda: i+a)
  512. self.assertRaises(TypeError, lambda: i-a)
  513. # Division of int by timedelta doesn't make sense.
  514. # Division by zero doesn't make sense.
  515. zero = 0
  516. self.assertRaises(TypeError, lambda: zero // a)
  517. self.assertRaises(ZeroDivisionError, lambda: a // zero)
  518. self.assertRaises(ZeroDivisionError, lambda: a / zero)
  519. self.assertRaises(ZeroDivisionError, lambda: a / 0.0)
  520. self.assertRaises(TypeError, lambda: a / '')
  521. @support.requires_IEEE_754
  522. def test_disallowed_special(self):
  523. a = timedelta(42)
  524. self.assertRaises(ValueError, a.__mul__, NAN)
  525. self.assertRaises(ValueError, a.__truediv__, NAN)
  526. def test_basic_attributes(self):
  527. days, seconds, us = 1, 7, 31
  528. td = timedelta(days, seconds, us)
  529. self.assertEqual(td.days, days)
  530. self.assertEqual(td.seconds, seconds)
  531. self.assertEqual(td.microseconds, us)
  532. def test_total_seconds(self):
  533. td = timedelta(days=365)
  534. self.assertEqual(td.total_seconds(), 31536000.0)
  535. for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:
  536. td = timedelta(seconds=total_seconds)
  537. self.assertEqual(td.total_seconds(), total_seconds)
  538. # Issue8644: Test that td.total_seconds() has the same
  539. # accuracy as td / timedelta(seconds=1).
  540. for ms in [-1, -2, -123]:
  541. td = timedelta(microseconds=ms)
  542. self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))
  543. def test_carries(self):
  544. t1 = timedelta(days=100,
  545. weeks=-7,
  546. hours=-24*(100-49),
  547. minutes=-3,
  548. seconds=12,
  549. microseconds=(3*60 - 12) * 1e6 + 1)
  550. t2 = timedelta(microseconds=1)
  551. self.assertEqual(t1, t2)
  552. def test_hash_equality(self):
  553. t1 = timedelta(days=100,
  554. weeks=-7,
  555. hours=-24*(100-49),
  556. minutes=-3,
  557. seconds=12,
  558. microseconds=(3*60 - 12) * 1000000)
  559. t2 = timedelta()
  560. self.assertEqual(hash(t1), hash(t2))
  561. t1 += timedelta(weeks=7)
  562. t2 += timedelta(days=7*7)
  563. self.assertEqual(t1, t2)
  564. self.assertEqual(hash(t1), hash(t2))
  565. d = {t1: 1}
  566. d[t2] = 2
  567. self.assertEqual(len(d), 1)
  568. self.assertEqual(d[t1], 2)
  569. def test_pickling(self):
  570. args = 12, 34, 56
  571. orig = timedelta(*args)
  572. for pickler, unpickler, proto in pickle_choices:
  573. green = pickler.dumps(orig, proto)
  574. derived = unpickler.loads(green)
  575. self.assertEqual(orig, derived)
  576. def test_compare(self):
  577. t1 = timedelta(2, 3, 4)
  578. t2 = timedelta(2, 3, 4)
  579. self.assertEqual(t1, t2)
  580. self.assertTrue(t1 <= t2)
  581. self.assertTrue(t1 >= t2)
  582. self.assertFalse(t1 != t2)
  583. self.assertFalse(t1 < t2)
  584. self.assertFalse(t1 > t2)
  585. for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
  586. t2 = timedelta(*args) # this is larger than t1
  587. self.assertTrue(t1 < t2)
  588. self.assertTrue(t2 > t1)
  589. self.assertTrue(t1 <= t2)
  590. self.assertTrue(t2 >= t1)
  591. self.assertTrue(t1 != t2)
  592. self.assertTrue(t2 != t1)
  593. self.assertFalse(t1 == t2)
  594. self.assertFalse(t2 == t1)
  595. self.assertFalse(t1 > t2)
  596. self.assertFalse(t2 < t1)
  597. self.assertFalse(t1 >= t2)
  598. self.assertFalse(t2 <= t1)
  599. for badarg in OTHERSTUFF:
  600. self.assertEqual(t1 == badarg, False)
  601. self.assertEqual(t1 != badarg, True)
  602. self.assertEqual(badarg == t1, False)
  603. self.assertEqual(badarg != t1, True)
  604. self.assertRaises(TypeError, lambda: t1 <= badarg)
  605. self.assertRaises(TypeError, lambda: t1 < badarg)
  606. self.assertRaises(TypeError, lambda: t1 > badarg)
  607. self.assertRaises(TypeError, lambda: t1 >= badarg)
  608. self.assertRaises(TypeError, lambda: badarg <= t1)
  609. self.assertRaises(TypeError, lambda: badarg < t1)
  610. self.assertRaises(TypeError, lambda: badarg > t1)
  611. self.assertRaises(TypeError, lambda: badarg >= t1)
  612. def test_str(self):
  613. td = timedelta
  614. eq = self.assertEqual
  615. eq(str(td(1)), "1 day, 0:00:00")
  616. eq(str(td(-1)), "-1 day, 0:00:00")
  617. eq(str(td(2)), "2 days, 0:00:00")
  618. eq(str(td(-2)), "-2 days, 0:00:00")
  619. eq(str(td(hours=12, minutes=58, seconds=59)), "12:58:59")
  620. eq(str(td(hours=2, minutes=3, seconds=4)), "2:03:04")
  621. eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)),
  622. "-210 days, 23:12:34")
  623. eq(str(td(milliseconds=1)), "0:00:00.001000")
  624. eq(str(td(microseconds=3)), "0:00:00.000003")
  625. eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,
  626. microseconds=999999)),
  627. "999999999 days, 23:59:59.999999")
  628. def test_repr(self):
  629. name = 'datetime.' + self.theclass.__name__
  630. self.assertEqual(repr(self.theclass(1)),
  631. "%s(days=1)" % name)
  632. self.assertEqual(repr(self.theclass(10, 2)),
  633. "%s(days=10, seconds=2)" % name)
  634. self.assertEqual(repr(self.theclass(-10, 2, 400000)),
  635. "%s(days=-10, seconds=2, microseconds=400000)" % name)
  636. self.assertEqual(repr(self.theclass(seconds=60)),
  637. "%s(seconds=60)" % name)
  638. self.assertEqual(repr(self.theclass()),
  639. "%s(0)" % name)
  640. self.assertEqual(repr(self.theclass(microseconds=100)),
  641. "%s(microseconds=100)" % name)
  642. self.assertEqual(repr(self.theclass(days=1, microseconds=100)),
  643. "%s(days=1, microseconds=100)" % name)
  644. self.assertEqual(repr(self.theclass(seconds=1, microseconds=100)),
  645. "%s(seconds=1, microseconds=100)" % name)
  646. def test_roundtrip(self):
  647. for td in (timedelta(days=999999999, hours=23, minutes=59,
  648. seconds=59, microseconds=999999),
  649. timedelta(days=-999999999),
  650. timedelta(days=-999999999, seconds=1),
  651. timedelta(days=1, seconds=2, microseconds=3)):
  652. # Verify td -> string -> td identity.
  653. s = repr(td)
  654. self.assertTrue(s.startswith('datetime.'))
  655. s = s[9:]
  656. td2 = eval(s)
  657. self.assertEqual(td, td2)
  658. # Verify identity via reconstructing from pieces.
  659. td2 = timedelta(td.days, td.seconds, td.microseconds)
  660. self.assertEqual(td, td2)
  661. def test_resolution_info(self):
  662. self.assertIsInstance(timedelta.min, timedelta)
  663. self.assertIsInstance(timedelta.max, timedelta)
  664. self.assertIsInstance(timedelta.resolution, timedelta)
  665. self.assertTrue(timedelta.max > timedelta.min)
  666. self.assertEqual(timedelta.min, timedelta(-999999999))
  667. self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))
  668. self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))
  669. def test_overflow(self):
  670. tiny = timedelta.resolution
  671. td = timedelta.min + tiny
  672. td -= tiny # no problem
  673. self.assertRaises(OverflowError, td.__sub__, tiny)
  674. self.assertRaises(OverflowError, td.__add__, -tiny)
  675. td = timedelta.max - tiny
  676. td += tiny # no problem
  677. self.assertRaises(OverflowError, td.__add__, tiny)
  678. self.assertRaises(OverflowError, td.__sub__, -tiny)
  679. self.assertRaises(OverflowError, lambda: -timedelta.max)
  680. day = timedelta(1)
  681. self.assertRaises(OverflowError, day.__mul__, 10**9)
  682. self.assertRaises(OverflowError, day.__mul__, 1e9)
  683. self.assertRaises(OverflowError, day.__truediv__, 1e-20)
  684. self.assertRaises(OverflowError, day.__truediv__, 1e-10)
  685. self.assertRaises(OverflowError, day.__truediv__, 9e-10)
  686. @support.requires_IEEE_754
  687. def _test_overflow_special(self):
  688. day = timedelta(1)
  689. self.assertRaises(OverflowError, day.__mul__, INF)
  690. self.assertRaises(OverflowError, day.__mul__, -INF)
  691. def test_microsecond_rounding(self):
  692. td = timedelta
  693. eq = self.assertEqual
  694. # Single-field rounding.
  695. eq(td(milliseconds=0.4/1000), td(0)) # rounds to 0
  696. eq(td(milliseconds=-0.4/1000), td(0)) # rounds to 0
  697. eq(td(milliseconds=0.5/1000), td(microseconds=0))
  698. eq(td(milliseconds=-0.5/1000), td(microseconds=-0))
  699. eq(td(milliseconds=0.6/1000), td(microseconds=1))
  700. eq(td(milliseconds=-0.6/1000), td(microseconds=-1))
  701. eq(td(milliseconds=1.5/1000), td(microseconds=2))
  702. eq(td(milliseconds=-1.5/1000), td(microseconds=-2))
  703. eq(td(seconds=0.5/10**6), td(microseconds=0))
  704. eq(td(seconds=-0.5/10**6), td(microseconds=-0))
  705. eq(td(seconds=1/2**7), td(microseconds=7812))
  706. eq(td(seconds=-1/2**7), td(microseconds=-7812))
  707. # Rounding due to contributions from more than one field.
  708. us_per_hour = 3600e6
  709. us_per_day = us_per_hour * 24
  710. eq(td(days=.4/us_per_day), td(0))
  711. eq(td(hours=.2/us_per_hour), td(0))
  712. eq(td(days=.4/us_per_day, hours=.2/us_per_hour), td(microseconds=1))
  713. eq(td(days=-.4/us_per_day), td(0))
  714. eq(td(hours=-.2/us_per_hour), td(0))
  715. eq(td(days=-.4/us_per_day, hours=-.2/us_per_hour), td(microseconds=-1))
  716. # Test for a patch in Issue 8860
  717. eq(td(microseconds=0.5), 0.5*td(microseconds=1.0))
  718. eq(td(microseconds=0.5)//td.resolution, 0.5*td.resolution//td.resolution)
  719. def test_massive_normalization(self):
  720. td = timedelta(microseconds=-1)
  721. self.assertEqual((td.days, td.seconds, td.microseconds),
  722. (-1, 24*3600-1, 999999))
  723. def test_bool(self):
  724. self.assertTrue(timedelta(1))
  725. self.assertTrue(timedelta(0, 1))
  726. self.assertTrue(timedelta(0, 0, 1))
  727. self.assertTrue(timedelta(microseconds=1))
  728. self.assertFalse(timedelta(0))
  729. def test_subclass_timedelta(self):
  730. class T(timedelta):
  731. @staticmethod
  732. def from_td(td):
  733. return T(td.days, td.seconds, td.microseconds)
  734. def as_hours(self):
  735. sum = (self.days * 24 +
  736. self.seconds / 3600.0 +
  737. self.microseconds / 3600e6)
  738. return round(sum)
  739. t1 = T(days=1)
  740. self.assertIs(type(t1), T)
  741. self.assertEqual(t1.as_hours(), 24)
  742. t2 = T(days=-1, seconds=-3600)
  743. self.assertIs(type(t2), T)
  744. self.assertEqual(t2.as_hours(), -25)
  745. t3 = t1 + t2
  746. self.assertIs(type(t3), timedelta)
  747. t4 = T.from_td(t3)
  748. self.assertIs(type(t4), T)
  749. self.assertEqual(t3.days, t4.days)
  750. self.assertEqual(t3.seconds, t4.seconds)
  751. self.assertEqual(t3.microseconds, t4.microseconds)
  752. self.assertEqual(str(t3), str(t4))
  753. self.assertEqual(t4.as_hours(), -1)
  754. def test_subclass_date(self):
  755. class DateSubclass(date):
  756. pass
  757. d1 = DateSubclass(2018, 1, 5)
  758. td = timedelta(days=1)
  759. tests = [
  760. ('add', lambda d, t: d + t, DateSubclass(2018, 1, 6)),
  761. ('radd', lambda d, t: t + d, DateSubclass(2018, 1, 6)),
  762. ('sub', lambda d, t: d - t, DateSubclass(2018, 1, 4)),
  763. ]
  764. for name, func, expected in tests:
  765. with self.subTest(name):
  766. act = func(d1, td)
  767. self.assertEqual(act, expected)
  768. self.assertIsInstance(act, DateSubclass)
  769. def test_subclass_datetime(self):
  770. class DateTimeSubclass(datetime):
  771. pass
  772. d1 = DateTimeSubclass(2018, 1, 5, 12, 30)
  773. td = timedelta(days=1, minutes=30)
  774. tests = [
  775. ('add', lambda d, t: d + t, DateTimeSubclass(2018, 1, 6, 13)),
  776. ('radd', lambda d, t: t + d, DateTimeSubclass(2018, 1, 6, 13)),
  777. ('sub', lambda d, t: d - t, DateTimeSubclass(2018, 1, 4, 12)),
  778. ]
  779. for name, func, expected in tests:
  780. with self.subTest(name):
  781. act = func(d1, td)
  782. self.assertEqual(act, expected)
  783. self.assertIsInstance(act, DateTimeSubclass)
  784. def test_division(self):
  785. t = timedelta(hours=1, minutes=24, seconds=19)
  786. second = timedelta(seconds=1)
  787. self.assertEqual(t / second, 5059.0)
  788. self.assertEqual(t // second, 5059)
  789. t = timedelta(minutes=2, seconds=30)
  790. minute = timedelta(minutes=1)
  791. self.assertEqual(t / minute, 2.5)
  792. self.assertEqual(t // minute, 2)
  793. zerotd = timedelta(0)
  794. self.assertRaises(ZeroDivisionError, truediv, t, zerotd)
  795. self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)
  796. # self.assertRaises(TypeError, truediv, t, 2)
  797. # note: floor division of a timedelta by an integer *is*
  798. # currently permitted.
  799. def test_remainder(self):
  800. t = timedelta(minutes=2, seconds=30)
  801. minute = timedelta(minutes=1)
  802. r = t % minute
  803. self.assertEqual(r, timedelta(seconds=30))
  804. t = timedelta(minutes=-2, seconds=30)
  805. r = t % minute
  806. self.assertEqual(r, timedelta(seconds=30))
  807. zerotd = timedelta(0)
  808. self.assertRaises(ZeroDivisionError, mod, t, zerotd)
  809. self.assertRaises(TypeError, mod, t, 10)
  810. def test_divmod(self):
  811. t = timedelta(minutes=2, seconds=30)
  812. minute = timedelta(minutes=1)
  813. q, r = divmod(t, minute)
  814. self.assertEqual(q, 2)
  815. self.assertEqual(r, timedelta(seconds=30))
  816. t = timedelta(minutes=-2, seconds=30)
  817. q, r = divmod(t, minute)
  818. self.assertEqual(q, -2)
  819. self.assertEqual(r, timedelta(seconds=30))
  820. zerotd = timedelta(0)
  821. self.assertRaises(ZeroDivisionError, divmod, t, zerotd)
  822. self.assertRaises(TypeError, divmod, t, 10)
  823. def test_issue31293(self):
  824. # The interpreter shouldn't crash in case a timedelta is divided or
  825. # multiplied by a float with a bad as_integer_ratio() method.
  826. def get_bad_float(bad_ratio):
  827. class BadFloat(float):
  828. def as_integer_ratio(self):
  829. return bad_ratio
  830. return BadFloat()
  831. with self.assertRaises(TypeError):
  832. timedelta() / get_bad_float(1 << 1000)
  833. with self.assertRaises(TypeError):
  834. timedelta() * get_bad_float(1 << 1000)
  835. for bad_ratio in [(), (42, ), (1, 2, 3)]:
  836. with self.assertRaises(ValueError):
  837. timedelta() / get_bad_float(bad_ratio)
  838. with self.assertRaises(ValueError):
  839. timedelta() * get_bad_float(bad_ratio)
  840. def test_issue31752(self):
  841. # The interpreter shouldn't crash because divmod() returns negative
  842. # remainder.
  843. class BadInt(int):
  844. def __mul__(self, other):
  845. return Prod()
  846. def __rmul__(self, other):
  847. return Prod()
  848. def __floordiv__(self, other):
  849. return Prod()
  850. def __rfloordiv__(self, other):
  851. return Prod()
  852. class Prod:
  853. def __add__(self, other):
  854. return Sum()
  855. def __radd__(self, other):
  856. return Sum()
  857. class Sum(int):
  858. def __divmod__(self, other):
  859. return divmodresult
  860. for divmodresult in [None, (), (0, 1, 2), (0, -1)]:
  861. with self.subTest(divmodresult=divmodresult):
  862. # The following examples should not crash.
  863. try:
  864. timedelta(microseconds=BadInt(1))
  865. except TypeError:
  866. pass
  867. try:
  868. timedelta(hours=BadInt(1))
  869. except TypeError:
  870. pass
  871. try:
  872. timedelta(weeks=BadInt(1))
  873. except (TypeError, ValueError):
  874. pass
  875. try:
  876. timedelta(1) * BadInt(1)
  877. except (TypeError, ValueError):
  878. pass
  879. try:
  880. BadInt(1) * timedelta(1)
  881. except TypeError:
  882. pass
  883. try:
  884. timedelta(1) // BadInt(1)
  885. except TypeError:
  886. pass
  887. #############################################################################
  888. # date tests
  889. class TestDateOnly(unittest.TestCase):
  890. # Tests here won't pass if also run on datetime objects, so don't
  891. # subclass this to test datetimes too.
  892. def test_delta_non_days_ignored(self):
  893. dt = date(2000, 1, 2)
  894. delta = timedelta(days=1, hours=2, minutes=3, seconds=4,
  895. microseconds=5)
  896. days = timedelta(delta.days)
  897. self.assertEqual(days, timedelta(1))
  898. dt2 = dt + delta
  899. self.assertEqual(dt2, dt + days)
  900. dt2 = delta + dt
  901. self.assertEqual(dt2, dt + days)
  902. dt2 = dt - delta
  903. self.assertEqual(dt2, dt - days)
  904. delta = -delta
  905. days = timedelta(delta.days)
  906. self.assertEqual(days, timedelta(-2))
  907. dt2 = dt + delta
  908. self.assertEqual(dt2, dt + days)
  909. dt2 = delta + dt
  910. self.assertEqual(dt2, dt + days)
  911. dt2 = dt - delta
  912. self.assertEqual(dt2, dt - days)
  913. class SubclassDate(date):
  914. sub_var = 1
  915. class TestDate(HarmlessMixedComparison, unittest.TestCase):
  916. # Tests here should pass for both dates and datetimes, except for a
  917. # few tests that TestDateTime overrides.
  918. theclass = date
  919. def test_basic_attributes(self):
  920. dt = self.theclass(2002, 3, 1)
  921. self.assertEqual(dt.year, 2002)
  922. self.assertEqual(dt.month, 3)
  923. self.assertEqual(dt.day, 1)
  924. def test_roundtrip(self):
  925. for dt in (self.theclass(1, 2, 3),
  926. self.theclass.today()):
  927. # Verify dt -> string -> date identity.
  928. s = repr(dt)
  929. self.assertTrue(s.startswith('datetime.'))
  930. s = s[9:]
  931. dt2 = eval(s)
  932. self.assertEqual(dt, dt2)
  933. # Verify identity via reconstructing from pieces.
  934. dt2 = self.theclass(dt.year, dt.month, dt.day)
  935. self.assertEqual(dt, dt2)
  936. def test_ordinal_conversions(self):
  937. # Check some fixed values.
  938. for y, m, d, n in [(1, 1, 1, 1), # calendar origin
  939. (1, 12, 31, 365),
  940. (2, 1, 1, 366),
  941. # first example from "Calendrical Calculations"
  942. (1945, 11, 12, 710347)]:
  943. d = self.theclass(y, m, d)
  944. self.assertEqual(n, d.toordinal())
  945. fromord = self.theclass.fromordinal(n)
  946. self.assertEqual(d, fromord)
  947. if hasattr(fromord, "hour"):
  948. # if we're checking something fancier than a date, verify
  949. # the extra fields have been zeroed out
  950. self.assertEqual(fromord.hour, 0)
  951. self.assertEqual(fromord.minute, 0)
  952. self.assertEqual(fromord.second, 0)
  953. self.assertEqual(fromord.microsecond, 0)
  954. # Check first and last days of year spottily across the whole
  955. # range of years supported.
  956. for year in range(MINYEAR, MAXYEAR+1, 7):
  957. # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.
  958. d = self.theclass(year, 1, 1)
  959. n = d.toordinal()
  960. d2 = self.theclass.fromordinal(n)
  961. self.assertEqual(d, d2)
  962. # Verify that moving back a day gets to the end of year-1.
  963. if year > 1:
  964. d = self.theclass.fromordinal(n-1)
  965. d2 = self.theclass(year-1, 12, 31)
  966. self.assertEqual(d, d2)
  967. self.assertEqual(d2.toordinal(), n-1)
  968. # Test every day in a leap-year and a non-leap year.
  969. dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  970. for year, isleap in (2000, True), (2002, False):
  971. n = self.theclass(year, 1, 1).toordinal()
  972. for month, maxday in zip(range(1, 13), dim):
  973. if month == 2 and isleap:
  974. maxday += 1
  975. for day in range(1, maxday+1):
  976. d = self.theclass(year, month, day)
  977. self.assertEqual(d.toordinal(), n)
  978. self.assertEqual(d, self.theclass.fromordinal(n))
  979. n += 1
  980. def test_extreme_ordinals(self):
  981. a = self.theclass.min
  982. a = self.theclass(a.year, a.month, a.day) # get rid of time parts
  983. aord = a.toordinal()
  984. b = a.fromordinal(aord)
  985. self.assertEqual(a, b)
  986. self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))
  987. b = a + timedelta(days=1)
  988. self.assertEqual(b.toordinal(), aord + 1)
  989. self.assertEqual(b, self.theclass.fromordinal(aord + 1))
  990. a = self.theclass.max
  991. a = self.theclass(a.year, a.month, a.day) # get rid of time parts
  992. aord = a.toordinal()
  993. b = a.fromordinal(aord)
  994. self.assertEqual(a, b)
  995. self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))
  996. b = a - timedelta(days=1)
  997. self.assertEqual(b.toordinal(), aord - 1)
  998. self.assertEqual(b, self.theclass.fromordinal(aord - 1))
  999. def test_bad_constructor_arguments(self):
  1000. # bad years
  1001. self.theclass(MINYEAR, 1, 1) # no exception
  1002. self.theclass(MAXYEAR, 1, 1) # no exception
  1003. self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
  1004. self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
  1005. # bad months
  1006. self.theclass(2000, 1, 1) # no exception
  1007. self.theclass(2000, 12, 1) # no exception
  1008. self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
  1009. self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
  1010. # bad days
  1011. self.theclass(2000, 2, 29) # no exception
  1012. self.theclass(2004, 2, 29) # no exception
  1013. self.theclass(2400, 2, 29) # no exception
  1014. self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
  1015. self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
  1016. self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
  1017. self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
  1018. self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
  1019. self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
  1020. def test_hash_equality(self):
  1021. d = self.theclass(2000, 12, 31)
  1022. # same thing
  1023. e = self.theclass(2000, 12, 31)
  1024. self.assertEqual(d, e)
  1025. self.assertEqual(hash(d), hash(e))
  1026. dic = {d: 1}
  1027. dic[e] = 2
  1028. self.assertEqual(len(dic), 1)
  1029. self.assertEqual(dic[d], 2)
  1030. self.assertEqual(dic[e], 2)
  1031. d = self.theclass(2001, 1, 1)
  1032. # same thing
  1033. e = self.theclass(2001, 1, 1)
  1034. self.assertEqual(d, e)
  1035. self.assertEqual(hash(d), hash(e))
  1036. dic = {d: 1}
  1037. dic[e] = 2
  1038. self.assertEqual(len(dic), 1)
  1039. self.assertEqual(dic[d], 2)
  1040. self.assertEqual(dic[e], 2)
  1041. def test_computations(self):
  1042. a = self.theclass(2002, 1, 31)
  1043. b = self.theclass(1956, 1, 31)
  1044. c = self.theclass(2001,2,1)
  1045. diff = a-b
  1046. self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
  1047. self.assertEqual(diff.seconds, 0)
  1048. self.assertEqual(diff.microseconds, 0)
  1049. day = timedelta(1)
  1050. week = timedelta(7)
  1051. a = self.theclass(2002, 3, 2)
  1052. self.assertEqual(a + day, self.theclass(2002, 3, 3))
  1053. self.assertEqual(day + a, self.theclass(2002, 3, 3))
  1054. self.assertEqual(a - day, self.theclass(2002, 3, 1))
  1055. self.assertEqual(-day + a, self.theclass(2002, 3, 1))
  1056. self.assertEqual(a + week, self.theclass(2002, 3, 9))
  1057. self.assertEqual(a - week, self.theclass(2002, 2, 23))
  1058. self.assertEqual(a + 52*week, self.theclass(2003, 3, 1))
  1059. self.assertEqual(a - 52*week, self.theclass(2001, 3, 3))
  1060. self.assertEqual((a + week) - a, week)
  1061. self.assertEqual((a + day) - a, day)
  1062. self.assertEqual((a - week) - a, -week)
  1063. self.assertEqual((a - day) - a, -day)
  1064. self.assertEqual(a - (a + week), -week)
  1065. self.assertEqual(a - (a + day), -day)
  1066. self.assertEqual(a - (a - week), week)
  1067. self.assertEqual(a - (a - day), day)
  1068. self.assertEqual(c - (c - day), day)
  1069. # Add/sub ints or floats should be illegal
  1070. for i in 1, 1.0:
  1071. self.assertRaises(TypeError, lambda: a+i)
  1072. self.assertRaises(TypeError, lambda: a-i)
  1073. self.assertRaises(TypeError, lambda: i+a)
  1074. self.assertRaises(TypeError, lambda: i-a)
  1075. # delta - date is senseless.
  1076. self.assertRaises(TypeError, lambda: day - a)
  1077. # mixing date and (delta or date) via * or // is senseless
  1078. self.assertRaises(TypeError, lambda: day * a)
  1079. self.assertRaises(TypeError, lambda: a * day)
  1080. self.assertRaises(TypeError, lambda: day // a)
  1081. self.assertRaises(TypeError, lambda: a // day)
  1082. self.assertRaises(TypeError, lambda: a * a)
  1083. self.assertRaises(TypeError, lambda: a // a)
  1084. # date + date is senseless
  1085. self.assertRaises(TypeError, lambda: a + a)
  1086. def test_overflow(self):
  1087. tiny = self.theclass.resolution
  1088. for delta in [tiny, timedelta(1), timedelta(2)]:
  1089. dt = self.theclass.min + delta
  1090. dt -= delta # no problem
  1091. self.assertRaises(OverflowError, dt.__sub__, delta)
  1092. self.assertRaises(OverflowError, dt.__add__, -delta)
  1093. dt = self.theclass.max - delta
  1094. dt += delta # no problem
  1095. self.assertRaises(OverflowError, dt.__add__, delta)
  1096. self.assertRaises(OverflowError, dt.__sub__, -delta)
  1097. def test_fromtimestamp(self):
  1098. import time
  1099. # Try an arbitrary fixed value.
  1100. year, month, day = 1999, 9, 19
  1101. ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))
  1102. d = self.theclass.fromtimestamp(ts)
  1103. self.assertEqual(d.year, year)
  1104. self.assertEqual(d.month, month)
  1105. self.assertEqual(d.day, day)
  1106. def test_insane_fromtimestamp(self):
  1107. # It's possible that some platform maps time_t to double,
  1108. # and that this test will fail there. This test should
  1109. # exempt such platforms (provided they return reasonable
  1110. # results!).
  1111. for insane in -1e200, 1e200:
  1112. self.assertRaises(OverflowError, self.theclass.fromtimestamp,
  1113. insane)
  1114. def test_today(self):
  1115. import time
  1116. # We claim that today() is like fromtimestamp(time.time()), so
  1117. # prove it.
  1118. for dummy in range(3):
  1119. today = self.theclass.today()
  1120. ts = time.time()
  1121. todayagain = self.theclass.fromtimestamp(ts)
  1122. if today == todayagain:
  1123. break
  1124. # There are several legit reasons that could fail:
  1125. # 1. It recently became midnight, between the today() and the
  1126. # time() calls.
  1127. # 2. The platform time() has such fine resolution that we'll
  1128. # never get the same value twice.
  1129. # 3. The platform time() has poor resolution, and we just
  1130. # happened to call today() right before a resolution quantum
  1131. # boundary.
  1132. # 4. The system clock got fiddled between calls.
  1133. # In any case, wait a little while and try again.
  1134. time.sleep(0.1)
  1135. # It worked or it didn't. If it didn't, assume it's reason #2, and
  1136. # let the test pass if they're within half a second of each other.
  1137. if today != todayagain:
  1138. self.assertAlmostEqual(todayagain, today,
  1139. delta=timedelta(seconds=0.5))
  1140. def test_weekday(self):
  1141. for i in range(7):
  1142. # March 4, 2002 is a Monday
  1143. self.assertEqual(self.theclass(2002, 3, 4+i).weekday(), i)
  1144. self.assertEqual(self.theclass(2002, 3, 4+i).isoweekday(), i+1)
  1145. # January 2, 1956 is a Monday
  1146. self.assertEqual(self.theclass(1956, 1, 2+i).weekday(), i)
  1147. self.assertEqual(self.theclass(1956, 1, 2+i).isoweekday(), i+1)
  1148. def test_isocalendar(self):
  1149. # Check examples from
  1150. # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
  1151. week_mondays = [
  1152. ((2003, 12, 22), (2003, 52, 1)),
  1153. ((2003, 12, 29), (2004, 1, 1)),
  1154. ((2004, 1, 5), (2004, 2, 1)),
  1155. ((2009, 12, 21), (2009, 52, 1)),
  1156. ((2009, 12, 28), (2009, 53, 1)),
  1157. ((2010, 1, 4), (2010, 1, 1)),
  1158. ]
  1159. test_cases = []
  1160. for cal_date, iso_date in week_mondays:
  1161. base_date = self.theclass(*cal_date)
  1162. # Adds one test case for every day of the specified weeks
  1163. for i in range(7):
  1164. new_date = base_date + timedelta(i)
  1165. new_iso = iso_date[0:2] + (iso_date[2] + i,)
  1166. test_cases.append((new_date, new_iso))
  1167. for d, exp_iso in test_cases:
  1168. with self.subTest(d=d, comparison="tuple"):
  1169. self.assertEqual(d.isocalendar(), exp_iso)
  1170. # Check that the tuple contents are accessible by field name
  1171. with self.subTest(d=d, comparison="fields"):
  1172. t = d.isocalendar()
  1173. self.assertEqual((t.year, t.week, t.weekday), exp_iso)
  1174. def test_isocalendar_pickling(self):
  1175. """Test that the result of datetime.isocalendar() can be pickled.
  1176. The result of a round trip should be a plain tuple.
  1177. """
  1178. d = self.theclass(2019, 1, 1)
  1179. p = pickle.dumps(d.isocalendar())
  1180. res = pickle.loads(p)
  1181. self.assertEqual(type(res), tuple)
  1182. self.assertEqual(res, (2019, 1, 2))
  1183. def test_iso_long_years(self):
  1184. # Calculate long ISO years and compare to table from
  1185. # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
  1186. ISO_LONG_YEARS_TABLE = """
  1187. 4 32 60 88
  1188. 9 37 65 93
  1189. 15 43 71 99
  1190. 20 48 76
  1191. 26 54 82
  1192. 105 133 161 189
  1193. 111 139 167 195
  1194. 116 144 172
  1195. 122 150 178
  1196. 128 156 184
  1197. 201 229 257 285
  1198. 207 235 263 291
  1199. 212 240 268 296
  1200. 218 246 274
  1201. 224 252 280
  1202. 303 331 359 387
  1203. 308 336 364 392
  1204. 314 342 370 398
  1205. 320 348 376
  1206. 325 353 381
  1207. """
  1208. iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))
  1209. L = []
  1210. for i in range(400):
  1211. d = self.theclass(2000+i, 12, 31)
  1212. d1 = self.theclass(1600+i, 12, 31)
  1213. self.assertEqual(d.isocalendar()[1:], d1.isocalendar()[1:])
  1214. if d.isocalendar()[1] == 53:
  1215. L.append(i)
  1216. self.assertEqual(L, iso_long_years)
  1217. def test_isoformat(self):
  1218. t = self.theclass(2, 3, 2)
  1219. self.assertEqual(t.isoformat(), "0002-03-02")
  1220. def test_ctime(self):
  1221. t = self.theclass(2002, 3, 2)
  1222. self.assertEqual(t.ctime(), "Sat Mar 2 00:00:00 2002")
  1223. def test_strftime(self):
  1224. t = self.theclass(2005, 3, 2)
  1225. self.assertEqual(t.strftime("m:%m d:%d y:%y"), "m:03 d:02 y:05")
  1226. self.assertEqual(t.strftime(""), "") # SF bug #761337
  1227. self.assertEqual(t.strftime('x'*1000), 'x'*1000) # SF bug #1556784
  1228. self.assertRaises(TypeError, t.strftime) # needs an arg
  1229. self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
  1230. self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
  1231. # test that unicode input is allowed (issue 2782)
  1232. self.assertEqual(t.strftime("%m"), "03")
  1233. # A naive object replaces %z and %Z w/ empty strings.
  1234. self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
  1235. #make sure that invalid format specifiers are handled correctly
  1236. #self.assertRaises(ValueError, t.strftime, "%e")
  1237. #self.assertRaises(ValueError, t.strftime, "%")
  1238. #self.assertRaises(ValueError, t.strftime, "%#")
  1239. #oh well, some systems just ignore those invalid ones.
  1240. #at least, exercise them to make sure that no crashes
  1241. #are generated
  1242. for f in ["%e", "%", "%#"]:
  1243. try:
  1244. t.strftime(f)
  1245. except ValueError:
  1246. pass
  1247. # bpo-34482: Check that surrogates don't cause a crash.
  1248. try:
  1249. t.strftime('%y\ud800%m')
  1250. except UnicodeEncodeError:
  1251. pass
  1252. #check that this standard extension works
  1253. t.strftime("%f")
  1254. def test_strftime_trailing_percent(self):
  1255. # bpo-35066: Make sure trailing '%' doesn't cause datetime's strftime to
  1256. # complain. Different libcs have different handling of trailing
  1257. # percents, so we simply check datetime's strftime acts the same as
  1258. # time.strftime.
  1259. t = self.theclass(2005, 3, 2)
  1260. try:
  1261. _time.strftime('%')
  1262. except ValueError:
  1263. self.skipTest('time module does not support trailing %')
  1264. self.assertEqual(t.strftime('%'), _time.strftime('%', t.timetuple()))
  1265. self.assertEqual(
  1266. t.strftime("m:%m d:%d y:%y %"),
  1267. _time.strftime("m:03 d:02 y:05 %", t.timetuple()),
  1268. )
  1269. def test_format(self):
  1270. dt = self.theclass(2007, 9, 10)
  1271. self.assertEqual(dt.__format__(''), str(dt))
  1272. with self.assertRaisesRegex(TypeError, 'must be str, not int'):
  1273. dt.__format__(123)
  1274. # check that a derived class's __str__() gets called
  1275. class A(self.theclass):
  1276. def __str__(self):
  1277. return 'A'
  1278. a = A(2007, 9, 10)
  1279. self.assertEqual(a.__format__(''), 'A')
  1280. # check that a derived class's strftime gets called
  1281. class B(self.theclass):
  1282. def strftime(self, format_spec):
  1283. return 'B'
  1284. b = B(2007, 9, 10)
  1285. self.assertEqual(b.__format__(''), str(dt))
  1286. for fmt in ["m:%m d:%d y:%y",
  1287. "m:%m d:%d y:%y H:%H M:%M S:%S",
  1288. "%z %Z",
  1289. ]:
  1290. self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
  1291. self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
  1292. self.assertEqual(b.__format__(fmt), 'B')
  1293. def test_resolution_info(self):
  1294. # XXX: Should min and max respect subclassing?
  1295. if issubclass(self.theclass, datetime):
  1296. expected_class = datetime
  1297. else:
  1298. expected_class = date
  1299. self.assertIsInstance(self.theclass.min, expected_class)
  1300. self.assertIsInstance(self.theclass.max, expected_class)
  1301. self.assertIsInstance(self.theclass.resolution, timedelta)
  1302. self.assertTrue(self.theclass.max > self.theclass.min)
  1303. def test_extreme_timedelta(self):
  1304. big = self.theclass.max - self.theclass.min
  1305. # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds
  1306. n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds
  1307. # n == 315537897599999999 ~= 2**58.13
  1308. justasbig = timedelta(0, 0, n)
  1309. self.assertEqual(big, justasbig)
  1310. self.assertEqual(self.theclass.min + big, self.theclass.max)
  1311. self.assertEqual(self.theclass.max - big, self.theclass.min)
  1312. def test_timetuple(self):
  1313. for i in range(7):
  1314. # January 2, 1956 is a Monday (0)
  1315. d = self.theclass(1956, 1, 2+i)
  1316. t = d.timetuple()
  1317. self.assertEqual(t, (1956, 1, 2+i, 0, 0, 0, i, 2+i, -1))
  1318. # February 1, 1956 is a Wednesday (2)
  1319. d = self.theclass(1956, 2, 1+i)
  1320. t = d.timetuple()
  1321. self.assertEqual(t, (1956, 2, 1+i, 0, 0, 0, (2+i)%7, 32+i, -1))
  1322. # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day
  1323. # of the year.
  1324. d = self.theclass(1956, 3, 1+i)
  1325. t = d.timetuple()
  1326. self.assertEqual(t, (1956, 3, 1+i, 0, 0, 0, (3+i)%7, 61+i, -1))
  1327. self.assertEqual(t.tm_year, 1956)
  1328. self.assertEqual(t.tm_mon, 3)
  1329. self.assertEqual(t.tm_mday, 1+i)
  1330. self.assertEqual(t.tm_hour, 0)
  1331. self.assertEqual(t.tm_min, 0)
  1332. self.assertEqual(t.tm_sec, 0)
  1333. self.assertEqual(t.tm_wday, (3+i)%7)
  1334. self.assertEqual(t.tm_yday, 61+i)
  1335. self.assertEqual(t.tm_isdst, -1)
  1336. def test_pickling(self):
  1337. args = 6, 7, 23
  1338. orig = self.theclass(*args)
  1339. for pickler, unpickler, proto in pickle_choices:
  1340. green = pickler.dumps(orig, proto)
  1341. derived = unpickler.loads(green)
  1342. self.assertEqual(orig, derived)
  1343. self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
  1344. def test_compat_unpickle(self):
  1345. tests = [
  1346. b"cdatetime\ndate\n(S'\\x07\\xdf\\x0b\\x1b'\ntR.",
  1347. b'cdatetime\ndate\n(U\x04\x07\xdf\x0b\x1btR.',
  1348. b'\x80\x02cdatetime\ndate\nU\x04\x07\xdf\x0b\x1b\x85R.',
  1349. ]
  1350. args = 2015, 11, 27
  1351. expected = self.theclass(*args)
  1352. for data in tests:
  1353. for loads in pickle_loads:
  1354. derived = loads(data, encoding='latin1')
  1355. self.assertEqual(derived, expected)
  1356. def test_compare(self):
  1357. t1 = self.theclass(2, 3, 4)
  1358. t2 = self.theclass(2, 3, 4)
  1359. self.assertEqual(t1, t2)
  1360. self.assertTrue(t1 <= t2)
  1361. self.assertTrue(t1 >= t2)
  1362. self.assertFalse(t1 != t2)
  1363. self.assertFalse(t1 < t2)
  1364. self.assertFalse(t1 > t2)
  1365. for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):
  1366. t2 = self.theclass(*args) # this is larger than t1
  1367. self.assertTrue(t1 < t2)
  1368. self.assertTrue(t2 > t1)
  1369. self.assertTrue(t1 <= t2)
  1370. self.assertTrue(t2 >= t1)
  1371. self.assertTrue(t1 != t2)
  1372. self.assertTrue(t2 != t1)
  1373. self.assertFalse(t1 == t2)
  1374. self.assertFalse(t2 == t1)
  1375. self.assertFalse(t1 > t2)
  1376. self.assertFalse(t2 < t1)
  1377. self.assertFalse(t1 >= t2)
  1378. self.assertFalse(t2 <= t1)
  1379. for badarg in OTHERSTUFF:
  1380. self.assertEqual(t1 == badarg, False)
  1381. self.assertEqual(t1 != badarg, True)
  1382. self.assertEqual(badarg == t1, False)
  1383. self.assertEqual(badarg != t1, True)
  1384. self.assertRaises(TypeError, lambda: t1 < badarg)
  1385. self.assertRaises(TypeError, lambda: t1 > badarg)
  1386. self.assertRaises(TypeError, lambda: t1 >= badarg)
  1387. self.assertRaises(TypeError, lambda: badarg <= t1)
  1388. self.assertRaises(TypeError, lambda: badarg < t1)
  1389. self.assertRaises(TypeError, lambda: badarg > t1)
  1390. self.assertRaises(TypeError, lambda: badarg >= t1)
  1391. def test_mixed_compare(self):
  1392. our = self.theclass(2000, 4, 5)
  1393. # Our class can be compared for equality to other classes
  1394. self.assertEqual(our == 1, False)
  1395. self.assertEqual(1 == our, False)
  1396. self.assertEqual(our != 1, True)
  1397. self.assertEqual(1 != our, True)
  1398. # But the ordering is undefined
  1399. self.assertRaises(TypeError, lambda: our < 1)
  1400. self.assertRaises(TypeError, lambda: 1 < our)
  1401. # Repeat those tests with a different class
  1402. class SomeClass:
  1403. pass
  1404. their = SomeClass()
  1405. self.assertEqual(our == their, False)
  1406. self.assertEqual(their == our, False)
  1407. self.assertEqual(our != their, True)
  1408. self.assertEqual(their != our, True)
  1409. self.assertRaises(TypeError, lambda: our < their)
  1410. self.assertRaises(TypeError, lambda: their < our)
  1411. def test_bool(self):
  1412. # All dates are considered true.
  1413. self.assertTrue(self.theclass.min)
  1414. self.assertTrue(self.theclass.max)
  1415. def test_strftime_y2k(self):
  1416. for y in (1, 49, 70, 99, 100, 999, 1000, 1970):
  1417. d = self.theclass(y, 1, 1)
  1418. # Issue 13305: For years < 1000, the value is not always
  1419. # padded to 4 digits across platforms. The C standard
  1420. # assumes year >= 1900, so it does not specify the number
  1421. # of digits.
  1422. if d.strftime("%Y") != '%04d' % y:
  1423. # Year 42 returns '42', not padded
  1424. self.assertEqual(d.strftime("%Y"), '%d' % y)
  1425. # '0042' is obtained anyway
  1426. if support.has_strftime_extensions:
  1427. self.assertEqual(d.strftime("%4Y"), '%04d' % y)
  1428. def test_replace(self):
  1429. cls = self.theclass
  1430. args = [1, 2, 3]
  1431. base = cls(*args)
  1432. self.assertEqual(base, base.replace())
  1433. i = 0
  1434. for name, newval in (("year", 2),
  1435. ("month", 3),
  1436. ("day", 4)):
  1437. newargs = args[:]
  1438. newargs[i] = newval
  1439. expected = cls(*newargs)
  1440. got = base.replace(**{name: newval})
  1441. self.assertEqual(expected, got)
  1442. i += 1
  1443. # Out of bounds.
  1444. base = cls(2000, 2, 29)
  1445. self.assertRaises(ValueError, base.replace, year=2001)
  1446. def test_subclass_replace(self):
  1447. class DateSubclass(self.theclass):
  1448. pass
  1449. dt = DateSubclass(2012, 1, 1)
  1450. self.assertIs(type(dt.replace(year=2013)), DateSubclass)
  1451. def test_subclass_date(self):
  1452. class C(self.theclass):
  1453. theAnswer = 42
  1454. def __new__(cls, *args, **kws):
  1455. temp = kws.copy()
  1456. extra = temp.pop('extra')
  1457. result = self.theclass.__new__(cls, *args, **temp)
  1458. result.extra = extra
  1459. return result
  1460. def newmeth(self, start):
  1461. return start + self.year + self.month
  1462. args = 2003, 4, 14
  1463. dt1 = self.theclass(*args)
  1464. dt2 = C(*args, **{'extra': 7})
  1465. self.assertEqual(dt2.__class__, C)
  1466. self.assertEqual(dt2.theAnswer, 42)
  1467. self.assertEqual(dt2.extra, 7)
  1468. self.assertEqual(dt1.toordinal(), dt2.toordinal())
  1469. self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month - 7)
  1470. def test_subclass_alternate_constructors(self):
  1471. # Test that alternate constructors call the constructor
  1472. class DateSubclass(self.theclass):
  1473. def __new__(cls, *args, **kwargs):
  1474. result = self.theclass.__new__(cls, *args, **kwargs)
  1475. result.extra = 7
  1476. return result
  1477. args = (2003, 4, 14)
  1478. d_ord = 731319 # Equivalent ordinal date
  1479. d_isoformat = '2003-04-14' # Equivalent isoformat()
  1480. base_d = DateSubclass(*args)
  1481. self.assertIsInstance(base_d, DateSubclass)
  1482. self.assertEqual(base_d.extra, 7)
  1483. # Timestamp depends on time zone, so we'll calculate the equivalent here
  1484. ts = datetime.combine(base_d, time(0)).timestamp()
  1485. test_cases = [
  1486. ('fromordinal', (d_ord,)),
  1487. ('fromtimestamp', (ts,)),
  1488. ('fromisoformat', (d_isoformat,)),
  1489. ]
  1490. for constr_name, constr_args in test_cases:
  1491. for base_obj in (DateSubclass, base_d):
  1492. # Test both the classmethod and method
  1493. with self.subTest(base_obj_type=type(base_obj),
  1494. constr_name=constr_name):
  1495. constr = getattr(base_obj, constr_name)
  1496. dt = constr(*constr_args)
  1497. # Test that it creates the right subclass
  1498. self.assertIsInstance(dt, DateSubclass)
  1499. # Test that it's equal to the base object
  1500. self.assertEqual(dt, base_d)
  1501. # Test that it called the constructor
  1502. self.assertEqual(dt.extra, 7)
  1503. def test_pickling_subclass_date(self):
  1504. args = 6, 7, 23
  1505. orig = SubclassDate(*args)
  1506. for pickler, unpickler, proto in pickle_choices:
  1507. green = pickler.dumps(orig, proto)
  1508. derived = unpickler.loads(green)
  1509. self.assertEqual(orig, derived)
  1510. self.assertTrue(isinstance(derived, SubclassDate))
  1511. def test_backdoor_resistance(self):
  1512. # For fast unpickling, the constructor accepts a pickle byte string.
  1513. # This is a low-overhead backdoor. A user can (by intent or
  1514. # mistake) pass a string directly, which (if it's the right length)
  1515. # will get treated like a pickle, and bypass the normal sanity
  1516. # checks in the constructor. This can create insane objects.
  1517. # The constructor doesn't want to burn the time to validate all
  1518. # fields, but does check the month field. This stops, e.g.,
  1519. # datetime.datetime('1995-03-25') from yielding an insane object.
  1520. base = b'1995-03-25'
  1521. if not issubclass(self.theclass, datetime):
  1522. base = base[:4]
  1523. for month_byte in b'9', b'\0', b'\r', b'\xff':
  1524. self.assertRaises(TypeError, self.theclass,
  1525. base[:2] + month_byte + base[3:])
  1526. if issubclass(self.theclass, datetime):
  1527. # Good bytes, but bad tzinfo:
  1528. with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'):
  1529. self.theclass(bytes([1] * len(base)), 'EST')
  1530. for ord_byte in range(1, 13):
  1531. # This shouldn't blow up because of the month byte alone. If
  1532. # the implementation changes to do more-careful checking, it may
  1533. # blow up because other fields are insane.
  1534. self.theclass(base[:2] + bytes([ord_byte]) + base[3:])
  1535. def test_fromisoformat(self):
  1536. # Test that isoformat() is reversible
  1537. base_dates = [
  1538. (1, 1, 1),
  1539. (1000, 2, 14),
  1540. (1900, 1, 1),
  1541. (2000, 2, 29),
  1542. (2004, 11, 12),
  1543. (2004, 4, 3),
  1544. (2017, 5, 30)
  1545. ]
  1546. for dt_tuple in base_dates:
  1547. dt = self.theclass(*dt_tuple)
  1548. dt_str = dt.isoformat()
  1549. with self.subTest(dt_str=dt_str):
  1550. dt_rt = self.theclass.fromisoformat(dt.isoformat())
  1551. self.assertEqual(dt, dt_rt)
  1552. def test_fromisoformat_date_examples(self):
  1553. examples = [
  1554. ('00010101', self.theclass(1, 1, 1)),
  1555. ('20000101', self.theclass(2000, 1, 1)),
  1556. ('20250102', self.theclass(2025, 1, 2)),
  1557. ('99991231', self.theclass(9999, 12, 31)),
  1558. ('0001-01-01', self.theclass(1, 1, 1)),
  1559. ('2000-01-01', self.theclass(2000, 1, 1)),
  1560. ('2025-01-02', self.theclass(2025, 1, 2)),
  1561. ('9999-12-31', self.theclass(9999, 12, 31)),
  1562. ('2025W01', self.theclass(2024, 12, 30)),
  1563. ('2025-W01', self.theclass(2024, 12, 30)),
  1564. ('2025W014', self.theclass(2025, 1, 2)),
  1565. ('2025-W01-4', self.theclass(2025, 1, 2)),
  1566. ('2026W01', self.theclass(2025, 12, 29)),
  1567. ('2026-W01', self.theclass(2025, 12, 29)),
  1568. ('2026W013', self.theclass(2025, 12, 31)),
  1569. ('2026-W01-3', self.theclass(2025, 12, 31)),
  1570. ('2022W52', self.theclass(2022, 12, 26)),
  1571. ('2022-W52', self.theclass(2022, 12, 26)),
  1572. ('2022W527', self.theclass(2023, 1, 1)),
  1573. ('2022-W52-7', self.theclass(2023, 1, 1)),
  1574. ('2015W534', self.theclass(2015, 12, 31)), # Has week 53
  1575. ('2015-W53-4', self.theclass(2015, 12, 31)), # Has week 53
  1576. ('2015-W53-5', self.theclass(2016, 1, 1)),
  1577. ('2020W531', self.theclass(2020, 12, 28)), # Leap year
  1578. ('2020-W53-1', self.theclass(2020, 12, 28)), # Leap year
  1579. ('2020-W53-6', self.theclass(2021, 1, 2)),
  1580. ]
  1581. for input_str, expected in examples:
  1582. with self.subTest(input_str=input_str):
  1583. actual = self.theclass.fromisoformat(input_str)
  1584. self.assertEqual(actual, expected)
  1585. def test_fromisoformat_subclass(self):
  1586. class DateSubclass(self.theclass):
  1587. pass
  1588. dt = DateSubclass(2014, 12, 14)
  1589. dt_rt = DateSubclass.fromisoformat(dt.isoformat())
  1590. self.assertIsInstance(dt_rt, DateSubclass)
  1591. def test_fromisoformat_fails(self):
  1592. # Test that fromisoformat() fails on invalid values
  1593. bad_strs = [
  1594. '', # Empty string
  1595. '\ud800', # bpo-34454: Surrogate code point
  1596. '009-03-04', # Not 10 characters
  1597. '123456789', # Not a date
  1598. '200a-12-04', # Invalid character in year
  1599. '2009-1a-04', # Invalid character in month
  1600. '2009-12-0a', # Invalid character in day
  1601. '2009-01-32', # Invalid day
  1602. '2009-02-29', # Invalid leap day
  1603. '2019-W53-1', # No week 53 in 2019
  1604. '2020-W54-1', # No week 54
  1605. '2009\ud80002\ud80028', # Separators are surrogate codepoints
  1606. ]
  1607. for bad_str in bad_strs:
  1608. with self.assertRaises(ValueError):
  1609. self.theclass.fromisoformat(bad_str)
  1610. def test_fromisoformat_fails_typeerror(self):
  1611. # Test that fromisoformat fails when passed the wrong type
  1612. bad_types = [b'2009-03-01', None, io.StringIO('2009-03-01')]
  1613. for bad_type in bad_types:
  1614. with self.assertRaises(TypeError):
  1615. self.theclass.fromisoformat(bad_type)
  1616. def test_fromisocalendar(self):
  1617. # For each test case, assert that fromisocalendar is the
  1618. # inverse of the isocalendar function
  1619. dates = [
  1620. (2016, 4, 3),
  1621. (2005, 1, 2), # (2004, 53, 7)
  1622. (2008, 12, 30), # (2009, 1, 2)
  1623. (2010, 1, 2), # (2009, 53, 6)
  1624. (2009, 12, 31), # (2009, 53, 4)
  1625. (1900, 1, 1), # Unusual non-leap year (year % 100 == 0)
  1626. (1900, 12, 31),
  1627. (2000, 1, 1), # Unusual leap year (year % 400 == 0)
  1628. (2000, 12, 31),
  1629. (2004, 1, 1), # Leap year
  1630. (2004, 12, 31),
  1631. (1, 1, 1),
  1632. (9999, 12, 31),
  1633. (MINYEAR, 1, 1),
  1634. (MAXYEAR, 12, 31),
  1635. ]
  1636. for datecomps in dates:
  1637. with self.subTest(datecomps=datecomps):
  1638. dobj = self.theclass(*datecomps)
  1639. isocal = dobj.isocalendar()
  1640. d_roundtrip = self.theclass.fromisocalendar(*isocal)
  1641. self.assertEqual(dobj, d_roundtrip)
  1642. def test_fromisocalendar_value_errors(self):
  1643. isocals = [
  1644. (2019, 0, 1),
  1645. (2019, -1, 1),
  1646. (2019, 54, 1),
  1647. (2019, 1, 0),
  1648. (2019, 1, -1),
  1649. (2019, 1, 8),
  1650. (2019, 53, 1),
  1651. (10000, 1, 1),
  1652. (0, 1, 1),
  1653. (9999999, 1, 1),
  1654. (2<<32, 1, 1),
  1655. (2019, 2<<32, 1),
  1656. (2019, 1, 2<<32),
  1657. ]
  1658. for isocal in isocals:
  1659. with self.subTest(isocal=isocal):
  1660. with self.assertRaises(ValueError):
  1661. self.theclass.fromisocalendar(*isocal)
  1662. def test_fromisocalendar_type_errors(self):
  1663. err_txformers = [
  1664. str,
  1665. float,
  1666. lambda x: None,
  1667. ]
  1668. # Take a valid base tuple and transform it to contain one argument
  1669. # with the wrong type. Repeat this for each argument, e.g.
  1670. # [("2019", 1, 1), (2019, "1", 1), (2019, 1, "1"), ...]
  1671. isocals = []
  1672. base = (2019, 1, 1)
  1673. for i in range(3):
  1674. for txformer in err_txformers:
  1675. err_val = list(base)
  1676. err_val[i] = txformer(err_val[i])
  1677. isocals.append(tuple(err_val))
  1678. for isocal in isocals:
  1679. with self.subTest(isocal=isocal):
  1680. with self.assertRaises(TypeError):
  1681. self.theclass.fromisocalendar(*isocal)
  1682. #############################################################################
  1683. # datetime tests
  1684. class SubclassDatetime(datetime):
  1685. sub_var = 1
  1686. class TestDateTime(TestDate):
  1687. theclass = datetime
  1688. def test_basic_attributes(self):
  1689. dt = self.theclass(2002, 3, 1, 12, 0)
  1690. self.assertEqual(dt.year, 2002)
  1691. self.assertEqual(dt.month, 3)
  1692. self.assertEqual(dt.day, 1)
  1693. self.assertEqual(dt.hour, 12)
  1694. self.assertEqual(dt.minute, 0)
  1695. self.assertEqual(dt.second, 0)
  1696. self.assertEqual(dt.microsecond, 0)
  1697. def test_basic_attributes_nonzero(self):
  1698. # Make sure all attributes are non-zero so bugs in
  1699. # bit-shifting access show up.
  1700. dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)
  1701. self.assertEqual(dt.year, 2002)
  1702. self.assertEqual(dt.month, 3)
  1703. self.assertEqual(dt.day, 1)
  1704. self.assertEqual(dt.hour, 12)
  1705. self.assertEqual(dt.minute, 59)
  1706. self.assertEqual(dt.second, 59)
  1707. self.assertEqual(dt.microsecond, 8000)
  1708. def test_roundtrip(self):
  1709. for dt in (self.theclass(1, 2, 3, 4, 5, 6, 7),
  1710. self.theclass.now()):
  1711. # Verify dt -> string -> datetime identity.
  1712. s = repr(dt)
  1713. self.assertTrue(s.startswith('datetime.'))
  1714. s = s[9:]
  1715. dt2 = eval(s)
  1716. self.assertEqual(dt, dt2)
  1717. # Verify identity via reconstructing from pieces.
  1718. dt2 = self.theclass(dt.year, dt.month, dt.day,
  1719. dt.hour, dt.minute, dt.second,
  1720. dt.microsecond)
  1721. self.assertEqual(dt, dt2)
  1722. def test_isoformat(self):
  1723. t = self.theclass(1, 2, 3, 4, 5, 1, 123)
  1724. self.assertEqual(t.isoformat(), "0001-02-03T04:05:01.000123")
  1725. self.assertEqual(t.isoformat('T'), "0001-02-03T04:05:01.000123")
  1726. self.assertEqual(t.isoformat(' '), "0001-02-03 04:05:01.000123")
  1727. self.assertEqual(t.isoformat('\x00'), "0001-02-03\x0004:05:01.000123")
  1728. # bpo-34482: Check that surrogates are handled properly.
  1729. self.assertEqual(t.isoformat('\ud800'),
  1730. "0001-02-03\ud80004:05:01.000123")
  1731. self.assertEqual(t.isoformat(timespec='hours'), "0001-02-03T04")
  1732. self.assertEqual(t.isoformat(timespec='minutes'), "0001-02-03T04:05")
  1733. self.assertEqual(t.isoformat(timespec='seconds'), "0001-02-03T04:05:01")
  1734. self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.000")
  1735. self.assertEqual(t.isoformat(timespec='microseconds'), "0001-02-03T04:05:01.000123")
  1736. self.assertEqual(t.isoformat(timespec='auto'), "0001-02-03T04:05:01.000123")
  1737. self.assertEqual(t.isoformat(sep=' ', timespec='minutes'), "0001-02-03 04:05")
  1738. self.assertRaises(ValueError, t.isoformat, timespec='foo')
  1739. # bpo-34482: Check that surrogates are handled properly.
  1740. self.assertRaises(ValueError, t.isoformat, timespec='\ud800')
  1741. # str is ISO format with the separator forced to a blank.
  1742. self.assertEqual(str(t), "0001-02-03 04:05:01.000123")
  1743. t = self.theclass(1, 2, 3, 4, 5, 1, 999500, tzinfo=timezone.utc)
  1744. self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.999+00:00")
  1745. t = self.theclass(1, 2, 3, 4, 5, 1, 999500)
  1746. self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.999")
  1747. t = self.theclass(1, 2, 3, 4, 5, 1)
  1748. self.assertEqual(t.isoformat(timespec='auto'), "0001-02-03T04:05:01")
  1749. self.assertEqual(t.isoformat(timespec='milliseconds'), "0001-02-03T04:05:01.000")
  1750. self.assertEqual(t.isoformat(timespec='microseconds'), "0001-02-03T04:05:01.000000")
  1751. t = self.theclass(2, 3, 2)
  1752. self.assertEqual(t.isoformat(), "0002-03-02T00:00:00")
  1753. self.assertEqual(t.isoformat('T'), "0002-03-02T00:00:00")
  1754. self.assertEqual(t.isoformat(' '), "0002-03-02 00:00:00")
  1755. # str is ISO format with the separator forced to a blank.
  1756. self.assertEqual(str(t), "0002-03-02 00:00:00")
  1757. # ISO format with timezone
  1758. tz = FixedOffset(timedelta(seconds=16), 'XXX')
  1759. t = self.theclass(2, 3, 2, tzinfo=tz)
  1760. self.assertEqual(t.isoformat(), "0002-03-02T00:00:00+00:00:16")
  1761. def test_isoformat_timezone(self):
  1762. tzoffsets = [
  1763. ('05:00', timedelta(hours=5)),
  1764. ('02:00', timedelta(hours=2)),
  1765. ('06:27', timedelta(hours=6, minutes=27)),
  1766. ('12:32:30', timedelta(hours=12, minutes=32, seconds=30)),
  1767. ('02:04:09.123456', timedelta(hours=2, minutes=4, seconds=9, microseconds=123456))
  1768. ]
  1769. tzinfos = [
  1770. ('', None),
  1771. ('+00:00', timezone.utc),
  1772. ('+00:00', timezone(timedelta(0))),
  1773. ]
  1774. tzinfos += [
  1775. (prefix + expected, timezone(sign * td))
  1776. for expected, td in tzoffsets
  1777. for prefix, sign in [('-', -1), ('+', 1)]
  1778. ]
  1779. dt_base = self.theclass(2016, 4, 1, 12, 37, 9)
  1780. exp_base = '2016-04-01T12:37:09'
  1781. for exp_tz, tzi in tzinfos:
  1782. dt = dt_base.replace(tzinfo=tzi)
  1783. exp = exp_base + exp_tz
  1784. with self.subTest(tzi=tzi):
  1785. assert dt.isoformat() == exp
  1786. def test_format(self):
  1787. dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
  1788. self.assertEqual(dt.__format__(''), str(dt))
  1789. with self.assertRaisesRegex(TypeError, 'must be str, not int'):
  1790. dt.__format__(123)
  1791. # check that a derived class's __str__() gets called
  1792. class A(self.theclass):
  1793. def __str__(self):
  1794. return 'A'
  1795. a = A(2007, 9, 10, 4, 5, 1, 123)
  1796. self.assertEqual(a.__format__(''), 'A')
  1797. # check that a derived class's strftime gets called
  1798. class B(self.theclass):
  1799. def strftime(self, format_spec):
  1800. return 'B'
  1801. b = B(2007, 9, 10, 4, 5, 1, 123)
  1802. self.assertEqual(b.__format__(''), str(dt))
  1803. for fmt in ["m:%m d:%d y:%y",
  1804. "m:%m d:%d y:%y H:%H M:%M S:%S",
  1805. "%z %Z",
  1806. ]:
  1807. self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
  1808. self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
  1809. self.assertEqual(b.__format__(fmt), 'B')
  1810. def test_more_ctime(self):
  1811. # Test fields that TestDate doesn't touch.
  1812. import time
  1813. t = self.theclass(2002, 3, 2, 18, 3, 5, 123)
  1814. self.assertEqual(t.ctime(), "Sat Mar 2 18:03:05 2002")
  1815. # Oops! The next line fails on Win2K under MSVC 6, so it's commented
  1816. # out. The difference is that t.ctime() produces " 2" for the day,
  1817. # but platform ctime() produces "02" for the day. According to
  1818. # C99, t.ctime() is correct here.
  1819. # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
  1820. # So test a case where that difference doesn't matter.
  1821. t = self.theclass(2002, 3, 22, 18, 3, 5, 123)
  1822. self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))
  1823. def test_tz_independent_comparing(self):
  1824. dt1 = self.theclass(2002, 3, 1, 9, 0, 0)
  1825. dt2 = self.theclass(2002, 3, 1, 10, 0, 0)
  1826. dt3 = self.theclass(2002, 3, 1, 9, 0, 0)
  1827. self.assertEqual(dt1, dt3)
  1828. self.assertTrue(dt2 > dt3)
  1829. # Make sure comparison doesn't forget microseconds, and isn't done
  1830. # via comparing a float timestamp (an IEEE double doesn't have enough
  1831. # precision to span microsecond resolution across years 1 through 9999,
  1832. # so comparing via timestamp necessarily calls some distinct values
  1833. # equal).
  1834. dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)
  1835. us = timedelta(microseconds=1)
  1836. dt2 = dt1 + us
  1837. self.assertEqual(dt2 - dt1, us)
  1838. self.assertTrue(dt1 < dt2)
  1839. def test_strftime_with_bad_tzname_replace(self):
  1840. # verify ok if tzinfo.tzname().replace() returns a non-string
  1841. class MyTzInfo(FixedOffset):
  1842. def tzname(self, dt):
  1843. class MyStr(str):
  1844. def replace(self, *args):
  1845. return None
  1846. return MyStr('name')
  1847. t = self.theclass(2005, 3, 2, 0, 0, 0, 0, MyTzInfo(3, 'name'))
  1848. self.assertRaises(TypeError, t.strftime, '%Z')
  1849. def test_bad_constructor_arguments(self):
  1850. # bad years
  1851. self.theclass(MINYEAR, 1, 1) # no exception
  1852. self.theclass(MAXYEAR, 1, 1) # no exception
  1853. self.assertRaises(ValueError, self.theclass, MINYEAR-1, 1, 1)
  1854. self.assertRaises(ValueError, self.theclass, MAXYEAR+1, 1, 1)
  1855. # bad months
  1856. self.theclass(2000, 1, 1) # no exception
  1857. self.theclass(2000, 12, 1) # no exception
  1858. self.assertRaises(ValueError, self.theclass, 2000, 0, 1)
  1859. self.assertRaises(ValueError, self.theclass, 2000, 13, 1)
  1860. # bad days
  1861. self.theclass(2000, 2, 29) # no exception
  1862. self.theclass(2004, 2, 29) # no exception
  1863. self.theclass(2400, 2, 29) # no exception
  1864. self.assertRaises(ValueError, self.theclass, 2000, 2, 30)
  1865. self.assertRaises(ValueError, self.theclass, 2001, 2, 29)
  1866. self.assertRaises(ValueError, self.theclass, 2100, 2, 29)
  1867. self.assertRaises(ValueError, self.theclass, 1900, 2, 29)
  1868. self.assertRaises(ValueError, self.theclass, 2000, 1, 0)
  1869. self.assertRaises(ValueError, self.theclass, 2000, 1, 32)
  1870. # bad hours
  1871. self.theclass(2000, 1, 31, 0) # no exception
  1872. self.theclass(2000, 1, 31, 23) # no exception
  1873. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, -1)
  1874. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 24)
  1875. # bad minutes
  1876. self.theclass(2000, 1, 31, 23, 0) # no exception
  1877. self.theclass(2000, 1, 31, 23, 59) # no exception
  1878. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, -1)
  1879. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 60)
  1880. # bad seconds
  1881. self.theclass(2000, 1, 31, 23, 59, 0) # no exception
  1882. self.theclass(2000, 1, 31, 23, 59, 59) # no exception
  1883. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, -1)
  1884. self.assertRaises(ValueError, self.theclass, 2000, 1, 31, 23, 59, 60)
  1885. # bad microseconds
  1886. self.theclass(2000, 1, 31, 23, 59, 59, 0) # no exception
  1887. self.theclass(2000, 1, 31, 23, 59, 59, 999999) # no exception
  1888. self.assertRaises(ValueError, self.theclass,
  1889. 2000, 1, 31, 23, 59, 59, -1)
  1890. self.assertRaises(ValueError, self.theclass,
  1891. 2000, 1, 31, 23, 59, 59,
  1892. 1000000)
  1893. # bad fold
  1894. self.assertRaises(ValueError, self.theclass,
  1895. 2000, 1, 31, fold=-1)
  1896. self.assertRaises(ValueError, self.theclass,
  1897. 2000, 1, 31, fold=2)
  1898. # Positional fold:
  1899. self.assertRaises(TypeError, self.theclass,
  1900. 2000, 1, 31, 23, 59, 59, 0, None, 1)
  1901. def test_hash_equality(self):
  1902. d = self.theclass(2000, 12, 31, 23, 30, 17)
  1903. e = self.theclass(2000, 12, 31, 23, 30, 17)
  1904. self.assertEqual(d, e)
  1905. self.assertEqual(hash(d), hash(e))
  1906. dic = {d: 1}
  1907. dic[e] = 2
  1908. self.assertEqual(len(dic), 1)
  1909. self.assertEqual(dic[d], 2)
  1910. self.assertEqual(dic[e], 2)
  1911. d = self.theclass(2001, 1, 1, 0, 5, 17)
  1912. e = self.theclass(2001, 1, 1, 0, 5, 17)
  1913. self.assertEqual(d, e)
  1914. self.assertEqual(hash(d), hash(e))
  1915. dic = {d: 1}
  1916. dic[e] = 2
  1917. self.assertEqual(len(dic), 1)
  1918. self.assertEqual(dic[d], 2)
  1919. self.assertEqual(dic[e], 2)
  1920. def test_computations(self):
  1921. a = self.theclass(2002, 1, 31)
  1922. b = self.theclass(1956, 1, 31)
  1923. diff = a-b
  1924. self.assertEqual(diff.days, 46*365 + len(range(1956, 2002, 4)))
  1925. self.assertEqual(diff.seconds, 0)
  1926. self.assertEqual(diff.microseconds, 0)
  1927. a = self.theclass(2002, 3, 2, 17, 6)
  1928. millisec = timedelta(0, 0, 1000)
  1929. hour = timedelta(0, 3600)
  1930. day = timedelta(1)
  1931. week = timedelta(7)
  1932. self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))
  1933. self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))
  1934. self.assertEqual(a + 10*hour, self.theclass(2002, 3, 3, 3, 6))
  1935. self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))
  1936. self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))
  1937. self.assertEqual(a - hour, a + -hour)
  1938. self.assertEqual(a - 20*hour, self.theclass(2002, 3, 1, 21, 6))
  1939. self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))
  1940. self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))
  1941. self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))
  1942. self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))
  1943. self.assertEqual(a + 52*week, self.theclass(2003, 3, 1, 17, 6))
  1944. self.assertEqual(a - 52*week, self.theclass(2001, 3, 3, 17, 6))
  1945. self.assertEqual((a + week) - a, week)
  1946. self.assertEqual((a + day) - a, day)
  1947. self.assertEqual((a + hour) - a, hour)
  1948. self.assertEqual((a + millisec) - a, millisec)
  1949. self.assertEqual((a - week) - a, -week)
  1950. self.assertEqual((a - day) - a, -day)
  1951. self.assertEqual((a - hour) - a, -hour)
  1952. self.assertEqual((a - millisec) - a, -millisec)
  1953. self.assertEqual(a - (a + week), -week)
  1954. self.assertEqual(a - (a + day), -day)
  1955. self.assertEqual(a - (a + hour), -hour)
  1956. self.assertEqual(a - (a + millisec), -millisec)
  1957. self.assertEqual(a - (a - week), week)
  1958. self.assertEqual(a - (a - day), day)
  1959. self.assertEqual(a - (a - hour), hour)
  1960. self.assertEqual(a - (a - millisec), millisec)
  1961. self.assertEqual(a + (week + day + hour + millisec),
  1962. self.theclass(2002, 3, 10, 18, 6, 0, 1000))
  1963. self.assertEqual(a + (week + day + hour + millisec),
  1964. (((a + week) + day) + hour) + millisec)
  1965. self.assertEqual(a - (week + day + hour + millisec),
  1966. self.theclass(2002, 2, 22, 16, 5, 59, 999000))
  1967. self.assertEqual(a - (week + day + hour + millisec),
  1968. (((a - week) - day) - hour) - millisec)
  1969. # Add/sub ints or floats should be illegal
  1970. for i in 1, 1.0:
  1971. self.assertRaises(TypeError, lambda: a+i)
  1972. self.assertRaises(TypeError, lambda: a-i)
  1973. self.assertRaises(TypeError, lambda: i+a)
  1974. self.assertRaises(TypeError, lambda: i-a)
  1975. # delta - datetime is senseless.
  1976. self.assertRaises(TypeError, lambda: day - a)
  1977. # mixing datetime and (delta or datetime) via * or // is senseless
  1978. self.assertRaises(TypeError, lambda: day * a)
  1979. self.assertRaises(TypeError, lambda: a * day)
  1980. self.assertRaises(TypeError, lambda: day // a)
  1981. self.assertRaises(TypeError, lambda: a // day)
  1982. self.assertRaises(TypeError, lambda: a * a)
  1983. self.assertRaises(TypeError, lambda: a // a)
  1984. # datetime + datetime is senseless
  1985. self.assertRaises(TypeError, lambda: a + a)
  1986. def test_pickling(self):
  1987. args = 6, 7, 23, 20, 59, 1, 64**2
  1988. orig = self.theclass(*args)
  1989. for pickler, unpickler, proto in pickle_choices:
  1990. green = pickler.dumps(orig, proto)
  1991. derived = unpickler.loads(green)
  1992. self.assertEqual(orig, derived)
  1993. self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
  1994. def test_more_pickling(self):
  1995. a = self.theclass(2003, 2, 7, 16, 48, 37, 444116)
  1996. for proto in range(pickle.HIGHEST_PROTOCOL + 1):
  1997. s = pickle.dumps(a, proto)
  1998. b = pickle.loads(s)
  1999. self.assertEqual(b.year, 2003)
  2000. self.assertEqual(b.month, 2)
  2001. self.assertEqual(b.day, 7)
  2002. def test_pickling_subclass_datetime(self):
  2003. args = 6, 7, 23, 20, 59, 1, 64**2
  2004. orig = SubclassDatetime(*args)
  2005. for pickler, unpickler, proto in pickle_choices:
  2006. green = pickler.dumps(orig, proto)
  2007. derived = unpickler.loads(green)
  2008. self.assertEqual(orig, derived)
  2009. self.assertTrue(isinstance(derived, SubclassDatetime))
  2010. def test_compat_unpickle(self):
  2011. tests = [
  2012. b'cdatetime\ndatetime\n('
  2013. b"S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x00\\x10\\x00'\ntR.",
  2014. b'cdatetime\ndatetime\n('
  2015. b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00tR.',
  2016. b'\x80\x02cdatetime\ndatetime\n'
  2017. b'U\n\x07\xdf\x0b\x1b\x14;\x01\x00\x10\x00\x85R.',
  2018. ]
  2019. args = 2015, 11, 27, 20, 59, 1, 64**2
  2020. expected = self.theclass(*args)
  2021. for data in tests:
  2022. for loads in pickle_loads:
  2023. derived = loads(data, encoding='latin1')
  2024. self.assertEqual(derived, expected)
  2025. def test_more_compare(self):
  2026. # The test_compare() inherited from TestDate covers the error cases.
  2027. # We just want to test lexicographic ordering on the members datetime
  2028. # has that date lacks.
  2029. args = [2000, 11, 29, 20, 58, 16, 999998]
  2030. t1 = self.theclass(*args)
  2031. t2 = self.theclass(*args)
  2032. self.assertEqual(t1, t2)
  2033. self.assertTrue(t1 <= t2)
  2034. self.assertTrue(t1 >= t2)
  2035. self.assertFalse(t1 != t2)
  2036. self.assertFalse(t1 < t2)
  2037. self.assertFalse(t1 > t2)
  2038. for i in range(len(args)):
  2039. newargs = args[:]
  2040. newargs[i] = args[i] + 1
  2041. t2 = self.theclass(*newargs) # this is larger than t1
  2042. self.assertTrue(t1 < t2)
  2043. self.assertTrue(t2 > t1)
  2044. self.assertTrue(t1 <= t2)
  2045. self.assertTrue(t2 >= t1)
  2046. self.assertTrue(t1 != t2)
  2047. self.assertTrue(t2 != t1)
  2048. self.assertFalse(t1 == t2)
  2049. self.assertFalse(t2 == t1)
  2050. self.assertFalse(t1 > t2)
  2051. self.assertFalse(t2 < t1)
  2052. self.assertFalse(t1 >= t2)
  2053. self.assertFalse(t2 <= t1)
  2054. # A helper for timestamp constructor tests.
  2055. def verify_field_equality(self, expected, got):
  2056. self.assertEqual(expected.tm_year, got.year)
  2057. self.assertEqual(expected.tm_mon, got.month)
  2058. self.assertEqual(expected.tm_mday, got.day)
  2059. self.assertEqual(expected.tm_hour, got.hour)
  2060. self.assertEqual(expected.tm_min, got.minute)
  2061. self.assertEqual(expected.tm_sec, got.second)
  2062. def test_fromtimestamp(self):
  2063. import time
  2064. ts = time.time()
  2065. expected = time.localtime(ts)
  2066. got = self.theclass.fromtimestamp(ts)
  2067. self.verify_field_equality(expected, got)
  2068. def test_utcfromtimestamp(self):
  2069. import time
  2070. ts = time.time()
  2071. expected = time.gmtime(ts)
  2072. got = self.theclass.utcfromtimestamp(ts)
  2073. self.verify_field_equality(expected, got)
  2074. # Run with US-style DST rules: DST begins 2 a.m. on second Sunday in
  2075. # March (M3.2.0) and ends 2 a.m. on first Sunday in November (M11.1.0).
  2076. @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
  2077. def test_timestamp_naive(self):
  2078. t = self.theclass(1970, 1, 1)
  2079. self.assertEqual(t.timestamp(), 18000.0)
  2080. t = self.theclass(1970, 1, 1, 1, 2, 3, 4)
  2081. self.assertEqual(t.timestamp(),
  2082. 18000.0 + 3600 + 2*60 + 3 + 4*1e-6)
  2083. # Missing hour
  2084. t0 = self.theclass(2012, 3, 11, 2, 30)
  2085. t1 = t0.replace(fold=1)
  2086. self.assertEqual(self.theclass.fromtimestamp(t1.timestamp()),
  2087. t0 - timedelta(hours=1))
  2088. self.assertEqual(self.theclass.fromtimestamp(t0.timestamp()),
  2089. t1 + timedelta(hours=1))
  2090. # Ambiguous hour defaults to DST
  2091. t = self.theclass(2012, 11, 4, 1, 30)
  2092. self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t)
  2093. # Timestamp may raise an overflow error on some platforms
  2094. # XXX: Do we care to support the first and last year?
  2095. for t in [self.theclass(2,1,1), self.theclass(9998,12,12)]:
  2096. try:
  2097. s = t.timestamp()
  2098. except OverflowError:
  2099. pass
  2100. else:
  2101. self.assertEqual(self.theclass.fromtimestamp(s), t)
  2102. def test_timestamp_aware(self):
  2103. t = self.theclass(1970, 1, 1, tzinfo=timezone.utc)
  2104. self.assertEqual(t.timestamp(), 0.0)
  2105. t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc)
  2106. self.assertEqual(t.timestamp(),
  2107. 3600 + 2*60 + 3 + 4*1e-6)
  2108. t = self.theclass(1970, 1, 1, 1, 2, 3, 4,
  2109. tzinfo=timezone(timedelta(hours=-5), 'EST'))
  2110. self.assertEqual(t.timestamp(),
  2111. 18000 + 3600 + 2*60 + 3 + 4*1e-6)
  2112. @support.run_with_tz('MSK-03') # Something east of Greenwich
  2113. def test_microsecond_rounding(self):
  2114. for fts in [self.theclass.fromtimestamp,
  2115. self.theclass.utcfromtimestamp]:
  2116. zero = fts(0)
  2117. self.assertEqual(zero.second, 0)
  2118. self.assertEqual(zero.microsecond, 0)
  2119. one = fts(1e-6)
  2120. try:
  2121. minus_one = fts(-1e-6)
  2122. except OSError:
  2123. # localtime(-1) and gmtime(-1) is not supported on Windows
  2124. pass
  2125. else:
  2126. self.assertEqual(minus_one.second, 59)
  2127. self.assertEqual(minus_one.microsecond, 999999)
  2128. t = fts(-1e-8)
  2129. self.assertEqual(t, zero)
  2130. t = fts(-9e-7)
  2131. self.assertEqual(t, minus_one)
  2132. t = fts(-1e-7)
  2133. self.assertEqual(t, zero)
  2134. t = fts(-1/2**7)
  2135. self.assertEqual(t.second, 59)
  2136. self.assertEqual(t.microsecond, 992188)
  2137. t = fts(1e-7)
  2138. self.assertEqual(t, zero)
  2139. t = fts(9e-7)
  2140. self.assertEqual(t, one)
  2141. t = fts(0.99999949)
  2142. self.assertEqual(t.second, 0)
  2143. self.assertEqual(t.microsecond, 999999)
  2144. t = fts(0.9999999)
  2145. self.assertEqual(t.second, 1)
  2146. self.assertEqual(t.microsecond, 0)
  2147. t = fts(1/2**7)
  2148. self.assertEqual(t.second, 0)
  2149. self.assertEqual(t.microsecond, 7812)
  2150. def test_timestamp_limits(self):
  2151. with self.subTest("minimum UTC"):
  2152. min_dt = self.theclass.min.replace(tzinfo=timezone.utc)
  2153. min_ts = min_dt.timestamp()
  2154. # This test assumes that datetime.min == 0000-01-01T00:00:00.00
  2155. # If that assumption changes, this value can change as well
  2156. self.assertEqual(min_ts, -62135596800)
  2157. with self.subTest("maximum UTC"):
  2158. # Zero out microseconds to avoid rounding issues
  2159. max_dt = self.theclass.max.replace(tzinfo=timezone.utc,
  2160. microsecond=0)
  2161. max_ts = max_dt.timestamp()
  2162. # This test assumes that datetime.max == 9999-12-31T23:59:59.999999
  2163. # If that assumption changes, this value can change as well
  2164. self.assertEqual(max_ts, 253402300799.0)
  2165. def test_fromtimestamp_limits(self):
  2166. try:
  2167. self.theclass.fromtimestamp(-2**32 - 1)
  2168. except (OSError, OverflowError):
  2169. self.skipTest("Test not valid on this platform")
  2170. # XXX: Replace these with datetime.{min,max}.timestamp() when we solve
  2171. # the issue with gh-91012
  2172. min_dt = self.theclass.min + timedelta(days=1)
  2173. min_ts = min_dt.timestamp()
  2174. max_dt = self.theclass.max.replace(microsecond=0)
  2175. max_ts = ((self.theclass.max - timedelta(hours=23)).timestamp() +
  2176. timedelta(hours=22, minutes=59, seconds=59).total_seconds())
  2177. for (test_name, ts, expected) in [
  2178. ("minimum", min_ts, min_dt),
  2179. ("maximum", max_ts, max_dt),
  2180. ]:
  2181. with self.subTest(test_name, ts=ts, expected=expected):
  2182. actual = self.theclass.fromtimestamp(ts)
  2183. self.assertEqual(actual, expected)
  2184. # Test error conditions
  2185. test_cases = [
  2186. ("Too small by a little", min_ts - timedelta(days=1, hours=12).total_seconds()),
  2187. ("Too small by a lot", min_ts - timedelta(days=400).total_seconds()),
  2188. ("Too big by a little", max_ts + timedelta(days=1).total_seconds()),
  2189. ("Too big by a lot", max_ts + timedelta(days=400).total_seconds()),
  2190. ]
  2191. for test_name, ts in test_cases:
  2192. with self.subTest(test_name, ts=ts):
  2193. with self.assertRaises((ValueError, OverflowError)):
  2194. # converting a Python int to C time_t can raise a
  2195. # OverflowError, especially on 32-bit platforms.
  2196. self.theclass.fromtimestamp(ts)
  2197. def test_utcfromtimestamp_limits(self):
  2198. try:
  2199. self.theclass.utcfromtimestamp(-2**32 - 1)
  2200. except (OSError, OverflowError):
  2201. self.skipTest("Test not valid on this platform")
  2202. min_dt = self.theclass.min.replace(tzinfo=timezone.utc)
  2203. min_ts = min_dt.timestamp()
  2204. max_dt = self.theclass.max.replace(microsecond=0, tzinfo=timezone.utc)
  2205. max_ts = max_dt.timestamp()
  2206. for (test_name, ts, expected) in [
  2207. ("minimum", min_ts, min_dt.replace(tzinfo=None)),
  2208. ("maximum", max_ts, max_dt.replace(tzinfo=None)),
  2209. ]:
  2210. with self.subTest(test_name, ts=ts, expected=expected):
  2211. try:
  2212. actual = self.theclass.utcfromtimestamp(ts)
  2213. except (OSError, OverflowError) as exc:
  2214. self.skipTest(str(exc))
  2215. self.assertEqual(actual, expected)
  2216. # Test error conditions
  2217. test_cases = [
  2218. ("Too small by a little", min_ts - 1),
  2219. ("Too small by a lot", min_ts - timedelta(days=400).total_seconds()),
  2220. ("Too big by a little", max_ts + 1),
  2221. ("Too big by a lot", max_ts + timedelta(days=400).total_seconds()),
  2222. ]
  2223. for test_name, ts in test_cases:
  2224. with self.subTest(test_name, ts=ts):
  2225. with self.assertRaises((ValueError, OverflowError)):
  2226. # converting a Python int to C time_t can raise a
  2227. # OverflowError, especially on 32-bit platforms.
  2228. self.theclass.utcfromtimestamp(ts)
  2229. def test_insane_fromtimestamp(self):
  2230. # It's possible that some platform maps time_t to double,
  2231. # and that this test will fail there. This test should
  2232. # exempt such platforms (provided they return reasonable
  2233. # results!).
  2234. for insane in -1e200, 1e200:
  2235. self.assertRaises(OverflowError, self.theclass.fromtimestamp,
  2236. insane)
  2237. def test_insane_utcfromtimestamp(self):
  2238. # It's possible that some platform maps time_t to double,
  2239. # and that this test will fail there. This test should
  2240. # exempt such platforms (provided they return reasonable
  2241. # results!).
  2242. for insane in -1e200, 1e200:
  2243. self.assertRaises(OverflowError, self.theclass.utcfromtimestamp,
  2244. insane)
  2245. @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
  2246. def test_negative_float_fromtimestamp(self):
  2247. # The result is tz-dependent; at least test that this doesn't
  2248. # fail (like it did before bug 1646728 was fixed).
  2249. self.theclass.fromtimestamp(-1.05)
  2250. @unittest.skipIf(sys.platform == "win32", "Windows doesn't accept negative timestamps")
  2251. def test_negative_float_utcfromtimestamp(self):
  2252. d = self.theclass.utcfromtimestamp(-1.05)
  2253. self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))
  2254. def test_utcnow(self):
  2255. import time
  2256. # Call it a success if utcnow() and utcfromtimestamp() are within
  2257. # a second of each other.
  2258. tolerance = timedelta(seconds=1)
  2259. for dummy in range(3):
  2260. from_now = self.theclass.utcnow()
  2261. from_timestamp = self.theclass.utcfromtimestamp(time.time())
  2262. if abs(from_timestamp - from_now) <= tolerance:
  2263. break
  2264. # Else try again a few times.
  2265. self.assertLessEqual(abs(from_timestamp - from_now), tolerance)
  2266. def test_strptime(self):
  2267. string = '2004-12-01 13:02:47.197'
  2268. format = '%Y-%m-%d %H:%M:%S.%f'
  2269. expected = _strptime._strptime_datetime(self.theclass, string, format)
  2270. got = self.theclass.strptime(string, format)
  2271. self.assertEqual(expected, got)
  2272. self.assertIs(type(expected), self.theclass)
  2273. self.assertIs(type(got), self.theclass)
  2274. # bpo-34482: Check that surrogates are handled properly.
  2275. inputs = [
  2276. ('2004-12-01\ud80013:02:47.197', '%Y-%m-%d\ud800%H:%M:%S.%f'),
  2277. ('2004\ud80012-01 13:02:47.197', '%Y\ud800%m-%d %H:%M:%S.%f'),
  2278. ('2004-12-01 13:02\ud80047.197', '%Y-%m-%d %H:%M\ud800%S.%f'),
  2279. ]
  2280. for string, format in inputs:
  2281. with self.subTest(string=string, format=format):
  2282. expected = _strptime._strptime_datetime(self.theclass, string,
  2283. format)
  2284. got = self.theclass.strptime(string, format)
  2285. self.assertEqual(expected, got)
  2286. strptime = self.theclass.strptime
  2287. self.assertEqual(strptime("+0002", "%z").utcoffset(), 2 * MINUTE)
  2288. self.assertEqual(strptime("-0002", "%z").utcoffset(), -2 * MINUTE)
  2289. self.assertEqual(
  2290. strptime("-00:02:01.000003", "%z").utcoffset(),
  2291. -timedelta(minutes=2, seconds=1, microseconds=3)
  2292. )
  2293. # Only local timezone and UTC are supported
  2294. for tzseconds, tzname in ((0, 'UTC'), (0, 'GMT'),
  2295. (-_time.timezone, _time.tzname[0])):
  2296. if tzseconds < 0:
  2297. sign = '-'
  2298. seconds = -tzseconds
  2299. else:
  2300. sign ='+'
  2301. seconds = tzseconds
  2302. hours, minutes = divmod(seconds//60, 60)
  2303. dtstr = "{}{:02d}{:02d} {}".format(sign, hours, minutes, tzname)
  2304. dt = strptime(dtstr, "%z %Z")
  2305. self.assertEqual(dt.utcoffset(), timedelta(seconds=tzseconds))
  2306. self.assertEqual(dt.tzname(), tzname)
  2307. # Can produce inconsistent datetime
  2308. dtstr, fmt = "+1234 UTC", "%z %Z"
  2309. dt = strptime(dtstr, fmt)
  2310. self.assertEqual(dt.utcoffset(), 12 * HOUR + 34 * MINUTE)
  2311. self.assertEqual(dt.tzname(), 'UTC')
  2312. # yet will roundtrip
  2313. self.assertEqual(dt.strftime(fmt), dtstr)
  2314. # Produce naive datetime if no %z is provided
  2315. self.assertEqual(strptime("UTC", "%Z").tzinfo, None)
  2316. with self.assertRaises(ValueError): strptime("-2400", "%z")
  2317. with self.assertRaises(ValueError): strptime("-000", "%z")
  2318. with self.assertRaises(ValueError): strptime("z", "%z")
  2319. def test_strptime_single_digit(self):
  2320. # bpo-34903: Check that single digit dates and times are allowed.
  2321. strptime = self.theclass.strptime
  2322. with self.assertRaises(ValueError):
  2323. # %y does require two digits.
  2324. newdate = strptime('01/02/3 04:05:06', '%d/%m/%y %H:%M:%S')
  2325. dt1 = self.theclass(2003, 2, 1, 4, 5, 6)
  2326. dt2 = self.theclass(2003, 1, 2, 4, 5, 6)
  2327. dt3 = self.theclass(2003, 2, 1, 0, 0, 0)
  2328. dt4 = self.theclass(2003, 1, 25, 0, 0, 0)
  2329. inputs = [
  2330. ('%d', '1/02/03 4:5:6', '%d/%m/%y %H:%M:%S', dt1),
  2331. ('%m', '01/2/03 4:5:6', '%d/%m/%y %H:%M:%S', dt1),
  2332. ('%H', '01/02/03 4:05:06', '%d/%m/%y %H:%M:%S', dt1),
  2333. ('%M', '01/02/03 04:5:06', '%d/%m/%y %H:%M:%S', dt1),
  2334. ('%S', '01/02/03 04:05:6', '%d/%m/%y %H:%M:%S', dt1),
  2335. ('%j', '2/03 04am:05:06', '%j/%y %I%p:%M:%S',dt2),
  2336. ('%I', '02/03 4am:05:06', '%j/%y %I%p:%M:%S',dt2),
  2337. ('%w', '6/04/03', '%w/%U/%y', dt3),
  2338. # %u requires a single digit.
  2339. ('%W', '6/4/2003', '%u/%W/%Y', dt3),
  2340. ('%V', '6/4/2003', '%u/%V/%G', dt4),
  2341. ]
  2342. for reason, string, format, target in inputs:
  2343. reason = 'test single digit ' + reason
  2344. with self.subTest(reason=reason,
  2345. string=string,
  2346. format=format,
  2347. target=target):
  2348. newdate = strptime(string, format)
  2349. self.assertEqual(newdate, target, msg=reason)
  2350. def test_more_timetuple(self):
  2351. # This tests fields beyond those tested by the TestDate.test_timetuple.
  2352. t = self.theclass(2004, 12, 31, 6, 22, 33)
  2353. self.assertEqual(t.timetuple(), (2004, 12, 31, 6, 22, 33, 4, 366, -1))
  2354. self.assertEqual(t.timetuple(),
  2355. (t.year, t.month, t.day,
  2356. t.hour, t.minute, t.second,
  2357. t.weekday(),
  2358. t.toordinal() - date(t.year, 1, 1).toordinal() + 1,
  2359. -1))
  2360. tt = t.timetuple()
  2361. self.assertEqual(tt.tm_year, t.year)
  2362. self.assertEqual(tt.tm_mon, t.month)
  2363. self.assertEqual(tt.tm_mday, t.day)
  2364. self.assertEqual(tt.tm_hour, t.hour)
  2365. self.assertEqual(tt.tm_min, t.minute)
  2366. self.assertEqual(tt.tm_sec, t.second)
  2367. self.assertEqual(tt.tm_wday, t.weekday())
  2368. self.assertEqual(tt.tm_yday, t.toordinal() -
  2369. date(t.year, 1, 1).toordinal() + 1)
  2370. self.assertEqual(tt.tm_isdst, -1)
  2371. def test_more_strftime(self):
  2372. # This tests fields beyond those tested by the TestDate.test_strftime.
  2373. t = self.theclass(2004, 12, 31, 6, 22, 33, 47)
  2374. self.assertEqual(t.strftime("%m %d %y %f %S %M %H %j"),
  2375. "12 31 04 000047 33 22 06 366")
  2376. for (s, us), z in [((33, 123), "33.000123"), ((33, 0), "33"),]:
  2377. tz = timezone(-timedelta(hours=2, seconds=s, microseconds=us))
  2378. t = t.replace(tzinfo=tz)
  2379. self.assertEqual(t.strftime("%z"), "-0200" + z)
  2380. # bpo-34482: Check that surrogates don't cause a crash.
  2381. try:
  2382. t.strftime('%y\ud800%m %H\ud800%M')
  2383. except UnicodeEncodeError:
  2384. pass
  2385. def test_extract(self):
  2386. dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
  2387. self.assertEqual(dt.date(), date(2002, 3, 4))
  2388. self.assertEqual(dt.time(), time(18, 45, 3, 1234))
  2389. def test_combine(self):
  2390. d = date(2002, 3, 4)
  2391. t = time(18, 45, 3, 1234)
  2392. expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)
  2393. combine = self.theclass.combine
  2394. dt = combine(d, t)
  2395. self.assertEqual(dt, expected)
  2396. dt = combine(time=t, date=d)
  2397. self.assertEqual(dt, expected)
  2398. self.assertEqual(d, dt.date())
  2399. self.assertEqual(t, dt.time())
  2400. self.assertEqual(dt, combine(dt.date(), dt.time()))
  2401. self.assertRaises(TypeError, combine) # need an arg
  2402. self.assertRaises(TypeError, combine, d) # need two args
  2403. self.assertRaises(TypeError, combine, t, d) # args reversed
  2404. self.assertRaises(TypeError, combine, d, t, 1) # wrong tzinfo type
  2405. self.assertRaises(TypeError, combine, d, t, 1, 2) # too many args
  2406. self.assertRaises(TypeError, combine, "date", "time") # wrong types
  2407. self.assertRaises(TypeError, combine, d, "time") # wrong type
  2408. self.assertRaises(TypeError, combine, "date", t) # wrong type
  2409. # tzinfo= argument
  2410. dt = combine(d, t, timezone.utc)
  2411. self.assertIs(dt.tzinfo, timezone.utc)
  2412. dt = combine(d, t, tzinfo=timezone.utc)
  2413. self.assertIs(dt.tzinfo, timezone.utc)
  2414. t = time()
  2415. dt = combine(dt, t)
  2416. self.assertEqual(dt.date(), d)
  2417. self.assertEqual(dt.time(), t)
  2418. def test_replace(self):
  2419. cls = self.theclass
  2420. args = [1, 2, 3, 4, 5, 6, 7]
  2421. base = cls(*args)
  2422. self.assertEqual(base, base.replace())
  2423. i = 0
  2424. for name, newval in (("year", 2),
  2425. ("month", 3),
  2426. ("day", 4),
  2427. ("hour", 5),
  2428. ("minute", 6),
  2429. ("second", 7),
  2430. ("microsecond", 8)):
  2431. newargs = args[:]
  2432. newargs[i] = newval
  2433. expected = cls(*newargs)
  2434. got = base.replace(**{name: newval})
  2435. self.assertEqual(expected, got)
  2436. i += 1
  2437. # Out of bounds.
  2438. base = cls(2000, 2, 29)
  2439. self.assertRaises(ValueError, base.replace, year=2001)
  2440. @support.run_with_tz('EDT4')
  2441. def test_astimezone(self):
  2442. dt = self.theclass.now()
  2443. f = FixedOffset(44, "0044")
  2444. dt_utc = dt.replace(tzinfo=timezone(timedelta(hours=-4), 'EDT'))
  2445. self.assertEqual(dt.astimezone(), dt_utc) # naive
  2446. self.assertRaises(TypeError, dt.astimezone, f, f) # too many args
  2447. self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type
  2448. dt_f = dt.replace(tzinfo=f) + timedelta(hours=4, minutes=44)
  2449. self.assertEqual(dt.astimezone(f), dt_f) # naive
  2450. self.assertEqual(dt.astimezone(tz=f), dt_f) # naive
  2451. class Bogus(tzinfo):
  2452. def utcoffset(self, dt): return None
  2453. def dst(self, dt): return timedelta(0)
  2454. bog = Bogus()
  2455. self.assertRaises(ValueError, dt.astimezone, bog) # naive
  2456. self.assertEqual(dt.replace(tzinfo=bog).astimezone(f), dt_f)
  2457. class AlsoBogus(tzinfo):
  2458. def utcoffset(self, dt): return timedelta(0)
  2459. def dst(self, dt): return None
  2460. alsobog = AlsoBogus()
  2461. self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive
  2462. class Broken(tzinfo):
  2463. def utcoffset(self, dt): return 1
  2464. def dst(self, dt): return 1
  2465. broken = Broken()
  2466. dt_broken = dt.replace(tzinfo=broken)
  2467. with self.assertRaises(TypeError):
  2468. dt_broken.astimezone()
  2469. def test_subclass_datetime(self):
  2470. class C(self.theclass):
  2471. theAnswer = 42
  2472. def __new__(cls, *args, **kws):
  2473. temp = kws.copy()
  2474. extra = temp.pop('extra')
  2475. result = self.theclass.__new__(cls, *args, **temp)
  2476. result.extra = extra
  2477. return result
  2478. def newmeth(self, start):
  2479. return start + self.year + self.month + self.second
  2480. args = 2003, 4, 14, 12, 13, 41
  2481. dt1 = self.theclass(*args)
  2482. dt2 = C(*args, **{'extra': 7})
  2483. self.assertEqual(dt2.__class__, C)
  2484. self.assertEqual(dt2.theAnswer, 42)
  2485. self.assertEqual(dt2.extra, 7)
  2486. self.assertEqual(dt1.toordinal(), dt2.toordinal())
  2487. self.assertEqual(dt2.newmeth(-7), dt1.year + dt1.month +
  2488. dt1.second - 7)
  2489. def test_subclass_alternate_constructors_datetime(self):
  2490. # Test that alternate constructors call the constructor
  2491. class DateTimeSubclass(self.theclass):
  2492. def __new__(cls, *args, **kwargs):
  2493. result = self.theclass.__new__(cls, *args, **kwargs)
  2494. result.extra = 7
  2495. return result
  2496. args = (2003, 4, 14, 12, 30, 15, 123456)
  2497. d_isoformat = '2003-04-14T12:30:15.123456' # Equivalent isoformat()
  2498. utc_ts = 1050323415.123456 # UTC timestamp
  2499. base_d = DateTimeSubclass(*args)
  2500. self.assertIsInstance(base_d, DateTimeSubclass)
  2501. self.assertEqual(base_d.extra, 7)
  2502. # Timestamp depends on time zone, so we'll calculate the equivalent here
  2503. ts = base_d.timestamp()
  2504. test_cases = [
  2505. ('fromtimestamp', (ts,), base_d),
  2506. # See https://bugs.python.org/issue32417
  2507. ('fromtimestamp', (ts, timezone.utc),
  2508. base_d.astimezone(timezone.utc)),
  2509. ('utcfromtimestamp', (utc_ts,), base_d),
  2510. ('fromisoformat', (d_isoformat,), base_d),
  2511. ('strptime', (d_isoformat, '%Y-%m-%dT%H:%M:%S.%f'), base_d),
  2512. ('combine', (date(*args[0:3]), time(*args[3:])), base_d),
  2513. ]
  2514. for constr_name, constr_args, expected in test_cases:
  2515. for base_obj in (DateTimeSubclass, base_d):
  2516. # Test both the classmethod and method
  2517. with self.subTest(base_obj_type=type(base_obj),
  2518. constr_name=constr_name):
  2519. constructor = getattr(base_obj, constr_name)
  2520. dt = constructor(*constr_args)
  2521. # Test that it creates the right subclass
  2522. self.assertIsInstance(dt, DateTimeSubclass)
  2523. # Test that it's equal to the base object
  2524. self.assertEqual(dt, expected)
  2525. # Test that it called the constructor
  2526. self.assertEqual(dt.extra, 7)
  2527. def test_subclass_now(self):
  2528. # Test that alternate constructors call the constructor
  2529. class DateTimeSubclass(self.theclass):
  2530. def __new__(cls, *args, **kwargs):
  2531. result = self.theclass.__new__(cls, *args, **kwargs)
  2532. result.extra = 7
  2533. return result
  2534. test_cases = [
  2535. ('now', 'now', {}),
  2536. ('utcnow', 'utcnow', {}),
  2537. ('now_utc', 'now', {'tz': timezone.utc}),
  2538. ('now_fixed', 'now', {'tz': timezone(timedelta(hours=-5), "EST")}),
  2539. ]
  2540. for name, meth_name, kwargs in test_cases:
  2541. with self.subTest(name):
  2542. constr = getattr(DateTimeSubclass, meth_name)
  2543. dt = constr(**kwargs)
  2544. self.assertIsInstance(dt, DateTimeSubclass)
  2545. self.assertEqual(dt.extra, 7)
  2546. def test_fromisoformat_datetime(self):
  2547. # Test that isoformat() is reversible
  2548. base_dates = [
  2549. (1, 1, 1),
  2550. (1900, 1, 1),
  2551. (2004, 11, 12),
  2552. (2017, 5, 30)
  2553. ]
  2554. base_times = [
  2555. (0, 0, 0, 0),
  2556. (0, 0, 0, 241000),
  2557. (0, 0, 0, 234567),
  2558. (12, 30, 45, 234567)
  2559. ]
  2560. separators = [' ', 'T']
  2561. tzinfos = [None, timezone.utc,
  2562. timezone(timedelta(hours=-5)),
  2563. timezone(timedelta(hours=2))]
  2564. dts = [self.theclass(*date_tuple, *time_tuple, tzinfo=tzi)
  2565. for date_tuple in base_dates
  2566. for time_tuple in base_times
  2567. for tzi in tzinfos]
  2568. for dt in dts:
  2569. for sep in separators:
  2570. dtstr = dt.isoformat(sep=sep)
  2571. with self.subTest(dtstr=dtstr):
  2572. dt_rt = self.theclass.fromisoformat(dtstr)
  2573. self.assertEqual(dt, dt_rt)
  2574. def test_fromisoformat_timezone(self):
  2575. base_dt = self.theclass(2014, 12, 30, 12, 30, 45, 217456)
  2576. tzoffsets = [
  2577. timedelta(hours=5), timedelta(hours=2),
  2578. timedelta(hours=6, minutes=27),
  2579. timedelta(hours=12, minutes=32, seconds=30),
  2580. timedelta(hours=2, minutes=4, seconds=9, microseconds=123456)
  2581. ]
  2582. tzoffsets += [-1 * td for td in tzoffsets]
  2583. tzinfos = [None, timezone.utc,
  2584. timezone(timedelta(hours=0))]
  2585. tzinfos += [timezone(td) for td in tzoffsets]
  2586. for tzi in tzinfos:
  2587. dt = base_dt.replace(tzinfo=tzi)
  2588. dtstr = dt.isoformat()
  2589. with self.subTest(tstr=dtstr):
  2590. dt_rt = self.theclass.fromisoformat(dtstr)
  2591. assert dt == dt_rt, dt_rt
  2592. def test_fromisoformat_separators(self):
  2593. separators = [
  2594. ' ', 'T', '\u007f', # 1-bit widths
  2595. '\u0080', 'ʁ', # 2-bit widths
  2596. 'ᛇ', '時', # 3-bit widths
  2597. '🐍', # 4-bit widths
  2598. '\ud800', # bpo-34454: Surrogate code point
  2599. ]
  2600. for sep in separators:
  2601. dt = self.theclass(2018, 1, 31, 23, 59, 47, 124789)
  2602. dtstr = dt.isoformat(sep=sep)
  2603. with self.subTest(dtstr=dtstr):
  2604. dt_rt = self.theclass.fromisoformat(dtstr)
  2605. self.assertEqual(dt, dt_rt)
  2606. def test_fromisoformat_ambiguous(self):
  2607. # Test strings like 2018-01-31+12:15 (where +12:15 is not a time zone)
  2608. separators = ['+', '-']
  2609. for sep in separators:
  2610. dt = self.theclass(2018, 1, 31, 12, 15)
  2611. dtstr = dt.isoformat(sep=sep)
  2612. with self.subTest(dtstr=dtstr):
  2613. dt_rt = self.theclass.fromisoformat(dtstr)
  2614. self.assertEqual(dt, dt_rt)
  2615. def test_fromisoformat_timespecs(self):
  2616. datetime_bases = [
  2617. (2009, 12, 4, 8, 17, 45, 123456),
  2618. (2009, 12, 4, 8, 17, 45, 0)]
  2619. tzinfos = [None, timezone.utc,
  2620. timezone(timedelta(hours=-5)),
  2621. timezone(timedelta(hours=2)),
  2622. timezone(timedelta(hours=6, minutes=27))]
  2623. timespecs = ['hours', 'minutes', 'seconds',
  2624. 'milliseconds', 'microseconds']
  2625. for ip, ts in enumerate(timespecs):
  2626. for tzi in tzinfos:
  2627. for dt_tuple in datetime_bases:
  2628. if ts == 'milliseconds':
  2629. new_microseconds = 1000 * (dt_tuple[6] // 1000)
  2630. dt_tuple = dt_tuple[0:6] + (new_microseconds,)
  2631. dt = self.theclass(*(dt_tuple[0:(4 + ip)]), tzinfo=tzi)
  2632. dtstr = dt.isoformat(timespec=ts)
  2633. with self.subTest(dtstr=dtstr):
  2634. dt_rt = self.theclass.fromisoformat(dtstr)
  2635. self.assertEqual(dt, dt_rt)
  2636. def test_fromisoformat_datetime_examples(self):
  2637. UTC = timezone.utc
  2638. BST = timezone(timedelta(hours=1), 'BST')
  2639. EST = timezone(timedelta(hours=-5), 'EST')
  2640. EDT = timezone(timedelta(hours=-4), 'EDT')
  2641. examples = [
  2642. ('2025-01-02', self.theclass(2025, 1, 2, 0, 0)),
  2643. ('2025-01-02T03', self.theclass(2025, 1, 2, 3, 0)),
  2644. ('2025-01-02T03:04', self.theclass(2025, 1, 2, 3, 4)),
  2645. ('2025-01-02T0304', self.theclass(2025, 1, 2, 3, 4)),
  2646. ('2025-01-02T03:04:05', self.theclass(2025, 1, 2, 3, 4, 5)),
  2647. ('2025-01-02T030405', self.theclass(2025, 1, 2, 3, 4, 5)),
  2648. ('2025-01-02T03:04:05.6',
  2649. self.theclass(2025, 1, 2, 3, 4, 5, 600000)),
  2650. ('2025-01-02T03:04:05,6',
  2651. self.theclass(2025, 1, 2, 3, 4, 5, 600000)),
  2652. ('2025-01-02T03:04:05.678',
  2653. self.theclass(2025, 1, 2, 3, 4, 5, 678000)),
  2654. ('2025-01-02T03:04:05.678901',
  2655. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2656. ('2025-01-02T03:04:05,678901',
  2657. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2658. ('2025-01-02T030405.678901',
  2659. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2660. ('2025-01-02T030405,678901',
  2661. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2662. ('2025-01-02T03:04:05.6789010',
  2663. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2664. ('2009-04-19T03:15:45.2345',
  2665. self.theclass(2009, 4, 19, 3, 15, 45, 234500)),
  2666. ('2009-04-19T03:15:45.1234567',
  2667. self.theclass(2009, 4, 19, 3, 15, 45, 123456)),
  2668. ('2025-01-02T03:04:05,678',
  2669. self.theclass(2025, 1, 2, 3, 4, 5, 678000)),
  2670. ('20250102', self.theclass(2025, 1, 2, 0, 0)),
  2671. ('20250102T03', self.theclass(2025, 1, 2, 3, 0)),
  2672. ('20250102T03:04', self.theclass(2025, 1, 2, 3, 4)),
  2673. ('20250102T03:04:05', self.theclass(2025, 1, 2, 3, 4, 5)),
  2674. ('20250102T030405', self.theclass(2025, 1, 2, 3, 4, 5)),
  2675. ('20250102T03:04:05.6',
  2676. self.theclass(2025, 1, 2, 3, 4, 5, 600000)),
  2677. ('20250102T03:04:05,6',
  2678. self.theclass(2025, 1, 2, 3, 4, 5, 600000)),
  2679. ('20250102T03:04:05.678',
  2680. self.theclass(2025, 1, 2, 3, 4, 5, 678000)),
  2681. ('20250102T03:04:05,678',
  2682. self.theclass(2025, 1, 2, 3, 4, 5, 678000)),
  2683. ('20250102T03:04:05.678901',
  2684. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2685. ('20250102T030405.678901',
  2686. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2687. ('20250102T030405,678901',
  2688. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2689. ('20250102T030405.6789010',
  2690. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2691. ('2022W01', self.theclass(2022, 1, 3)),
  2692. ('2022W52520', self.theclass(2022, 12, 26, 20, 0)),
  2693. ('2022W527520', self.theclass(2023, 1, 1, 20, 0)),
  2694. ('2026W01516', self.theclass(2025, 12, 29, 16, 0)),
  2695. ('2026W013516', self.theclass(2025, 12, 31, 16, 0)),
  2696. ('2025W01503', self.theclass(2024, 12, 30, 3, 0)),
  2697. ('2025W014503', self.theclass(2025, 1, 2, 3, 0)),
  2698. ('2025W01512', self.theclass(2024, 12, 30, 12, 0)),
  2699. ('2025W014512', self.theclass(2025, 1, 2, 12, 0)),
  2700. ('2025W014T121431', self.theclass(2025, 1, 2, 12, 14, 31)),
  2701. ('2026W013T162100', self.theclass(2025, 12, 31, 16, 21)),
  2702. ('2026W013 162100', self.theclass(2025, 12, 31, 16, 21)),
  2703. ('2022W527T202159', self.theclass(2023, 1, 1, 20, 21, 59)),
  2704. ('2022W527 202159', self.theclass(2023, 1, 1, 20, 21, 59)),
  2705. ('2025W014 121431', self.theclass(2025, 1, 2, 12, 14, 31)),
  2706. ('2025W014T030405', self.theclass(2025, 1, 2, 3, 4, 5)),
  2707. ('2025W014 030405', self.theclass(2025, 1, 2, 3, 4, 5)),
  2708. ('2020-W53-6T03:04:05', self.theclass(2021, 1, 2, 3, 4, 5)),
  2709. ('2020W537 03:04:05', self.theclass(2021, 1, 3, 3, 4, 5)),
  2710. ('2025-W01-4T03:04:05', self.theclass(2025, 1, 2, 3, 4, 5)),
  2711. ('2025-W01-4T03:04:05.678901',
  2712. self.theclass(2025, 1, 2, 3, 4, 5, 678901)),
  2713. ('2025-W01-4T12:14:31', self.theclass(2025, 1, 2, 12, 14, 31)),
  2714. ('2025-W01-4T12:14:31.012345',
  2715. self.theclass(2025, 1, 2, 12, 14, 31, 12345)),
  2716. ('2026-W01-3T16:21:00', self.theclass(2025, 12, 31, 16, 21)),
  2717. ('2026-W01-3T16:21:00.000000', self.theclass(2025, 12, 31, 16, 21)),
  2718. ('2022-W52-7T20:21:59',
  2719. self.theclass(2023, 1, 1, 20, 21, 59)),
  2720. ('2022-W52-7T20:21:59.999999',
  2721. self.theclass(2023, 1, 1, 20, 21, 59, 999999)),
  2722. ('2025-W01003+00',
  2723. self.theclass(2024, 12, 30, 3, 0, tzinfo=UTC)),
  2724. ('2025-01-02T03:04:05+00',
  2725. self.theclass(2025, 1, 2, 3, 4, 5, tzinfo=UTC)),
  2726. ('2025-01-02T03:04:05Z',
  2727. self.theclass(2025, 1, 2, 3, 4, 5, tzinfo=UTC)),
  2728. ('2025-01-02003:04:05,6+00:00:00.00',
  2729. self.theclass(2025, 1, 2, 3, 4, 5, 600000, tzinfo=UTC)),
  2730. ('2000-01-01T00+21',
  2731. self.theclass(2000, 1, 1, 0, 0, tzinfo=timezone(timedelta(hours=21)))),
  2732. ('2025-01-02T03:05:06+0300',
  2733. self.theclass(2025, 1, 2, 3, 5, 6,
  2734. tzinfo=timezone(timedelta(hours=3)))),
  2735. ('2025-01-02T03:05:06-0300',
  2736. self.theclass(2025, 1, 2, 3, 5, 6,
  2737. tzinfo=timezone(timedelta(hours=-3)))),
  2738. ('2025-01-02T03:04:05+0000',
  2739. self.theclass(2025, 1, 2, 3, 4, 5, tzinfo=UTC)),
  2740. ('2025-01-02T03:05:06+03',
  2741. self.theclass(2025, 1, 2, 3, 5, 6,
  2742. tzinfo=timezone(timedelta(hours=3)))),
  2743. ('2025-01-02T03:05:06-03',
  2744. self.theclass(2025, 1, 2, 3, 5, 6,
  2745. tzinfo=timezone(timedelta(hours=-3)))),
  2746. ('2020-01-01T03:05:07.123457-05:00',
  2747. self.theclass(2020, 1, 1, 3, 5, 7, 123457, tzinfo=EST)),
  2748. ('2020-01-01T03:05:07.123457-0500',
  2749. self.theclass(2020, 1, 1, 3, 5, 7, 123457, tzinfo=EST)),
  2750. ('2020-06-01T04:05:06.111111-04:00',
  2751. self.theclass(2020, 6, 1, 4, 5, 6, 111111, tzinfo=EDT)),
  2752. ('2020-06-01T04:05:06.111111-0400',
  2753. self.theclass(2020, 6, 1, 4, 5, 6, 111111, tzinfo=EDT)),
  2754. ('2021-10-31T01:30:00.000000+01:00',
  2755. self.theclass(2021, 10, 31, 1, 30, tzinfo=BST)),
  2756. ('2021-10-31T01:30:00.000000+0100',
  2757. self.theclass(2021, 10, 31, 1, 30, tzinfo=BST)),
  2758. ('2025-01-02T03:04:05,6+000000.00',
  2759. self.theclass(2025, 1, 2, 3, 4, 5, 600000, tzinfo=UTC)),
  2760. ('2025-01-02T03:04:05,678+00:00:10',
  2761. self.theclass(2025, 1, 2, 3, 4, 5, 678000,
  2762. tzinfo=timezone(timedelta(seconds=10)))),
  2763. ]
  2764. for input_str, expected in examples:
  2765. with self.subTest(input_str=input_str):
  2766. actual = self.theclass.fromisoformat(input_str)
  2767. self.assertEqual(actual, expected)
  2768. def test_fromisoformat_fails_datetime(self):
  2769. # Test that fromisoformat() fails on invalid values
  2770. bad_strs = [
  2771. '', # Empty string
  2772. '\ud800', # bpo-34454: Surrogate code point
  2773. '2009.04-19T03', # Wrong first separator
  2774. '2009-04.19T03', # Wrong second separator
  2775. '2009-04-19T0a', # Invalid hours
  2776. '2009-04-19T03:1a:45', # Invalid minutes
  2777. '2009-04-19T03:15:4a', # Invalid seconds
  2778. '2009-04-19T03;15:45', # Bad first time separator
  2779. '2009-04-19T03:15;45', # Bad second time separator
  2780. '2009-04-19T03:15:4500:00', # Bad time zone separator
  2781. '2009-04-19T03:15:45.123456+24:30', # Invalid time zone offset
  2782. '2009-04-19T03:15:45.123456-24:30', # Invalid negative offset
  2783. '2009-04-10ᛇᛇᛇᛇᛇ12:15', # Too many unicode separators
  2784. '2009-04\ud80010T12:15', # Surrogate char in date
  2785. '2009-04-10T12\ud80015', # Surrogate char in time
  2786. '2009-04-19T1', # Incomplete hours
  2787. '2009-04-19T12:3', # Incomplete minutes
  2788. '2009-04-19T12:30:4', # Incomplete seconds
  2789. '2009-04-19T12:', # Ends with time separator
  2790. '2009-04-19T12:30:', # Ends with time separator
  2791. '2009-04-19T12:30:45.', # Ends with time separator
  2792. '2009-04-19T12:30:45.123456+', # Ends with timzone separator
  2793. '2009-04-19T12:30:45.123456-', # Ends with timzone separator
  2794. '2009-04-19T12:30:45.123456-05:00a', # Extra text
  2795. '2009-04-19T12:30:45.123-05:00a', # Extra text
  2796. '2009-04-19T12:30:45-05:00a', # Extra text
  2797. ]
  2798. for bad_str in bad_strs:
  2799. with self.subTest(bad_str=bad_str):
  2800. with self.assertRaises(ValueError):
  2801. self.theclass.fromisoformat(bad_str)
  2802. def test_fromisoformat_fails_surrogate(self):
  2803. # Test that when fromisoformat() fails with a surrogate character as
  2804. # the separator, the error message contains the original string
  2805. dtstr = "2018-01-03\ud80001:0113"
  2806. with self.assertRaisesRegex(ValueError, re.escape(repr(dtstr))):
  2807. self.theclass.fromisoformat(dtstr)
  2808. def test_fromisoformat_utc(self):
  2809. dt_str = '2014-04-19T13:21:13+00:00'
  2810. dt = self.theclass.fromisoformat(dt_str)
  2811. self.assertIs(dt.tzinfo, timezone.utc)
  2812. def test_fromisoformat_subclass(self):
  2813. class DateTimeSubclass(self.theclass):
  2814. pass
  2815. dt = DateTimeSubclass(2014, 12, 14, 9, 30, 45, 457390,
  2816. tzinfo=timezone(timedelta(hours=10, minutes=45)))
  2817. dt_rt = DateTimeSubclass.fromisoformat(dt.isoformat())
  2818. self.assertEqual(dt, dt_rt)
  2819. self.assertIsInstance(dt_rt, DateTimeSubclass)
  2820. class TestSubclassDateTime(TestDateTime):
  2821. theclass = SubclassDatetime
  2822. # Override tests not designed for subclass
  2823. @unittest.skip('not appropriate for subclasses')
  2824. def test_roundtrip(self):
  2825. pass
  2826. class SubclassTime(time):
  2827. sub_var = 1
  2828. class TestTime(HarmlessMixedComparison, unittest.TestCase):
  2829. theclass = time
  2830. def test_basic_attributes(self):
  2831. t = self.theclass(12, 0)
  2832. self.assertEqual(t.hour, 12)
  2833. self.assertEqual(t.minute, 0)
  2834. self.assertEqual(t.second, 0)
  2835. self.assertEqual(t.microsecond, 0)
  2836. def test_basic_attributes_nonzero(self):
  2837. # Make sure all attributes are non-zero so bugs in
  2838. # bit-shifting access show up.
  2839. t = self.theclass(12, 59, 59, 8000)
  2840. self.assertEqual(t.hour, 12)
  2841. self.assertEqual(t.minute, 59)
  2842. self.assertEqual(t.second, 59)
  2843. self.assertEqual(t.microsecond, 8000)
  2844. def test_roundtrip(self):
  2845. t = self.theclass(1, 2, 3, 4)
  2846. # Verify t -> string -> time identity.
  2847. s = repr(t)
  2848. self.assertTrue(s.startswith('datetime.'))
  2849. s = s[9:]
  2850. t2 = eval(s)
  2851. self.assertEqual(t, t2)
  2852. # Verify identity via reconstructing from pieces.
  2853. t2 = self.theclass(t.hour, t.minute, t.second,
  2854. t.microsecond)
  2855. self.assertEqual(t, t2)
  2856. def test_comparing(self):
  2857. args = [1, 2, 3, 4]
  2858. t1 = self.theclass(*args)
  2859. t2 = self.theclass(*args)
  2860. self.assertEqual(t1, t2)
  2861. self.assertTrue(t1 <= t2)
  2862. self.assertTrue(t1 >= t2)
  2863. self.assertFalse(t1 != t2)
  2864. self.assertFalse(t1 < t2)
  2865. self.assertFalse(t1 > t2)
  2866. for i in range(len(args)):
  2867. newargs = args[:]
  2868. newargs[i] = args[i] + 1
  2869. t2 = self.theclass(*newargs) # this is larger than t1
  2870. self.assertTrue(t1 < t2)
  2871. self.assertTrue(t2 > t1)
  2872. self.assertTrue(t1 <= t2)
  2873. self.assertTrue(t2 >= t1)
  2874. self.assertTrue(t1 != t2)
  2875. self.assertTrue(t2 != t1)
  2876. self.assertFalse(t1 == t2)
  2877. self.assertFalse(t2 == t1)
  2878. self.assertFalse(t1 > t2)
  2879. self.assertFalse(t2 < t1)
  2880. self.assertFalse(t1 >= t2)
  2881. self.assertFalse(t2 <= t1)
  2882. for badarg in OTHERSTUFF:
  2883. self.assertEqual(t1 == badarg, False)
  2884. self.assertEqual(t1 != badarg, True)
  2885. self.assertEqual(badarg == t1, False)
  2886. self.assertEqual(badarg != t1, True)
  2887. self.assertRaises(TypeError, lambda: t1 <= badarg)
  2888. self.assertRaises(TypeError, lambda: t1 < badarg)
  2889. self.assertRaises(TypeError, lambda: t1 > badarg)
  2890. self.assertRaises(TypeError, lambda: t1 >= badarg)
  2891. self.assertRaises(TypeError, lambda: badarg <= t1)
  2892. self.assertRaises(TypeError, lambda: badarg < t1)
  2893. self.assertRaises(TypeError, lambda: badarg > t1)
  2894. self.assertRaises(TypeError, lambda: badarg >= t1)
  2895. def test_bad_constructor_arguments(self):
  2896. # bad hours
  2897. self.theclass(0, 0) # no exception
  2898. self.theclass(23, 0) # no exception
  2899. self.assertRaises(ValueError, self.theclass, -1, 0)
  2900. self.assertRaises(ValueError, self.theclass, 24, 0)
  2901. # bad minutes
  2902. self.theclass(23, 0) # no exception
  2903. self.theclass(23, 59) # no exception
  2904. self.assertRaises(ValueError, self.theclass, 23, -1)
  2905. self.assertRaises(ValueError, self.theclass, 23, 60)
  2906. # bad seconds
  2907. self.theclass(23, 59, 0) # no exception
  2908. self.theclass(23, 59, 59) # no exception
  2909. self.assertRaises(ValueError, self.theclass, 23, 59, -1)
  2910. self.assertRaises(ValueError, self.theclass, 23, 59, 60)
  2911. # bad microseconds
  2912. self.theclass(23, 59, 59, 0) # no exception
  2913. self.theclass(23, 59, 59, 999999) # no exception
  2914. self.assertRaises(ValueError, self.theclass, 23, 59, 59, -1)
  2915. self.assertRaises(ValueError, self.theclass, 23, 59, 59, 1000000)
  2916. def test_hash_equality(self):
  2917. d = self.theclass(23, 30, 17)
  2918. e = self.theclass(23, 30, 17)
  2919. self.assertEqual(d, e)
  2920. self.assertEqual(hash(d), hash(e))
  2921. dic = {d: 1}
  2922. dic[e] = 2
  2923. self.assertEqual(len(dic), 1)
  2924. self.assertEqual(dic[d], 2)
  2925. self.assertEqual(dic[e], 2)
  2926. d = self.theclass(0, 5, 17)
  2927. e = self.theclass(0, 5, 17)
  2928. self.assertEqual(d, e)
  2929. self.assertEqual(hash(d), hash(e))
  2930. dic = {d: 1}
  2931. dic[e] = 2
  2932. self.assertEqual(len(dic), 1)
  2933. self.assertEqual(dic[d], 2)
  2934. self.assertEqual(dic[e], 2)
  2935. def test_isoformat(self):
  2936. t = self.theclass(4, 5, 1, 123)
  2937. self.assertEqual(t.isoformat(), "04:05:01.000123")
  2938. self.assertEqual(t.isoformat(), str(t))
  2939. t = self.theclass()
  2940. self.assertEqual(t.isoformat(), "00:00:00")
  2941. self.assertEqual(t.isoformat(), str(t))
  2942. t = self.theclass(microsecond=1)
  2943. self.assertEqual(t.isoformat(), "00:00:00.000001")
  2944. self.assertEqual(t.isoformat(), str(t))
  2945. t = self.theclass(microsecond=10)
  2946. self.assertEqual(t.isoformat(), "00:00:00.000010")
  2947. self.assertEqual(t.isoformat(), str(t))
  2948. t = self.theclass(microsecond=100)
  2949. self.assertEqual(t.isoformat(), "00:00:00.000100")
  2950. self.assertEqual(t.isoformat(), str(t))
  2951. t = self.theclass(microsecond=1000)
  2952. self.assertEqual(t.isoformat(), "00:00:00.001000")
  2953. self.assertEqual(t.isoformat(), str(t))
  2954. t = self.theclass(microsecond=10000)
  2955. self.assertEqual(t.isoformat(), "00:00:00.010000")
  2956. self.assertEqual(t.isoformat(), str(t))
  2957. t = self.theclass(microsecond=100000)
  2958. self.assertEqual(t.isoformat(), "00:00:00.100000")
  2959. self.assertEqual(t.isoformat(), str(t))
  2960. t = self.theclass(hour=12, minute=34, second=56, microsecond=123456)
  2961. self.assertEqual(t.isoformat(timespec='hours'), "12")
  2962. self.assertEqual(t.isoformat(timespec='minutes'), "12:34")
  2963. self.assertEqual(t.isoformat(timespec='seconds'), "12:34:56")
  2964. self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.123")
  2965. self.assertEqual(t.isoformat(timespec='microseconds'), "12:34:56.123456")
  2966. self.assertEqual(t.isoformat(timespec='auto'), "12:34:56.123456")
  2967. self.assertRaises(ValueError, t.isoformat, timespec='monkey')
  2968. # bpo-34482: Check that surrogates are handled properly.
  2969. self.assertRaises(ValueError, t.isoformat, timespec='\ud800')
  2970. t = self.theclass(hour=12, minute=34, second=56, microsecond=999500)
  2971. self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.999")
  2972. t = self.theclass(hour=12, minute=34, second=56, microsecond=0)
  2973. self.assertEqual(t.isoformat(timespec='milliseconds'), "12:34:56.000")
  2974. self.assertEqual(t.isoformat(timespec='microseconds'), "12:34:56.000000")
  2975. self.assertEqual(t.isoformat(timespec='auto'), "12:34:56")
  2976. def test_isoformat_timezone(self):
  2977. tzoffsets = [
  2978. ('05:00', timedelta(hours=5)),
  2979. ('02:00', timedelta(hours=2)),
  2980. ('06:27', timedelta(hours=6, minutes=27)),
  2981. ('12:32:30', timedelta(hours=12, minutes=32, seconds=30)),
  2982. ('02:04:09.123456', timedelta(hours=2, minutes=4, seconds=9, microseconds=123456))
  2983. ]
  2984. tzinfos = [
  2985. ('', None),
  2986. ('+00:00', timezone.utc),
  2987. ('+00:00', timezone(timedelta(0))),
  2988. ]
  2989. tzinfos += [
  2990. (prefix + expected, timezone(sign * td))
  2991. for expected, td in tzoffsets
  2992. for prefix, sign in [('-', -1), ('+', 1)]
  2993. ]
  2994. t_base = self.theclass(12, 37, 9)
  2995. exp_base = '12:37:09'
  2996. for exp_tz, tzi in tzinfos:
  2997. t = t_base.replace(tzinfo=tzi)
  2998. exp = exp_base + exp_tz
  2999. with self.subTest(tzi=tzi):
  3000. assert t.isoformat() == exp
  3001. def test_1653736(self):
  3002. # verify it doesn't accept extra keyword arguments
  3003. t = self.theclass(second=1)
  3004. self.assertRaises(TypeError, t.isoformat, foo=3)
  3005. def test_strftime(self):
  3006. t = self.theclass(1, 2, 3, 4)
  3007. self.assertEqual(t.strftime('%H %M %S %f'), "01 02 03 000004")
  3008. # A naive object replaces %z and %Z with empty strings.
  3009. self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
  3010. # bpo-34482: Check that surrogates don't cause a crash.
  3011. try:
  3012. t.strftime('%H\ud800%M')
  3013. except UnicodeEncodeError:
  3014. pass
  3015. def test_format(self):
  3016. t = self.theclass(1, 2, 3, 4)
  3017. self.assertEqual(t.__format__(''), str(t))
  3018. with self.assertRaisesRegex(TypeError, 'must be str, not int'):
  3019. t.__format__(123)
  3020. # check that a derived class's __str__() gets called
  3021. class A(self.theclass):
  3022. def __str__(self):
  3023. return 'A'
  3024. a = A(1, 2, 3, 4)
  3025. self.assertEqual(a.__format__(''), 'A')
  3026. # check that a derived class's strftime gets called
  3027. class B(self.theclass):
  3028. def strftime(self, format_spec):
  3029. return 'B'
  3030. b = B(1, 2, 3, 4)
  3031. self.assertEqual(b.__format__(''), str(t))
  3032. for fmt in ['%H %M %S',
  3033. ]:
  3034. self.assertEqual(t.__format__(fmt), t.strftime(fmt))
  3035. self.assertEqual(a.__format__(fmt), t.strftime(fmt))
  3036. self.assertEqual(b.__format__(fmt), 'B')
  3037. def test_str(self):
  3038. self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")
  3039. self.assertEqual(str(self.theclass(10, 2, 3, 4000)), "10:02:03.004000")
  3040. self.assertEqual(str(self.theclass(0, 2, 3, 400000)), "00:02:03.400000")
  3041. self.assertEqual(str(self.theclass(12, 2, 3, 0)), "12:02:03")
  3042. self.assertEqual(str(self.theclass(23, 15, 0, 0)), "23:15:00")
  3043. def test_repr(self):
  3044. name = 'datetime.' + self.theclass.__name__
  3045. self.assertEqual(repr(self.theclass(1, 2, 3, 4)),
  3046. "%s(1, 2, 3, 4)" % name)
  3047. self.assertEqual(repr(self.theclass(10, 2, 3, 4000)),
  3048. "%s(10, 2, 3, 4000)" % name)
  3049. self.assertEqual(repr(self.theclass(0, 2, 3, 400000)),
  3050. "%s(0, 2, 3, 400000)" % name)
  3051. self.assertEqual(repr(self.theclass(12, 2, 3, 0)),
  3052. "%s(12, 2, 3)" % name)
  3053. self.assertEqual(repr(self.theclass(23, 15, 0, 0)),
  3054. "%s(23, 15)" % name)
  3055. def test_resolution_info(self):
  3056. self.assertIsInstance(self.theclass.min, self.theclass)
  3057. self.assertIsInstance(self.theclass.max, self.theclass)
  3058. self.assertIsInstance(self.theclass.resolution, timedelta)
  3059. self.assertTrue(self.theclass.max > self.theclass.min)
  3060. def test_pickling(self):
  3061. args = 20, 59, 16, 64**2
  3062. orig = self.theclass(*args)
  3063. for pickler, unpickler, proto in pickle_choices:
  3064. green = pickler.dumps(orig, proto)
  3065. derived = unpickler.loads(green)
  3066. self.assertEqual(orig, derived)
  3067. self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
  3068. def test_pickling_subclass_time(self):
  3069. args = 20, 59, 16, 64**2
  3070. orig = SubclassTime(*args)
  3071. for pickler, unpickler, proto in pickle_choices:
  3072. green = pickler.dumps(orig, proto)
  3073. derived = unpickler.loads(green)
  3074. self.assertEqual(orig, derived)
  3075. self.assertTrue(isinstance(derived, SubclassTime))
  3076. def test_compat_unpickle(self):
  3077. tests = [
  3078. (b"cdatetime\ntime\n(S'\\x14;\\x10\\x00\\x10\\x00'\ntR.",
  3079. (20, 59, 16, 64**2)),
  3080. (b'cdatetime\ntime\n(U\x06\x14;\x10\x00\x10\x00tR.',
  3081. (20, 59, 16, 64**2)),
  3082. (b'\x80\x02cdatetime\ntime\nU\x06\x14;\x10\x00\x10\x00\x85R.',
  3083. (20, 59, 16, 64**2)),
  3084. (b"cdatetime\ntime\n(S'\\x14;\\x19\\x00\\x10\\x00'\ntR.",
  3085. (20, 59, 25, 64**2)),
  3086. (b'cdatetime\ntime\n(U\x06\x14;\x19\x00\x10\x00tR.',
  3087. (20, 59, 25, 64**2)),
  3088. (b'\x80\x02cdatetime\ntime\nU\x06\x14;\x19\x00\x10\x00\x85R.',
  3089. (20, 59, 25, 64**2)),
  3090. ]
  3091. for i, (data, args) in enumerate(tests):
  3092. with self.subTest(i=i):
  3093. expected = self.theclass(*args)
  3094. for loads in pickle_loads:
  3095. derived = loads(data, encoding='latin1')
  3096. self.assertEqual(derived, expected)
  3097. def test_bool(self):
  3098. # time is always True.
  3099. cls = self.theclass
  3100. self.assertTrue(cls(1))
  3101. self.assertTrue(cls(0, 1))
  3102. self.assertTrue(cls(0, 0, 1))
  3103. self.assertTrue(cls(0, 0, 0, 1))
  3104. self.assertTrue(cls(0))
  3105. self.assertTrue(cls())
  3106. def test_replace(self):
  3107. cls = self.theclass
  3108. args = [1, 2, 3, 4]
  3109. base = cls(*args)
  3110. self.assertEqual(base, base.replace())
  3111. i = 0
  3112. for name, newval in (("hour", 5),
  3113. ("minute", 6),
  3114. ("second", 7),
  3115. ("microsecond", 8)):
  3116. newargs = args[:]
  3117. newargs[i] = newval
  3118. expected = cls(*newargs)
  3119. got = base.replace(**{name: newval})
  3120. self.assertEqual(expected, got)
  3121. i += 1
  3122. # Out of bounds.
  3123. base = cls(1)
  3124. self.assertRaises(ValueError, base.replace, hour=24)
  3125. self.assertRaises(ValueError, base.replace, minute=-1)
  3126. self.assertRaises(ValueError, base.replace, second=100)
  3127. self.assertRaises(ValueError, base.replace, microsecond=1000000)
  3128. def test_subclass_replace(self):
  3129. class TimeSubclass(self.theclass):
  3130. pass
  3131. ctime = TimeSubclass(12, 30)
  3132. self.assertIs(type(ctime.replace(hour=10)), TimeSubclass)
  3133. def test_subclass_time(self):
  3134. class C(self.theclass):
  3135. theAnswer = 42
  3136. def __new__(cls, *args, **kws):
  3137. temp = kws.copy()
  3138. extra = temp.pop('extra')
  3139. result = self.theclass.__new__(cls, *args, **temp)
  3140. result.extra = extra
  3141. return result
  3142. def newmeth(self, start):
  3143. return start + self.hour + self.second
  3144. args = 4, 5, 6
  3145. dt1 = self.theclass(*args)
  3146. dt2 = C(*args, **{'extra': 7})
  3147. self.assertEqual(dt2.__class__, C)
  3148. self.assertEqual(dt2.theAnswer, 42)
  3149. self.assertEqual(dt2.extra, 7)
  3150. self.assertEqual(dt1.isoformat(), dt2.isoformat())
  3151. self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
  3152. def test_backdoor_resistance(self):
  3153. # see TestDate.test_backdoor_resistance().
  3154. base = '2:59.0'
  3155. for hour_byte in ' ', '9', chr(24), '\xff':
  3156. self.assertRaises(TypeError, self.theclass,
  3157. hour_byte + base[1:])
  3158. # Good bytes, but bad tzinfo:
  3159. with self.assertRaisesRegex(TypeError, '^bad tzinfo state arg$'):
  3160. self.theclass(bytes([1] * len(base)), 'EST')
  3161. # A mixin for classes with a tzinfo= argument. Subclasses must define
  3162. # theclass as a class attribute, and theclass(1, 1, 1, tzinfo=whatever)
  3163. # must be legit (which is true for time and datetime).
  3164. class TZInfoBase:
  3165. def test_argument_passing(self):
  3166. cls = self.theclass
  3167. # A datetime passes itself on, a time passes None.
  3168. class introspective(tzinfo):
  3169. def tzname(self, dt): return dt and "real" or "none"
  3170. def utcoffset(self, dt):
  3171. return timedelta(minutes = dt and 42 or -42)
  3172. dst = utcoffset
  3173. obj = cls(1, 2, 3, tzinfo=introspective())
  3174. expected = cls is time and "none" or "real"
  3175. self.assertEqual(obj.tzname(), expected)
  3176. expected = timedelta(minutes=(cls is time and -42 or 42))
  3177. self.assertEqual(obj.utcoffset(), expected)
  3178. self.assertEqual(obj.dst(), expected)
  3179. def test_bad_tzinfo_classes(self):
  3180. cls = self.theclass
  3181. self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=12)
  3182. class NiceTry(object):
  3183. def __init__(self): pass
  3184. def utcoffset(self, dt): pass
  3185. self.assertRaises(TypeError, cls, 1, 1, 1, tzinfo=NiceTry)
  3186. class BetterTry(tzinfo):
  3187. def __init__(self): pass
  3188. def utcoffset(self, dt): pass
  3189. b = BetterTry()
  3190. t = cls(1, 1, 1, tzinfo=b)
  3191. self.assertIs(t.tzinfo, b)
  3192. def test_utc_offset_out_of_bounds(self):
  3193. class Edgy(tzinfo):
  3194. def __init__(self, offset):
  3195. self.offset = timedelta(minutes=offset)
  3196. def utcoffset(self, dt):
  3197. return self.offset
  3198. cls = self.theclass
  3199. for offset, legit in ((-1440, False),
  3200. (-1439, True),
  3201. (1439, True),
  3202. (1440, False)):
  3203. if cls is time:
  3204. t = cls(1, 2, 3, tzinfo=Edgy(offset))
  3205. elif cls is datetime:
  3206. t = cls(6, 6, 6, 1, 2, 3, tzinfo=Edgy(offset))
  3207. else:
  3208. assert 0, "impossible"
  3209. if legit:
  3210. aofs = abs(offset)
  3211. h, m = divmod(aofs, 60)
  3212. tag = "%c%02d:%02d" % (offset < 0 and '-' or '+', h, m)
  3213. if isinstance(t, datetime):
  3214. t = t.timetz()
  3215. self.assertEqual(str(t), "01:02:03" + tag)
  3216. else:
  3217. self.assertRaises(ValueError, str, t)
  3218. def test_tzinfo_classes(self):
  3219. cls = self.theclass
  3220. class C1(tzinfo):
  3221. def utcoffset(self, dt): return None
  3222. def dst(self, dt): return None
  3223. def tzname(self, dt): return None
  3224. for t in (cls(1, 1, 1),
  3225. cls(1, 1, 1, tzinfo=None),
  3226. cls(1, 1, 1, tzinfo=C1())):
  3227. self.assertIsNone(t.utcoffset())
  3228. self.assertIsNone(t.dst())
  3229. self.assertIsNone(t.tzname())
  3230. class C3(tzinfo):
  3231. def utcoffset(self, dt): return timedelta(minutes=-1439)
  3232. def dst(self, dt): return timedelta(minutes=1439)
  3233. def tzname(self, dt): return "aname"
  3234. t = cls(1, 1, 1, tzinfo=C3())
  3235. self.assertEqual(t.utcoffset(), timedelta(minutes=-1439))
  3236. self.assertEqual(t.dst(), timedelta(minutes=1439))
  3237. self.assertEqual(t.tzname(), "aname")
  3238. # Wrong types.
  3239. class C4(tzinfo):
  3240. def utcoffset(self, dt): return "aname"
  3241. def dst(self, dt): return 7
  3242. def tzname(self, dt): return 0
  3243. t = cls(1, 1, 1, tzinfo=C4())
  3244. self.assertRaises(TypeError, t.utcoffset)
  3245. self.assertRaises(TypeError, t.dst)
  3246. self.assertRaises(TypeError, t.tzname)
  3247. # Offset out of range.
  3248. class C6(tzinfo):
  3249. def utcoffset(self, dt): return timedelta(hours=-24)
  3250. def dst(self, dt): return timedelta(hours=24)
  3251. t = cls(1, 1, 1, tzinfo=C6())
  3252. self.assertRaises(ValueError, t.utcoffset)
  3253. self.assertRaises(ValueError, t.dst)
  3254. # Not a whole number of seconds.
  3255. class C7(tzinfo):
  3256. def utcoffset(self, dt): return timedelta(microseconds=61)
  3257. def dst(self, dt): return timedelta(microseconds=-81)
  3258. t = cls(1, 1, 1, tzinfo=C7())
  3259. self.assertEqual(t.utcoffset(), timedelta(microseconds=61))
  3260. self.assertEqual(t.dst(), timedelta(microseconds=-81))
  3261. def test_aware_compare(self):
  3262. cls = self.theclass
  3263. # Ensure that utcoffset() gets ignored if the comparands have
  3264. # the same tzinfo member.
  3265. class OperandDependentOffset(tzinfo):
  3266. def utcoffset(self, t):
  3267. if t.minute < 10:
  3268. # d0 and d1 equal after adjustment
  3269. return timedelta(minutes=t.minute)
  3270. else:
  3271. # d2 off in the weeds
  3272. return timedelta(minutes=59)
  3273. base = cls(8, 9, 10, tzinfo=OperandDependentOffset())
  3274. d0 = base.replace(minute=3)
  3275. d1 = base.replace(minute=9)
  3276. d2 = base.replace(minute=11)
  3277. for x in d0, d1, d2:
  3278. for y in d0, d1, d2:
  3279. for op in lt, le, gt, ge, eq, ne:
  3280. got = op(x, y)
  3281. expected = op(x.minute, y.minute)
  3282. self.assertEqual(got, expected)
  3283. # However, if they're different members, uctoffset is not ignored.
  3284. # Note that a time can't actually have an operand-dependent offset,
  3285. # though (and time.utcoffset() passes None to tzinfo.utcoffset()),
  3286. # so skip this test for time.
  3287. if cls is not time:
  3288. d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
  3289. d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
  3290. d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
  3291. for x in d0, d1, d2:
  3292. for y in d0, d1, d2:
  3293. got = (x > y) - (x < y)
  3294. if (x is d0 or x is d1) and (y is d0 or y is d1):
  3295. expected = 0
  3296. elif x is y is d2:
  3297. expected = 0
  3298. elif x is d2:
  3299. expected = -1
  3300. else:
  3301. assert y is d2
  3302. expected = 1
  3303. self.assertEqual(got, expected)
  3304. # Testing time objects with a non-None tzinfo.
  3305. class TestTimeTZ(TestTime, TZInfoBase, unittest.TestCase):
  3306. theclass = time
  3307. def test_empty(self):
  3308. t = self.theclass()
  3309. self.assertEqual(t.hour, 0)
  3310. self.assertEqual(t.minute, 0)
  3311. self.assertEqual(t.second, 0)
  3312. self.assertEqual(t.microsecond, 0)
  3313. self.assertIsNone(t.tzinfo)
  3314. def test_zones(self):
  3315. est = FixedOffset(-300, "EST", 1)
  3316. utc = FixedOffset(0, "UTC", -2)
  3317. met = FixedOffset(60, "MET", 3)
  3318. t1 = time( 7, 47, tzinfo=est)
  3319. t2 = time(12, 47, tzinfo=utc)
  3320. t3 = time(13, 47, tzinfo=met)
  3321. t4 = time(microsecond=40)
  3322. t5 = time(microsecond=40, tzinfo=utc)
  3323. self.assertEqual(t1.tzinfo, est)
  3324. self.assertEqual(t2.tzinfo, utc)
  3325. self.assertEqual(t3.tzinfo, met)
  3326. self.assertIsNone(t4.tzinfo)
  3327. self.assertEqual(t5.tzinfo, utc)
  3328. self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
  3329. self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
  3330. self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
  3331. self.assertIsNone(t4.utcoffset())
  3332. self.assertRaises(TypeError, t1.utcoffset, "no args")
  3333. self.assertEqual(t1.tzname(), "EST")
  3334. self.assertEqual(t2.tzname(), "UTC")
  3335. self.assertEqual(t3.tzname(), "MET")
  3336. self.assertIsNone(t4.tzname())
  3337. self.assertRaises(TypeError, t1.tzname, "no args")
  3338. self.assertEqual(t1.dst(), timedelta(minutes=1))
  3339. self.assertEqual(t2.dst(), timedelta(minutes=-2))
  3340. self.assertEqual(t3.dst(), timedelta(minutes=3))
  3341. self.assertIsNone(t4.dst())
  3342. self.assertRaises(TypeError, t1.dst, "no args")
  3343. self.assertEqual(hash(t1), hash(t2))
  3344. self.assertEqual(hash(t1), hash(t3))
  3345. self.assertEqual(hash(t2), hash(t3))
  3346. self.assertEqual(t1, t2)
  3347. self.assertEqual(t1, t3)
  3348. self.assertEqual(t2, t3)
  3349. self.assertNotEqual(t4, t5) # mixed tz-aware & naive
  3350. self.assertRaises(TypeError, lambda: t4 < t5) # mixed tz-aware & naive
  3351. self.assertRaises(TypeError, lambda: t5 < t4) # mixed tz-aware & naive
  3352. self.assertEqual(str(t1), "07:47:00-05:00")
  3353. self.assertEqual(str(t2), "12:47:00+00:00")
  3354. self.assertEqual(str(t3), "13:47:00+01:00")
  3355. self.assertEqual(str(t4), "00:00:00.000040")
  3356. self.assertEqual(str(t5), "00:00:00.000040+00:00")
  3357. self.assertEqual(t1.isoformat(), "07:47:00-05:00")
  3358. self.assertEqual(t2.isoformat(), "12:47:00+00:00")
  3359. self.assertEqual(t3.isoformat(), "13:47:00+01:00")
  3360. self.assertEqual(t4.isoformat(), "00:00:00.000040")
  3361. self.assertEqual(t5.isoformat(), "00:00:00.000040+00:00")
  3362. d = 'datetime.time'
  3363. self.assertEqual(repr(t1), d + "(7, 47, tzinfo=est)")
  3364. self.assertEqual(repr(t2), d + "(12, 47, tzinfo=utc)")
  3365. self.assertEqual(repr(t3), d + "(13, 47, tzinfo=met)")
  3366. self.assertEqual(repr(t4), d + "(0, 0, 0, 40)")
  3367. self.assertEqual(repr(t5), d + "(0, 0, 0, 40, tzinfo=utc)")
  3368. self.assertEqual(t1.strftime("%H:%M:%S %%Z=%Z %%z=%z"),
  3369. "07:47:00 %Z=EST %z=-0500")
  3370. self.assertEqual(t2.strftime("%H:%M:%S %Z %z"), "12:47:00 UTC +0000")
  3371. self.assertEqual(t3.strftime("%H:%M:%S %Z %z"), "13:47:00 MET +0100")
  3372. yuck = FixedOffset(-1439, "%z %Z %%z%%Z")
  3373. t1 = time(23, 59, tzinfo=yuck)
  3374. self.assertEqual(t1.strftime("%H:%M %%Z='%Z' %%z='%z'"),
  3375. "23:59 %Z='%z %Z %%z%%Z' %z='-2359'")
  3376. # Check that an invalid tzname result raises an exception.
  3377. class Badtzname(tzinfo):
  3378. tz = 42
  3379. def tzname(self, dt): return self.tz
  3380. t = time(2, 3, 4, tzinfo=Badtzname())
  3381. self.assertEqual(t.strftime("%H:%M:%S"), "02:03:04")
  3382. self.assertRaises(TypeError, t.strftime, "%Z")
  3383. # Issue #6697:
  3384. if '_Fast' in self.__class__.__name__:
  3385. Badtzname.tz = '\ud800'
  3386. self.assertRaises(ValueError, t.strftime, "%Z")
  3387. def test_hash_edge_cases(self):
  3388. # Offsets that overflow a basic time.
  3389. t1 = self.theclass(0, 1, 2, 3, tzinfo=FixedOffset(1439, ""))
  3390. t2 = self.theclass(0, 0, 2, 3, tzinfo=FixedOffset(1438, ""))
  3391. self.assertEqual(hash(t1), hash(t2))
  3392. t1 = self.theclass(23, 58, 6, 100, tzinfo=FixedOffset(-1000, ""))
  3393. t2 = self.theclass(23, 48, 6, 100, tzinfo=FixedOffset(-1010, ""))
  3394. self.assertEqual(hash(t1), hash(t2))
  3395. def test_pickling(self):
  3396. # Try one without a tzinfo.
  3397. args = 20, 59, 16, 64**2
  3398. orig = self.theclass(*args)
  3399. for pickler, unpickler, proto in pickle_choices:
  3400. green = pickler.dumps(orig, proto)
  3401. derived = unpickler.loads(green)
  3402. self.assertEqual(orig, derived)
  3403. self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
  3404. # Try one with a tzinfo.
  3405. tinfo = PicklableFixedOffset(-300, 'cookie')
  3406. orig = self.theclass(5, 6, 7, tzinfo=tinfo)
  3407. for pickler, unpickler, proto in pickle_choices:
  3408. green = pickler.dumps(orig, proto)
  3409. derived = unpickler.loads(green)
  3410. self.assertEqual(orig, derived)
  3411. self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
  3412. self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
  3413. self.assertEqual(derived.tzname(), 'cookie')
  3414. self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
  3415. def test_compat_unpickle(self):
  3416. tests = [
  3417. b"cdatetime\ntime\n(S'\\x05\\x06\\x07\\x01\\xe2@'\n"
  3418. b"ctest.datetimetester\nPicklableFixedOffset\n(tR"
  3419. b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n"
  3420. b"(I-1\nI68400\nI0\ntRs"
  3421. b"S'_FixedOffset__dstoffset'\nNs"
  3422. b"S'_FixedOffset__name'\nS'cookie'\nsbtR.",
  3423. b'cdatetime\ntime\n(U\x06\x05\x06\x07\x01\xe2@'
  3424. b'ctest.datetimetester\nPicklableFixedOffset\n)R'
  3425. b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n'
  3426. b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR'
  3427. b'U\x17_FixedOffset__dstoffsetN'
  3428. b'U\x12_FixedOffset__nameU\x06cookieubtR.',
  3429. b'\x80\x02cdatetime\ntime\nU\x06\x05\x06\x07\x01\xe2@'
  3430. b'ctest.datetimetester\nPicklableFixedOffset\n)R'
  3431. b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n'
  3432. b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R'
  3433. b'U\x17_FixedOffset__dstoffsetN'
  3434. b'U\x12_FixedOffset__nameU\x06cookieub\x86R.',
  3435. ]
  3436. tinfo = PicklableFixedOffset(-300, 'cookie')
  3437. expected = self.theclass(5, 6, 7, 123456, tzinfo=tinfo)
  3438. for data in tests:
  3439. for loads in pickle_loads:
  3440. derived = loads(data, encoding='latin1')
  3441. self.assertEqual(derived, expected, repr(data))
  3442. self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
  3443. self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
  3444. self.assertEqual(derived.tzname(), 'cookie')
  3445. def test_more_bool(self):
  3446. # time is always True.
  3447. cls = self.theclass
  3448. t = cls(0, tzinfo=FixedOffset(-300, ""))
  3449. self.assertTrue(t)
  3450. t = cls(5, tzinfo=FixedOffset(-300, ""))
  3451. self.assertTrue(t)
  3452. t = cls(5, tzinfo=FixedOffset(300, ""))
  3453. self.assertTrue(t)
  3454. t = cls(23, 59, tzinfo=FixedOffset(23*60 + 59, ""))
  3455. self.assertTrue(t)
  3456. def test_replace(self):
  3457. cls = self.theclass
  3458. z100 = FixedOffset(100, "+100")
  3459. zm200 = FixedOffset(timedelta(minutes=-200), "-200")
  3460. args = [1, 2, 3, 4, z100]
  3461. base = cls(*args)
  3462. self.assertEqual(base, base.replace())
  3463. i = 0
  3464. for name, newval in (("hour", 5),
  3465. ("minute", 6),
  3466. ("second", 7),
  3467. ("microsecond", 8),
  3468. ("tzinfo", zm200)):
  3469. newargs = args[:]
  3470. newargs[i] = newval
  3471. expected = cls(*newargs)
  3472. got = base.replace(**{name: newval})
  3473. self.assertEqual(expected, got)
  3474. i += 1
  3475. # Ensure we can get rid of a tzinfo.
  3476. self.assertEqual(base.tzname(), "+100")
  3477. base2 = base.replace(tzinfo=None)
  3478. self.assertIsNone(base2.tzinfo)
  3479. self.assertIsNone(base2.tzname())
  3480. # Ensure we can add one.
  3481. base3 = base2.replace(tzinfo=z100)
  3482. self.assertEqual(base, base3)
  3483. self.assertIs(base.tzinfo, base3.tzinfo)
  3484. # Out of bounds.
  3485. base = cls(1)
  3486. self.assertRaises(ValueError, base.replace, hour=24)
  3487. self.assertRaises(ValueError, base.replace, minute=-1)
  3488. self.assertRaises(ValueError, base.replace, second=100)
  3489. self.assertRaises(ValueError, base.replace, microsecond=1000000)
  3490. def test_mixed_compare(self):
  3491. t1 = self.theclass(1, 2, 3)
  3492. t2 = self.theclass(1, 2, 3)
  3493. self.assertEqual(t1, t2)
  3494. t2 = t2.replace(tzinfo=None)
  3495. self.assertEqual(t1, t2)
  3496. t2 = t2.replace(tzinfo=FixedOffset(None, ""))
  3497. self.assertEqual(t1, t2)
  3498. t2 = t2.replace(tzinfo=FixedOffset(0, ""))
  3499. self.assertNotEqual(t1, t2)
  3500. # In time w/ identical tzinfo objects, utcoffset is ignored.
  3501. class Varies(tzinfo):
  3502. def __init__(self):
  3503. self.offset = timedelta(minutes=22)
  3504. def utcoffset(self, t):
  3505. self.offset += timedelta(minutes=1)
  3506. return self.offset
  3507. v = Varies()
  3508. t1 = t2.replace(tzinfo=v)
  3509. t2 = t2.replace(tzinfo=v)
  3510. self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
  3511. self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
  3512. self.assertEqual(t1, t2)
  3513. # But if they're not identical, it isn't ignored.
  3514. t2 = t2.replace(tzinfo=Varies())
  3515. self.assertTrue(t1 < t2) # t1's offset counter still going up
  3516. def test_fromisoformat(self):
  3517. time_examples = [
  3518. (0, 0, 0, 0),
  3519. (23, 59, 59, 999999),
  3520. ]
  3521. hh = (9, 12, 20)
  3522. mm = (5, 30)
  3523. ss = (4, 45)
  3524. usec = (0, 245000, 678901)
  3525. time_examples += list(itertools.product(hh, mm, ss, usec))
  3526. tzinfos = [None, timezone.utc,
  3527. timezone(timedelta(hours=2)),
  3528. timezone(timedelta(hours=6, minutes=27))]
  3529. for ttup in time_examples:
  3530. for tzi in tzinfos:
  3531. t = self.theclass(*ttup, tzinfo=tzi)
  3532. tstr = t.isoformat()
  3533. with self.subTest(tstr=tstr):
  3534. t_rt = self.theclass.fromisoformat(tstr)
  3535. self.assertEqual(t, t_rt)
  3536. def test_fromisoformat_timezone(self):
  3537. base_time = self.theclass(12, 30, 45, 217456)
  3538. tzoffsets = [
  3539. timedelta(hours=5), timedelta(hours=2),
  3540. timedelta(hours=6, minutes=27),
  3541. timedelta(hours=12, minutes=32, seconds=30),
  3542. timedelta(hours=2, minutes=4, seconds=9, microseconds=123456)
  3543. ]
  3544. tzoffsets += [-1 * td for td in tzoffsets]
  3545. tzinfos = [None, timezone.utc,
  3546. timezone(timedelta(hours=0))]
  3547. tzinfos += [timezone(td) for td in tzoffsets]
  3548. for tzi in tzinfos:
  3549. t = base_time.replace(tzinfo=tzi)
  3550. tstr = t.isoformat()
  3551. with self.subTest(tstr=tstr):
  3552. t_rt = self.theclass.fromisoformat(tstr)
  3553. assert t == t_rt, t_rt
  3554. def test_fromisoformat_timespecs(self):
  3555. time_bases = [
  3556. (8, 17, 45, 123456),
  3557. (8, 17, 45, 0)
  3558. ]
  3559. tzinfos = [None, timezone.utc,
  3560. timezone(timedelta(hours=-5)),
  3561. timezone(timedelta(hours=2)),
  3562. timezone(timedelta(hours=6, minutes=27))]
  3563. timespecs = ['hours', 'minutes', 'seconds',
  3564. 'milliseconds', 'microseconds']
  3565. for ip, ts in enumerate(timespecs):
  3566. for tzi in tzinfos:
  3567. for t_tuple in time_bases:
  3568. if ts == 'milliseconds':
  3569. new_microseconds = 1000 * (t_tuple[-1] // 1000)
  3570. t_tuple = t_tuple[0:-1] + (new_microseconds,)
  3571. t = self.theclass(*(t_tuple[0:(1 + ip)]), tzinfo=tzi)
  3572. tstr = t.isoformat(timespec=ts)
  3573. with self.subTest(tstr=tstr):
  3574. t_rt = self.theclass.fromisoformat(tstr)
  3575. self.assertEqual(t, t_rt)
  3576. def test_fromisoformat_fractions(self):
  3577. strs = [
  3578. ('12:30:45.1', (12, 30, 45, 100000)),
  3579. ('12:30:45.12', (12, 30, 45, 120000)),
  3580. ('12:30:45.123', (12, 30, 45, 123000)),
  3581. ('12:30:45.1234', (12, 30, 45, 123400)),
  3582. ('12:30:45.12345', (12, 30, 45, 123450)),
  3583. ('12:30:45.123456', (12, 30, 45, 123456)),
  3584. ('12:30:45.1234567', (12, 30, 45, 123456)),
  3585. ('12:30:45.12345678', (12, 30, 45, 123456)),
  3586. ]
  3587. for time_str, time_comps in strs:
  3588. expected = self.theclass(*time_comps)
  3589. actual = self.theclass.fromisoformat(time_str)
  3590. self.assertEqual(actual, expected)
  3591. def test_fromisoformat_time_examples(self):
  3592. examples = [
  3593. ('0000', self.theclass(0, 0)),
  3594. ('00:00', self.theclass(0, 0)),
  3595. ('000000', self.theclass(0, 0)),
  3596. ('00:00:00', self.theclass(0, 0)),
  3597. ('000000.0', self.theclass(0, 0)),
  3598. ('00:00:00.0', self.theclass(0, 0)),
  3599. ('000000.000', self.theclass(0, 0)),
  3600. ('00:00:00.000', self.theclass(0, 0)),
  3601. ('000000.000000', self.theclass(0, 0)),
  3602. ('00:00:00.000000', self.theclass(0, 0)),
  3603. ('1200', self.theclass(12, 0)),
  3604. ('12:00', self.theclass(12, 0)),
  3605. ('120000', self.theclass(12, 0)),
  3606. ('12:00:00', self.theclass(12, 0)),
  3607. ('120000.0', self.theclass(12, 0)),
  3608. ('12:00:00.0', self.theclass(12, 0)),
  3609. ('120000.000', self.theclass(12, 0)),
  3610. ('12:00:00.000', self.theclass(12, 0)),
  3611. ('120000.000000', self.theclass(12, 0)),
  3612. ('12:00:00.000000', self.theclass(12, 0)),
  3613. ('2359', self.theclass(23, 59)),
  3614. ('23:59', self.theclass(23, 59)),
  3615. ('235959', self.theclass(23, 59, 59)),
  3616. ('23:59:59', self.theclass(23, 59, 59)),
  3617. ('235959.9', self.theclass(23, 59, 59, 900000)),
  3618. ('23:59:59.9', self.theclass(23, 59, 59, 900000)),
  3619. ('235959.999', self.theclass(23, 59, 59, 999000)),
  3620. ('23:59:59.999', self.theclass(23, 59, 59, 999000)),
  3621. ('235959.999999', self.theclass(23, 59, 59, 999999)),
  3622. ('23:59:59.999999', self.theclass(23, 59, 59, 999999)),
  3623. ('00:00:00Z', self.theclass(0, 0, tzinfo=timezone.utc)),
  3624. ('12:00:00+0000', self.theclass(12, 0, tzinfo=timezone.utc)),
  3625. ('12:00:00+00:00', self.theclass(12, 0, tzinfo=timezone.utc)),
  3626. ('00:00:00+05',
  3627. self.theclass(0, 0, tzinfo=timezone(timedelta(hours=5)))),
  3628. ('00:00:00+05:30',
  3629. self.theclass(0, 0, tzinfo=timezone(timedelta(hours=5, minutes=30)))),
  3630. ('12:00:00-05:00',
  3631. self.theclass(12, 0, tzinfo=timezone(timedelta(hours=-5)))),
  3632. ('12:00:00-0500',
  3633. self.theclass(12, 0, tzinfo=timezone(timedelta(hours=-5)))),
  3634. ('00:00:00,000-23:59:59.999999',
  3635. self.theclass(0, 0, tzinfo=timezone(-timedelta(hours=23, minutes=59, seconds=59, microseconds=999999)))),
  3636. ]
  3637. for input_str, expected in examples:
  3638. with self.subTest(input_str=input_str):
  3639. actual = self.theclass.fromisoformat(input_str)
  3640. self.assertEqual(actual, expected)
  3641. def test_fromisoformat_fails(self):
  3642. bad_strs = [
  3643. '', # Empty string
  3644. '12\ud80000', # Invalid separator - surrogate char
  3645. '12:', # Ends on a separator
  3646. '12:30:', # Ends on a separator
  3647. '12:30:15.', # Ends on a separator
  3648. '1', # Incomplete hours
  3649. '12:3', # Incomplete minutes
  3650. '12:30:1', # Incomplete seconds
  3651. '1a:30:45.334034', # Invalid character in hours
  3652. '12:a0:45.334034', # Invalid character in minutes
  3653. '12:30:a5.334034', # Invalid character in seconds
  3654. '12:30:45.123456+24:30', # Invalid time zone offset
  3655. '12:30:45.123456-24:30', # Invalid negative offset
  3656. '12:30:45', # Uses full-width unicode colons
  3657. '12:30:45.123456a', # Non-numeric data after 6 components
  3658. '12:30:45.123456789a', # Non-numeric data after 9 components
  3659. '12:30:45․123456', # Uses \u2024 in place of decimal point
  3660. '12:30:45a', # Extra at tend of basic time
  3661. '12:30:45.123a', # Extra at end of millisecond time
  3662. '12:30:45.123456a', # Extra at end of microsecond time
  3663. '12:30:45.123456-', # Extra at end of microsecond time
  3664. '12:30:45.123456+', # Extra at end of microsecond time
  3665. '12:30:45.123456+12:00:30a', # Extra at end of full time
  3666. ]
  3667. for bad_str in bad_strs:
  3668. with self.subTest(bad_str=bad_str):
  3669. with self.assertRaises(ValueError):
  3670. self.theclass.fromisoformat(bad_str)
  3671. def test_fromisoformat_fails_typeerror(self):
  3672. # Test the fromisoformat fails when passed the wrong type
  3673. bad_types = [b'12:30:45', None, io.StringIO('12:30:45')]
  3674. for bad_type in bad_types:
  3675. with self.assertRaises(TypeError):
  3676. self.theclass.fromisoformat(bad_type)
  3677. def test_fromisoformat_subclass(self):
  3678. class TimeSubclass(self.theclass):
  3679. pass
  3680. tsc = TimeSubclass(12, 14, 45, 203745, tzinfo=timezone.utc)
  3681. tsc_rt = TimeSubclass.fromisoformat(tsc.isoformat())
  3682. self.assertEqual(tsc, tsc_rt)
  3683. self.assertIsInstance(tsc_rt, TimeSubclass)
  3684. def test_subclass_timetz(self):
  3685. class C(self.theclass):
  3686. theAnswer = 42
  3687. def __new__(cls, *args, **kws):
  3688. temp = kws.copy()
  3689. extra = temp.pop('extra')
  3690. result = self.theclass.__new__(cls, *args, **temp)
  3691. result.extra = extra
  3692. return result
  3693. def newmeth(self, start):
  3694. return start + self.hour + self.second
  3695. args = 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
  3696. dt1 = self.theclass(*args)
  3697. dt2 = C(*args, **{'extra': 7})
  3698. self.assertEqual(dt2.__class__, C)
  3699. self.assertEqual(dt2.theAnswer, 42)
  3700. self.assertEqual(dt2.extra, 7)
  3701. self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
  3702. self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.second - 7)
  3703. # Testing datetime objects with a non-None tzinfo.
  3704. class TestDateTimeTZ(TestDateTime, TZInfoBase, unittest.TestCase):
  3705. theclass = datetime
  3706. def test_trivial(self):
  3707. dt = self.theclass(1, 2, 3, 4, 5, 6, 7)
  3708. self.assertEqual(dt.year, 1)
  3709. self.assertEqual(dt.month, 2)
  3710. self.assertEqual(dt.day, 3)
  3711. self.assertEqual(dt.hour, 4)
  3712. self.assertEqual(dt.minute, 5)
  3713. self.assertEqual(dt.second, 6)
  3714. self.assertEqual(dt.microsecond, 7)
  3715. self.assertEqual(dt.tzinfo, None)
  3716. def test_even_more_compare(self):
  3717. # The test_compare() and test_more_compare() inherited from TestDate
  3718. # and TestDateTime covered non-tzinfo cases.
  3719. # Smallest possible after UTC adjustment.
  3720. t1 = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
  3721. # Largest possible after UTC adjustment.
  3722. t2 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
  3723. tzinfo=FixedOffset(-1439, ""))
  3724. # Make sure those compare correctly, and w/o overflow.
  3725. self.assertTrue(t1 < t2)
  3726. self.assertTrue(t1 != t2)
  3727. self.assertTrue(t2 > t1)
  3728. self.assertEqual(t1, t1)
  3729. self.assertEqual(t2, t2)
  3730. # Equal after adjustment.
  3731. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""))
  3732. t2 = self.theclass(2, 1, 1, 3, 13, tzinfo=FixedOffset(3*60+13+2, ""))
  3733. self.assertEqual(t1, t2)
  3734. # Change t1 not to subtract a minute, and t1 should be larger.
  3735. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(0, ""))
  3736. self.assertTrue(t1 > t2)
  3737. # Change t1 to subtract 2 minutes, and t1 should be smaller.
  3738. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(2, ""))
  3739. self.assertTrue(t1 < t2)
  3740. # Back to the original t1, but make seconds resolve it.
  3741. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
  3742. second=1)
  3743. self.assertTrue(t1 > t2)
  3744. # Likewise, but make microseconds resolve it.
  3745. t1 = self.theclass(1, 12, 31, 23, 59, tzinfo=FixedOffset(1, ""),
  3746. microsecond=1)
  3747. self.assertTrue(t1 > t2)
  3748. # Make t2 naive and it should differ.
  3749. t2 = self.theclass.min
  3750. self.assertNotEqual(t1, t2)
  3751. self.assertEqual(t2, t2)
  3752. # and > comparison should fail
  3753. with self.assertRaises(TypeError):
  3754. t1 > t2
  3755. # It's also naive if it has tzinfo but tzinfo.utcoffset() is None.
  3756. class Naive(tzinfo):
  3757. def utcoffset(self, dt): return None
  3758. t2 = self.theclass(5, 6, 7, tzinfo=Naive())
  3759. self.assertNotEqual(t1, t2)
  3760. self.assertEqual(t2, t2)
  3761. # OTOH, it's OK to compare two of these mixing the two ways of being
  3762. # naive.
  3763. t1 = self.theclass(5, 6, 7)
  3764. self.assertEqual(t1, t2)
  3765. # Try a bogus uctoffset.
  3766. class Bogus(tzinfo):
  3767. def utcoffset(self, dt):
  3768. return timedelta(minutes=1440) # out of bounds
  3769. t1 = self.theclass(2, 2, 2, tzinfo=Bogus())
  3770. t2 = self.theclass(2, 2, 2, tzinfo=FixedOffset(0, ""))
  3771. self.assertRaises(ValueError, lambda: t1 == t2)
  3772. def test_pickling(self):
  3773. # Try one without a tzinfo.
  3774. args = 6, 7, 23, 20, 59, 1, 64**2
  3775. orig = self.theclass(*args)
  3776. for pickler, unpickler, proto in pickle_choices:
  3777. green = pickler.dumps(orig, proto)
  3778. derived = unpickler.loads(green)
  3779. self.assertEqual(orig, derived)
  3780. self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
  3781. # Try one with a tzinfo.
  3782. tinfo = PicklableFixedOffset(-300, 'cookie')
  3783. orig = self.theclass(*args, **{'tzinfo': tinfo})
  3784. derived = self.theclass(1, 1, 1, tzinfo=FixedOffset(0, "", 0))
  3785. for pickler, unpickler, proto in pickle_choices:
  3786. green = pickler.dumps(orig, proto)
  3787. derived = unpickler.loads(green)
  3788. self.assertEqual(orig, derived)
  3789. self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
  3790. self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
  3791. self.assertEqual(derived.tzname(), 'cookie')
  3792. self.assertEqual(orig.__reduce__(), orig.__reduce_ex__(2))
  3793. def test_compat_unpickle(self):
  3794. tests = [
  3795. b'cdatetime\ndatetime\n'
  3796. b"(S'\\x07\\xdf\\x0b\\x1b\\x14;\\x01\\x01\\xe2@'\n"
  3797. b'ctest.datetimetester\nPicklableFixedOffset\n(tR'
  3798. b"(dS'_FixedOffset__offset'\ncdatetime\ntimedelta\n"
  3799. b'(I-1\nI68400\nI0\ntRs'
  3800. b"S'_FixedOffset__dstoffset'\nNs"
  3801. b"S'_FixedOffset__name'\nS'cookie'\nsbtR.",
  3802. b'cdatetime\ndatetime\n'
  3803. b'(U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@'
  3804. b'ctest.datetimetester\nPicklableFixedOffset\n)R'
  3805. b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n'
  3806. b'(J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00tR'
  3807. b'U\x17_FixedOffset__dstoffsetN'
  3808. b'U\x12_FixedOffset__nameU\x06cookieubtR.',
  3809. b'\x80\x02cdatetime\ndatetime\n'
  3810. b'U\n\x07\xdf\x0b\x1b\x14;\x01\x01\xe2@'
  3811. b'ctest.datetimetester\nPicklableFixedOffset\n)R'
  3812. b'}(U\x14_FixedOffset__offsetcdatetime\ntimedelta\n'
  3813. b'J\xff\xff\xff\xffJ0\x0b\x01\x00K\x00\x87R'
  3814. b'U\x17_FixedOffset__dstoffsetN'
  3815. b'U\x12_FixedOffset__nameU\x06cookieub\x86R.',
  3816. ]
  3817. args = 2015, 11, 27, 20, 59, 1, 123456
  3818. tinfo = PicklableFixedOffset(-300, 'cookie')
  3819. expected = self.theclass(*args, **{'tzinfo': tinfo})
  3820. for data in tests:
  3821. for loads in pickle_loads:
  3822. derived = loads(data, encoding='latin1')
  3823. self.assertEqual(derived, expected)
  3824. self.assertIsInstance(derived.tzinfo, PicklableFixedOffset)
  3825. self.assertEqual(derived.utcoffset(), timedelta(minutes=-300))
  3826. self.assertEqual(derived.tzname(), 'cookie')
  3827. def test_extreme_hashes(self):
  3828. # If an attempt is made to hash these via subtracting the offset
  3829. # then hashing a datetime object, OverflowError results. The
  3830. # Python implementation used to blow up here.
  3831. t = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, ""))
  3832. hash(t)
  3833. t = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
  3834. tzinfo=FixedOffset(-1439, ""))
  3835. hash(t)
  3836. # OTOH, an OOB offset should blow up.
  3837. t = self.theclass(5, 5, 5, tzinfo=FixedOffset(-1440, ""))
  3838. self.assertRaises(ValueError, hash, t)
  3839. def test_zones(self):
  3840. est = FixedOffset(-300, "EST")
  3841. utc = FixedOffset(0, "UTC")
  3842. met = FixedOffset(60, "MET")
  3843. t1 = datetime(2002, 3, 19, 7, 47, tzinfo=est)
  3844. t2 = datetime(2002, 3, 19, 12, 47, tzinfo=utc)
  3845. t3 = datetime(2002, 3, 19, 13, 47, tzinfo=met)
  3846. self.assertEqual(t1.tzinfo, est)
  3847. self.assertEqual(t2.tzinfo, utc)
  3848. self.assertEqual(t3.tzinfo, met)
  3849. self.assertEqual(t1.utcoffset(), timedelta(minutes=-300))
  3850. self.assertEqual(t2.utcoffset(), timedelta(minutes=0))
  3851. self.assertEqual(t3.utcoffset(), timedelta(minutes=60))
  3852. self.assertEqual(t1.tzname(), "EST")
  3853. self.assertEqual(t2.tzname(), "UTC")
  3854. self.assertEqual(t3.tzname(), "MET")
  3855. self.assertEqual(hash(t1), hash(t2))
  3856. self.assertEqual(hash(t1), hash(t3))
  3857. self.assertEqual(hash(t2), hash(t3))
  3858. self.assertEqual(t1, t2)
  3859. self.assertEqual(t1, t3)
  3860. self.assertEqual(t2, t3)
  3861. self.assertEqual(str(t1), "2002-03-19 07:47:00-05:00")
  3862. self.assertEqual(str(t2), "2002-03-19 12:47:00+00:00")
  3863. self.assertEqual(str(t3), "2002-03-19 13:47:00+01:00")
  3864. d = 'datetime.datetime(2002, 3, 19, '
  3865. self.assertEqual(repr(t1), d + "7, 47, tzinfo=est)")
  3866. self.assertEqual(repr(t2), d + "12, 47, tzinfo=utc)")
  3867. self.assertEqual(repr(t3), d + "13, 47, tzinfo=met)")
  3868. def test_combine(self):
  3869. met = FixedOffset(60, "MET")
  3870. d = date(2002, 3, 4)
  3871. tz = time(18, 45, 3, 1234, tzinfo=met)
  3872. dt = datetime.combine(d, tz)
  3873. self.assertEqual(dt, datetime(2002, 3, 4, 18, 45, 3, 1234,
  3874. tzinfo=met))
  3875. def test_extract(self):
  3876. met = FixedOffset(60, "MET")
  3877. dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234, tzinfo=met)
  3878. self.assertEqual(dt.date(), date(2002, 3, 4))
  3879. self.assertEqual(dt.time(), time(18, 45, 3, 1234))
  3880. self.assertEqual(dt.timetz(), time(18, 45, 3, 1234, tzinfo=met))
  3881. def test_tz_aware_arithmetic(self):
  3882. now = self.theclass.now()
  3883. tz55 = FixedOffset(-330, "west 5:30")
  3884. timeaware = now.time().replace(tzinfo=tz55)
  3885. nowaware = self.theclass.combine(now.date(), timeaware)
  3886. self.assertIs(nowaware.tzinfo, tz55)
  3887. self.assertEqual(nowaware.timetz(), timeaware)
  3888. # Can't mix aware and non-aware.
  3889. self.assertRaises(TypeError, lambda: now - nowaware)
  3890. self.assertRaises(TypeError, lambda: nowaware - now)
  3891. # And adding datetime's doesn't make sense, aware or not.
  3892. self.assertRaises(TypeError, lambda: now + nowaware)
  3893. self.assertRaises(TypeError, lambda: nowaware + now)
  3894. self.assertRaises(TypeError, lambda: nowaware + nowaware)
  3895. # Subtracting should yield 0.
  3896. self.assertEqual(now - now, timedelta(0))
  3897. self.assertEqual(nowaware - nowaware, timedelta(0))
  3898. # Adding a delta should preserve tzinfo.
  3899. delta = timedelta(weeks=1, minutes=12, microseconds=5678)
  3900. nowawareplus = nowaware + delta
  3901. self.assertIs(nowaware.tzinfo, tz55)
  3902. nowawareplus2 = delta + nowaware
  3903. self.assertIs(nowawareplus2.tzinfo, tz55)
  3904. self.assertEqual(nowawareplus, nowawareplus2)
  3905. # that - delta should be what we started with, and that - what we
  3906. # started with should be delta.
  3907. diff = nowawareplus - delta
  3908. self.assertIs(diff.tzinfo, tz55)
  3909. self.assertEqual(nowaware, diff)
  3910. self.assertRaises(TypeError, lambda: delta - nowawareplus)
  3911. self.assertEqual(nowawareplus - nowaware, delta)
  3912. # Make up a random timezone.
  3913. tzr = FixedOffset(random.randrange(-1439, 1440), "randomtimezone")
  3914. # Attach it to nowawareplus.
  3915. nowawareplus = nowawareplus.replace(tzinfo=tzr)
  3916. self.assertIs(nowawareplus.tzinfo, tzr)
  3917. # Make sure the difference takes the timezone adjustments into account.
  3918. got = nowaware - nowawareplus
  3919. # Expected: (nowaware base - nowaware offset) -
  3920. # (nowawareplus base - nowawareplus offset) =
  3921. # (nowaware base - nowawareplus base) +
  3922. # (nowawareplus offset - nowaware offset) =
  3923. # -delta + nowawareplus offset - nowaware offset
  3924. expected = nowawareplus.utcoffset() - nowaware.utcoffset() - delta
  3925. self.assertEqual(got, expected)
  3926. # Try max possible difference.
  3927. min = self.theclass(1, 1, 1, tzinfo=FixedOffset(1439, "min"))
  3928. max = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999999,
  3929. tzinfo=FixedOffset(-1439, "max"))
  3930. maxdiff = max - min
  3931. self.assertEqual(maxdiff, self.theclass.max - self.theclass.min +
  3932. timedelta(minutes=2*1439))
  3933. # Different tzinfo, but the same offset
  3934. tza = timezone(HOUR, 'A')
  3935. tzb = timezone(HOUR, 'B')
  3936. delta = min.replace(tzinfo=tza) - max.replace(tzinfo=tzb)
  3937. self.assertEqual(delta, self.theclass.min - self.theclass.max)
  3938. def test_tzinfo_now(self):
  3939. meth = self.theclass.now
  3940. # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
  3941. base = meth()
  3942. # Try with and without naming the keyword.
  3943. off42 = FixedOffset(42, "42")
  3944. another = meth(off42)
  3945. again = meth(tz=off42)
  3946. self.assertIs(another.tzinfo, again.tzinfo)
  3947. self.assertEqual(another.utcoffset(), timedelta(minutes=42))
  3948. # Bad argument with and w/o naming the keyword.
  3949. self.assertRaises(TypeError, meth, 16)
  3950. self.assertRaises(TypeError, meth, tzinfo=16)
  3951. # Bad keyword name.
  3952. self.assertRaises(TypeError, meth, tinfo=off42)
  3953. # Too many args.
  3954. self.assertRaises(TypeError, meth, off42, off42)
  3955. # We don't know which time zone we're in, and don't have a tzinfo
  3956. # class to represent it, so seeing whether a tz argument actually
  3957. # does a conversion is tricky.
  3958. utc = FixedOffset(0, "utc", 0)
  3959. for weirdtz in [FixedOffset(timedelta(hours=15, minutes=58), "weirdtz", 0),
  3960. timezone(timedelta(hours=15, minutes=58), "weirdtz"),]:
  3961. for dummy in range(3):
  3962. now = datetime.now(weirdtz)
  3963. self.assertIs(now.tzinfo, weirdtz)
  3964. utcnow = datetime.utcnow().replace(tzinfo=utc)
  3965. now2 = utcnow.astimezone(weirdtz)
  3966. if abs(now - now2) < timedelta(seconds=30):
  3967. break
  3968. # Else the code is broken, or more than 30 seconds passed between
  3969. # calls; assuming the latter, just try again.
  3970. else:
  3971. # Three strikes and we're out.
  3972. self.fail("utcnow(), now(tz), or astimezone() may be broken")
  3973. def test_tzinfo_fromtimestamp(self):
  3974. import time
  3975. meth = self.theclass.fromtimestamp
  3976. ts = time.time()
  3977. # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
  3978. base = meth(ts)
  3979. # Try with and without naming the keyword.
  3980. off42 = FixedOffset(42, "42")
  3981. another = meth(ts, off42)
  3982. again = meth(ts, tz=off42)
  3983. self.assertIs(another.tzinfo, again.tzinfo)
  3984. self.assertEqual(another.utcoffset(), timedelta(minutes=42))
  3985. # Bad argument with and w/o naming the keyword.
  3986. self.assertRaises(TypeError, meth, ts, 16)
  3987. self.assertRaises(TypeError, meth, ts, tzinfo=16)
  3988. # Bad keyword name.
  3989. self.assertRaises(TypeError, meth, ts, tinfo=off42)
  3990. # Too many args.
  3991. self.assertRaises(TypeError, meth, ts, off42, off42)
  3992. # Too few args.
  3993. self.assertRaises(TypeError, meth)
  3994. # Try to make sure tz= actually does some conversion.
  3995. timestamp = 1000000000
  3996. utcdatetime = datetime.utcfromtimestamp(timestamp)
  3997. # In POSIX (epoch 1970), that's 2001-09-09 01:46:40 UTC, give or take.
  3998. # But on some flavor of Mac, it's nowhere near that. So we can't have
  3999. # any idea here what time that actually is, we can only test that
  4000. # relative changes match.
  4001. utcoffset = timedelta(hours=-15, minutes=39) # arbitrary, but not zero
  4002. tz = FixedOffset(utcoffset, "tz", 0)
  4003. expected = utcdatetime + utcoffset
  4004. got = datetime.fromtimestamp(timestamp, tz)
  4005. self.assertEqual(expected, got.replace(tzinfo=None))
  4006. def test_tzinfo_utcnow(self):
  4007. meth = self.theclass.utcnow
  4008. # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
  4009. base = meth()
  4010. # Try with and without naming the keyword; for whatever reason,
  4011. # utcnow() doesn't accept a tzinfo argument.
  4012. off42 = FixedOffset(42, "42")
  4013. self.assertRaises(TypeError, meth, off42)
  4014. self.assertRaises(TypeError, meth, tzinfo=off42)
  4015. def test_tzinfo_utcfromtimestamp(self):
  4016. import time
  4017. meth = self.theclass.utcfromtimestamp
  4018. ts = time.time()
  4019. # Ensure it doesn't require tzinfo (i.e., that this doesn't blow up).
  4020. base = meth(ts)
  4021. # Try with and without naming the keyword; for whatever reason,
  4022. # utcfromtimestamp() doesn't accept a tzinfo argument.
  4023. off42 = FixedOffset(42, "42")
  4024. self.assertRaises(TypeError, meth, ts, off42)
  4025. self.assertRaises(TypeError, meth, ts, tzinfo=off42)
  4026. def test_tzinfo_timetuple(self):
  4027. # TestDateTime tested most of this. datetime adds a twist to the
  4028. # DST flag.
  4029. class DST(tzinfo):
  4030. def __init__(self, dstvalue):
  4031. if isinstance(dstvalue, int):
  4032. dstvalue = timedelta(minutes=dstvalue)
  4033. self.dstvalue = dstvalue
  4034. def dst(self, dt):
  4035. return self.dstvalue
  4036. cls = self.theclass
  4037. for dstvalue, flag in (-33, 1), (33, 1), (0, 0), (None, -1):
  4038. d = cls(1, 1, 1, 10, 20, 30, 40, tzinfo=DST(dstvalue))
  4039. t = d.timetuple()
  4040. self.assertEqual(1, t.tm_year)
  4041. self.assertEqual(1, t.tm_mon)
  4042. self.assertEqual(1, t.tm_mday)
  4043. self.assertEqual(10, t.tm_hour)
  4044. self.assertEqual(20, t.tm_min)
  4045. self.assertEqual(30, t.tm_sec)
  4046. self.assertEqual(0, t.tm_wday)
  4047. self.assertEqual(1, t.tm_yday)
  4048. self.assertEqual(flag, t.tm_isdst)
  4049. # dst() returns wrong type.
  4050. self.assertRaises(TypeError, cls(1, 1, 1, tzinfo=DST("x")).timetuple)
  4051. # dst() at the edge.
  4052. self.assertEqual(cls(1,1,1, tzinfo=DST(1439)).timetuple().tm_isdst, 1)
  4053. self.assertEqual(cls(1,1,1, tzinfo=DST(-1439)).timetuple().tm_isdst, 1)
  4054. # dst() out of range.
  4055. self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(1440)).timetuple)
  4056. self.assertRaises(ValueError, cls(1,1,1, tzinfo=DST(-1440)).timetuple)
  4057. def test_utctimetuple(self):
  4058. class DST(tzinfo):
  4059. def __init__(self, dstvalue=0):
  4060. if isinstance(dstvalue, int):
  4061. dstvalue = timedelta(minutes=dstvalue)
  4062. self.dstvalue = dstvalue
  4063. def dst(self, dt):
  4064. return self.dstvalue
  4065. cls = self.theclass
  4066. # This can't work: DST didn't implement utcoffset.
  4067. self.assertRaises(NotImplementedError,
  4068. cls(1, 1, 1, tzinfo=DST(0)).utcoffset)
  4069. class UOFS(DST):
  4070. def __init__(self, uofs, dofs=None):
  4071. DST.__init__(self, dofs)
  4072. self.uofs = timedelta(minutes=uofs)
  4073. def utcoffset(self, dt):
  4074. return self.uofs
  4075. for dstvalue in -33, 33, 0, None:
  4076. d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=UOFS(-53, dstvalue))
  4077. t = d.utctimetuple()
  4078. self.assertEqual(d.year, t.tm_year)
  4079. self.assertEqual(d.month, t.tm_mon)
  4080. self.assertEqual(d.day, t.tm_mday)
  4081. self.assertEqual(11, t.tm_hour) # 20mm + 53mm = 1hn + 13mm
  4082. self.assertEqual(13, t.tm_min)
  4083. self.assertEqual(d.second, t.tm_sec)
  4084. self.assertEqual(d.weekday(), t.tm_wday)
  4085. self.assertEqual(d.toordinal() - date(1, 1, 1).toordinal() + 1,
  4086. t.tm_yday)
  4087. # Ensure tm_isdst is 0 regardless of what dst() says: DST
  4088. # is never in effect for a UTC time.
  4089. self.assertEqual(0, t.tm_isdst)
  4090. # For naive datetime, utctimetuple == timetuple except for isdst
  4091. d = cls(1, 2, 3, 10, 20, 30, 40)
  4092. t = d.utctimetuple()
  4093. self.assertEqual(t[:-1], d.timetuple()[:-1])
  4094. self.assertEqual(0, t.tm_isdst)
  4095. # Same if utcoffset is None
  4096. class NOFS(DST):
  4097. def utcoffset(self, dt):
  4098. return None
  4099. d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=NOFS())
  4100. t = d.utctimetuple()
  4101. self.assertEqual(t[:-1], d.timetuple()[:-1])
  4102. self.assertEqual(0, t.tm_isdst)
  4103. # Check that bad tzinfo is detected
  4104. class BOFS(DST):
  4105. def utcoffset(self, dt):
  4106. return "EST"
  4107. d = cls(1, 2, 3, 10, 20, 30, 40, tzinfo=BOFS())
  4108. self.assertRaises(TypeError, d.utctimetuple)
  4109. # Check that utctimetuple() is the same as
  4110. # astimezone(utc).timetuple()
  4111. d = cls(2010, 11, 13, 14, 15, 16, 171819)
  4112. for tz in [timezone.min, timezone.utc, timezone.max]:
  4113. dtz = d.replace(tzinfo=tz)
  4114. self.assertEqual(dtz.utctimetuple()[:-1],
  4115. dtz.astimezone(timezone.utc).timetuple()[:-1])
  4116. # At the edges, UTC adjustment can produce years out-of-range
  4117. # for a datetime object. Ensure that an OverflowError is
  4118. # raised.
  4119. tiny = cls(MINYEAR, 1, 1, 0, 0, 37, tzinfo=UOFS(1439))
  4120. # That goes back 1 minute less than a full day.
  4121. self.assertRaises(OverflowError, tiny.utctimetuple)
  4122. huge = cls(MAXYEAR, 12, 31, 23, 59, 37, 999999, tzinfo=UOFS(-1439))
  4123. # That goes forward 1 minute less than a full day.
  4124. self.assertRaises(OverflowError, huge.utctimetuple)
  4125. # More overflow cases
  4126. tiny = cls.min.replace(tzinfo=timezone(MINUTE))
  4127. self.assertRaises(OverflowError, tiny.utctimetuple)
  4128. huge = cls.max.replace(tzinfo=timezone(-MINUTE))
  4129. self.assertRaises(OverflowError, huge.utctimetuple)
  4130. def test_tzinfo_isoformat(self):
  4131. zero = FixedOffset(0, "+00:00")
  4132. plus = FixedOffset(220, "+03:40")
  4133. minus = FixedOffset(-231, "-03:51")
  4134. unknown = FixedOffset(None, "")
  4135. cls = self.theclass
  4136. datestr = '0001-02-03'
  4137. for ofs in None, zero, plus, minus, unknown:
  4138. for us in 0, 987001:
  4139. d = cls(1, 2, 3, 4, 5, 59, us, tzinfo=ofs)
  4140. timestr = '04:05:59' + (us and '.987001' or '')
  4141. ofsstr = ofs is not None and d.tzname() or ''
  4142. tailstr = timestr + ofsstr
  4143. iso = d.isoformat()
  4144. self.assertEqual(iso, datestr + 'T' + tailstr)
  4145. self.assertEqual(iso, d.isoformat('T'))
  4146. self.assertEqual(d.isoformat('k'), datestr + 'k' + tailstr)
  4147. self.assertEqual(d.isoformat('\u1234'), datestr + '\u1234' + tailstr)
  4148. self.assertEqual(str(d), datestr + ' ' + tailstr)
  4149. def test_replace(self):
  4150. cls = self.theclass
  4151. z100 = FixedOffset(100, "+100")
  4152. zm200 = FixedOffset(timedelta(minutes=-200), "-200")
  4153. args = [1, 2, 3, 4, 5, 6, 7, z100]
  4154. base = cls(*args)
  4155. self.assertEqual(base, base.replace())
  4156. i = 0
  4157. for name, newval in (("year", 2),
  4158. ("month", 3),
  4159. ("day", 4),
  4160. ("hour", 5),
  4161. ("minute", 6),
  4162. ("second", 7),
  4163. ("microsecond", 8),
  4164. ("tzinfo", zm200)):
  4165. newargs = args[:]
  4166. newargs[i] = newval
  4167. expected = cls(*newargs)
  4168. got = base.replace(**{name: newval})
  4169. self.assertEqual(expected, got)
  4170. i += 1
  4171. # Ensure we can get rid of a tzinfo.
  4172. self.assertEqual(base.tzname(), "+100")
  4173. base2 = base.replace(tzinfo=None)
  4174. self.assertIsNone(base2.tzinfo)
  4175. self.assertIsNone(base2.tzname())
  4176. # Ensure we can add one.
  4177. base3 = base2.replace(tzinfo=z100)
  4178. self.assertEqual(base, base3)
  4179. self.assertIs(base.tzinfo, base3.tzinfo)
  4180. # Out of bounds.
  4181. base = cls(2000, 2, 29)
  4182. self.assertRaises(ValueError, base.replace, year=2001)
  4183. def test_more_astimezone(self):
  4184. # The inherited test_astimezone covered some trivial and error cases.
  4185. fnone = FixedOffset(None, "None")
  4186. f44m = FixedOffset(44, "44")
  4187. fm5h = FixedOffset(-timedelta(hours=5), "m300")
  4188. dt = self.theclass.now(tz=f44m)
  4189. self.assertIs(dt.tzinfo, f44m)
  4190. # Replacing with degenerate tzinfo raises an exception.
  4191. self.assertRaises(ValueError, dt.astimezone, fnone)
  4192. # Replacing with same tzinfo makes no change.
  4193. x = dt.astimezone(dt.tzinfo)
  4194. self.assertIs(x.tzinfo, f44m)
  4195. self.assertEqual(x.date(), dt.date())
  4196. self.assertEqual(x.time(), dt.time())
  4197. # Replacing with different tzinfo does adjust.
  4198. got = dt.astimezone(fm5h)
  4199. self.assertIs(got.tzinfo, fm5h)
  4200. self.assertEqual(got.utcoffset(), timedelta(hours=-5))
  4201. expected = dt - dt.utcoffset() # in effect, convert to UTC
  4202. expected += fm5h.utcoffset(dt) # and from there to local time
  4203. expected = expected.replace(tzinfo=fm5h) # and attach new tzinfo
  4204. self.assertEqual(got.date(), expected.date())
  4205. self.assertEqual(got.time(), expected.time())
  4206. self.assertEqual(got.timetz(), expected.timetz())
  4207. self.assertIs(got.tzinfo, expected.tzinfo)
  4208. self.assertEqual(got, expected)
  4209. @support.run_with_tz('UTC')
  4210. def test_astimezone_default_utc(self):
  4211. dt = self.theclass.now(timezone.utc)
  4212. self.assertEqual(dt.astimezone(None), dt)
  4213. self.assertEqual(dt.astimezone(), dt)
  4214. # Note that offset in TZ variable has the opposite sign to that
  4215. # produced by %z directive.
  4216. @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
  4217. def test_astimezone_default_eastern(self):
  4218. dt = self.theclass(2012, 11, 4, 6, 30, tzinfo=timezone.utc)
  4219. local = dt.astimezone()
  4220. self.assertEqual(dt, local)
  4221. self.assertEqual(local.strftime("%z %Z"), "-0500 EST")
  4222. dt = self.theclass(2012, 11, 4, 5, 30, tzinfo=timezone.utc)
  4223. local = dt.astimezone()
  4224. self.assertEqual(dt, local)
  4225. self.assertEqual(local.strftime("%z %Z"), "-0400 EDT")
  4226. @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
  4227. def test_astimezone_default_near_fold(self):
  4228. # Issue #26616.
  4229. u = datetime(2015, 11, 1, 5, tzinfo=timezone.utc)
  4230. t = u.astimezone()
  4231. s = t.astimezone()
  4232. self.assertEqual(t.tzinfo, s.tzinfo)
  4233. def test_aware_subtract(self):
  4234. cls = self.theclass
  4235. # Ensure that utcoffset() is ignored when the operands have the
  4236. # same tzinfo member.
  4237. class OperandDependentOffset(tzinfo):
  4238. def utcoffset(self, t):
  4239. if t.minute < 10:
  4240. # d0 and d1 equal after adjustment
  4241. return timedelta(minutes=t.minute)
  4242. else:
  4243. # d2 off in the weeds
  4244. return timedelta(minutes=59)
  4245. base = cls(8, 9, 10, 11, 12, 13, 14, tzinfo=OperandDependentOffset())
  4246. d0 = base.replace(minute=3)
  4247. d1 = base.replace(minute=9)
  4248. d2 = base.replace(minute=11)
  4249. for x in d0, d1, d2:
  4250. for y in d0, d1, d2:
  4251. got = x - y
  4252. expected = timedelta(minutes=x.minute - y.minute)
  4253. self.assertEqual(got, expected)
  4254. # OTOH, if the tzinfo members are distinct, utcoffsets aren't
  4255. # ignored.
  4256. base = cls(8, 9, 10, 11, 12, 13, 14)
  4257. d0 = base.replace(minute=3, tzinfo=OperandDependentOffset())
  4258. d1 = base.replace(minute=9, tzinfo=OperandDependentOffset())
  4259. d2 = base.replace(minute=11, tzinfo=OperandDependentOffset())
  4260. for x in d0, d1, d2:
  4261. for y in d0, d1, d2:
  4262. got = x - y
  4263. if (x is d0 or x is d1) and (y is d0 or y is d1):
  4264. expected = timedelta(0)
  4265. elif x is y is d2:
  4266. expected = timedelta(0)
  4267. elif x is d2:
  4268. expected = timedelta(minutes=(11-59)-0)
  4269. else:
  4270. assert y is d2
  4271. expected = timedelta(minutes=0-(11-59))
  4272. self.assertEqual(got, expected)
  4273. def test_mixed_compare(self):
  4274. t1 = datetime(1, 2, 3, 4, 5, 6, 7)
  4275. t2 = datetime(1, 2, 3, 4, 5, 6, 7)
  4276. self.assertEqual(t1, t2)
  4277. t2 = t2.replace(tzinfo=None)
  4278. self.assertEqual(t1, t2)
  4279. t2 = t2.replace(tzinfo=FixedOffset(None, ""))
  4280. self.assertEqual(t1, t2)
  4281. t2 = t2.replace(tzinfo=FixedOffset(0, ""))
  4282. self.assertNotEqual(t1, t2)
  4283. # In datetime w/ identical tzinfo objects, utcoffset is ignored.
  4284. class Varies(tzinfo):
  4285. def __init__(self):
  4286. self.offset = timedelta(minutes=22)
  4287. def utcoffset(self, t):
  4288. self.offset += timedelta(minutes=1)
  4289. return self.offset
  4290. v = Varies()
  4291. t1 = t2.replace(tzinfo=v)
  4292. t2 = t2.replace(tzinfo=v)
  4293. self.assertEqual(t1.utcoffset(), timedelta(minutes=23))
  4294. self.assertEqual(t2.utcoffset(), timedelta(minutes=24))
  4295. self.assertEqual(t1, t2)
  4296. # But if they're not identical, it isn't ignored.
  4297. t2 = t2.replace(tzinfo=Varies())
  4298. self.assertTrue(t1 < t2) # t1's offset counter still going up
  4299. def test_subclass_datetimetz(self):
  4300. class C(self.theclass):
  4301. theAnswer = 42
  4302. def __new__(cls, *args, **kws):
  4303. temp = kws.copy()
  4304. extra = temp.pop('extra')
  4305. result = self.theclass.__new__(cls, *args, **temp)
  4306. result.extra = extra
  4307. return result
  4308. def newmeth(self, start):
  4309. return start + self.hour + self.year
  4310. args = 2002, 12, 31, 4, 5, 6, 500, FixedOffset(-300, "EST", 1)
  4311. dt1 = self.theclass(*args)
  4312. dt2 = C(*args, **{'extra': 7})
  4313. self.assertEqual(dt2.__class__, C)
  4314. self.assertEqual(dt2.theAnswer, 42)
  4315. self.assertEqual(dt2.extra, 7)
  4316. self.assertEqual(dt1.utcoffset(), dt2.utcoffset())
  4317. self.assertEqual(dt2.newmeth(-7), dt1.hour + dt1.year - 7)
  4318. # Pain to set up DST-aware tzinfo classes.
  4319. def first_sunday_on_or_after(dt):
  4320. days_to_go = 6 - dt.weekday()
  4321. if days_to_go:
  4322. dt += timedelta(days_to_go)
  4323. return dt
  4324. ZERO = timedelta(0)
  4325. MINUTE = timedelta(minutes=1)
  4326. HOUR = timedelta(hours=1)
  4327. DAY = timedelta(days=1)
  4328. # In the US, DST starts at 2am (standard time) on the first Sunday in April.
  4329. DSTSTART = datetime(1, 4, 1, 2)
  4330. # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct,
  4331. # which is the first Sunday on or after Oct 25. Because we view 1:MM as
  4332. # being standard time on that day, there is no spelling in local time of
  4333. # the last hour of DST (that's 1:MM DST, but 1:MM is taken as standard time).
  4334. DSTEND = datetime(1, 10, 25, 1)
  4335. class USTimeZone(tzinfo):
  4336. def __init__(self, hours, reprname, stdname, dstname):
  4337. self.stdoffset = timedelta(hours=hours)
  4338. self.reprname = reprname
  4339. self.stdname = stdname
  4340. self.dstname = dstname
  4341. def __repr__(self):
  4342. return self.reprname
  4343. def tzname(self, dt):
  4344. if self.dst(dt):
  4345. return self.dstname
  4346. else:
  4347. return self.stdname
  4348. def utcoffset(self, dt):
  4349. return self.stdoffset + self.dst(dt)
  4350. def dst(self, dt):
  4351. if dt is None or dt.tzinfo is None:
  4352. # An exception instead may be sensible here, in one or more of
  4353. # the cases.
  4354. return ZERO
  4355. assert dt.tzinfo is self
  4356. # Find first Sunday in April.
  4357. start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
  4358. assert start.weekday() == 6 and start.month == 4 and start.day <= 7
  4359. # Find last Sunday in October.
  4360. end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
  4361. assert end.weekday() == 6 and end.month == 10 and end.day >= 25
  4362. # Can't compare naive to aware objects, so strip the timezone from
  4363. # dt first.
  4364. if start <= dt.replace(tzinfo=None) < end:
  4365. return HOUR
  4366. else:
  4367. return ZERO
  4368. Eastern = USTimeZone(-5, "Eastern", "EST", "EDT")
  4369. Central = USTimeZone(-6, "Central", "CST", "CDT")
  4370. Mountain = USTimeZone(-7, "Mountain", "MST", "MDT")
  4371. Pacific = USTimeZone(-8, "Pacific", "PST", "PDT")
  4372. utc_real = FixedOffset(0, "UTC", 0)
  4373. # For better test coverage, we want another flavor of UTC that's west of
  4374. # the Eastern and Pacific timezones.
  4375. utc_fake = FixedOffset(-12*60, "UTCfake", 0)
  4376. class TestTimezoneConversions(unittest.TestCase):
  4377. # The DST switch times for 2002, in std time.
  4378. dston = datetime(2002, 4, 7, 2)
  4379. dstoff = datetime(2002, 10, 27, 1)
  4380. theclass = datetime
  4381. # Check a time that's inside DST.
  4382. def checkinside(self, dt, tz, utc, dston, dstoff):
  4383. self.assertEqual(dt.dst(), HOUR)
  4384. # Conversion to our own timezone is always an identity.
  4385. self.assertEqual(dt.astimezone(tz), dt)
  4386. asutc = dt.astimezone(utc)
  4387. there_and_back = asutc.astimezone(tz)
  4388. # Conversion to UTC and back isn't always an identity here,
  4389. # because there are redundant spellings (in local time) of
  4390. # UTC time when DST begins: the clock jumps from 1:59:59
  4391. # to 3:00:00, and a local time of 2:MM:SS doesn't really
  4392. # make sense then. The classes above treat 2:MM:SS as
  4393. # daylight time then (it's "after 2am"), really an alias
  4394. # for 1:MM:SS standard time. The latter form is what
  4395. # conversion back from UTC produces.
  4396. if dt.date() == dston.date() and dt.hour == 2:
  4397. # We're in the redundant hour, and coming back from
  4398. # UTC gives the 1:MM:SS standard-time spelling.
  4399. self.assertEqual(there_and_back + HOUR, dt)
  4400. # Although during was considered to be in daylight
  4401. # time, there_and_back is not.
  4402. self.assertEqual(there_and_back.dst(), ZERO)
  4403. # They're the same times in UTC.
  4404. self.assertEqual(there_and_back.astimezone(utc),
  4405. dt.astimezone(utc))
  4406. else:
  4407. # We're not in the redundant hour.
  4408. self.assertEqual(dt, there_and_back)
  4409. # Because we have a redundant spelling when DST begins, there is
  4410. # (unfortunately) an hour when DST ends that can't be spelled at all in
  4411. # local time. When DST ends, the clock jumps from 1:59 back to 1:00
  4412. # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be
  4413. # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be
  4414. # daylight time. The hour 1:MM daylight == 0:MM standard can't be
  4415. # expressed in local time. Nevertheless, we want conversion back
  4416. # from UTC to mimic the local clock's "repeat an hour" behavior.
  4417. nexthour_utc = asutc + HOUR
  4418. nexthour_tz = nexthour_utc.astimezone(tz)
  4419. if dt.date() == dstoff.date() and dt.hour == 0:
  4420. # We're in the hour before the last DST hour. The last DST hour
  4421. # is ineffable. We want the conversion back to repeat 1:MM.
  4422. self.assertEqual(nexthour_tz, dt.replace(hour=1))
  4423. nexthour_utc += HOUR
  4424. nexthour_tz = nexthour_utc.astimezone(tz)
  4425. self.assertEqual(nexthour_tz, dt.replace(hour=1))
  4426. else:
  4427. self.assertEqual(nexthour_tz - dt, HOUR)
  4428. # Check a time that's outside DST.
  4429. def checkoutside(self, dt, tz, utc):
  4430. self.assertEqual(dt.dst(), ZERO)
  4431. # Conversion to our own timezone is always an identity.
  4432. self.assertEqual(dt.astimezone(tz), dt)
  4433. # Converting to UTC and back is an identity too.
  4434. asutc = dt.astimezone(utc)
  4435. there_and_back = asutc.astimezone(tz)
  4436. self.assertEqual(dt, there_and_back)
  4437. def convert_between_tz_and_utc(self, tz, utc):
  4438. dston = self.dston.replace(tzinfo=tz)
  4439. # Because 1:MM on the day DST ends is taken as being standard time,
  4440. # there is no spelling in tz for the last hour of daylight time.
  4441. # For purposes of the test, the last hour of DST is 0:MM, which is
  4442. # taken as being daylight time (and 1:MM is taken as being standard
  4443. # time).
  4444. dstoff = self.dstoff.replace(tzinfo=tz)
  4445. for delta in (timedelta(weeks=13),
  4446. DAY,
  4447. HOUR,
  4448. timedelta(minutes=1),
  4449. timedelta(microseconds=1)):
  4450. self.checkinside(dston, tz, utc, dston, dstoff)
  4451. for during in dston + delta, dstoff - delta:
  4452. self.checkinside(during, tz, utc, dston, dstoff)
  4453. self.checkoutside(dstoff, tz, utc)
  4454. for outside in dston - delta, dstoff + delta:
  4455. self.checkoutside(outside, tz, utc)
  4456. def test_easy(self):
  4457. # Despite the name of this test, the endcases are excruciating.
  4458. self.convert_between_tz_and_utc(Eastern, utc_real)
  4459. self.convert_between_tz_and_utc(Pacific, utc_real)
  4460. self.convert_between_tz_and_utc(Eastern, utc_fake)
  4461. self.convert_between_tz_and_utc(Pacific, utc_fake)
  4462. # The next is really dancing near the edge. It works because
  4463. # Pacific and Eastern are far enough apart that their "problem
  4464. # hours" don't overlap.
  4465. self.convert_between_tz_and_utc(Eastern, Pacific)
  4466. self.convert_between_tz_and_utc(Pacific, Eastern)
  4467. # OTOH, these fail! Don't enable them. The difficulty is that
  4468. # the edge case tests assume that every hour is representable in
  4469. # the "utc" class. This is always true for a fixed-offset tzinfo
  4470. # class (like utc_real and utc_fake), but not for Eastern or Central.
  4471. # For these adjacent DST-aware time zones, the range of time offsets
  4472. # tested ends up creating hours in the one that aren't representable
  4473. # in the other. For the same reason, we would see failures in the
  4474. # Eastern vs Pacific tests too if we added 3*HOUR to the list of
  4475. # offset deltas in convert_between_tz_and_utc().
  4476. #
  4477. # self.convert_between_tz_and_utc(Eastern, Central) # can't work
  4478. # self.convert_between_tz_and_utc(Central, Eastern) # can't work
  4479. def test_tricky(self):
  4480. # 22:00 on day before daylight starts.
  4481. fourback = self.dston - timedelta(hours=4)
  4482. ninewest = FixedOffset(-9*60, "-0900", 0)
  4483. fourback = fourback.replace(tzinfo=ninewest)
  4484. # 22:00-0900 is 7:00 UTC == 2:00 EST == 3:00 DST. Since it's "after
  4485. # 2", we should get the 3 spelling.
  4486. # If we plug 22:00 the day before into Eastern, it "looks like std
  4487. # time", so its offset is returned as -5, and -5 - -9 = 4. Adding 4
  4488. # to 22:00 lands on 2:00, which makes no sense in local time (the
  4489. # local clock jumps from 1 to 3). The point here is to make sure we
  4490. # get the 3 spelling.
  4491. expected = self.dston.replace(hour=3)
  4492. got = fourback.astimezone(Eastern).replace(tzinfo=None)
  4493. self.assertEqual(expected, got)
  4494. # Similar, but map to 6:00 UTC == 1:00 EST == 2:00 DST. In that
  4495. # case we want the 1:00 spelling.
  4496. sixutc = self.dston.replace(hour=6, tzinfo=utc_real)
  4497. # Now 6:00 "looks like daylight", so the offset wrt Eastern is -4,
  4498. # and adding -4-0 == -4 gives the 2:00 spelling. We want the 1:00 EST
  4499. # spelling.
  4500. expected = self.dston.replace(hour=1)
  4501. got = sixutc.astimezone(Eastern).replace(tzinfo=None)
  4502. self.assertEqual(expected, got)
  4503. # Now on the day DST ends, we want "repeat an hour" behavior.
  4504. # UTC 4:MM 5:MM 6:MM 7:MM checking these
  4505. # EST 23:MM 0:MM 1:MM 2:MM
  4506. # EDT 0:MM 1:MM 2:MM 3:MM
  4507. # wall 0:MM 1:MM 1:MM 2:MM against these
  4508. for utc in utc_real, utc_fake:
  4509. for tz in Eastern, Pacific:
  4510. first_std_hour = self.dstoff - timedelta(hours=2) # 23:MM
  4511. # Convert that to UTC.
  4512. first_std_hour -= tz.utcoffset(None)
  4513. # Adjust for possibly fake UTC.
  4514. asutc = first_std_hour + utc.utcoffset(None)
  4515. # First UTC hour to convert; this is 4:00 when utc=utc_real &
  4516. # tz=Eastern.
  4517. asutcbase = asutc.replace(tzinfo=utc)
  4518. for tzhour in (0, 1, 1, 2):
  4519. expectedbase = self.dstoff.replace(hour=tzhour)
  4520. for minute in 0, 30, 59:
  4521. expected = expectedbase.replace(minute=minute)
  4522. asutc = asutcbase.replace(minute=minute)
  4523. astz = asutc.astimezone(tz)
  4524. self.assertEqual(astz.replace(tzinfo=None), expected)
  4525. asutcbase += HOUR
  4526. def test_bogus_dst(self):
  4527. class ok(tzinfo):
  4528. def utcoffset(self, dt): return HOUR
  4529. def dst(self, dt): return HOUR
  4530. now = self.theclass.now().replace(tzinfo=utc_real)
  4531. # Doesn't blow up.
  4532. now.astimezone(ok())
  4533. # Does blow up.
  4534. class notok(ok):
  4535. def dst(self, dt): return None
  4536. self.assertRaises(ValueError, now.astimezone, notok())
  4537. # Sometimes blow up. In the following, tzinfo.dst()
  4538. # implementation may return None or not None depending on
  4539. # whether DST is assumed to be in effect. In this situation,
  4540. # a ValueError should be raised by astimezone().
  4541. class tricky_notok(ok):
  4542. def dst(self, dt):
  4543. if dt.year == 2000:
  4544. return None
  4545. else:
  4546. return 10*HOUR
  4547. dt = self.theclass(2001, 1, 1).replace(tzinfo=utc_real)
  4548. self.assertRaises(ValueError, dt.astimezone, tricky_notok())
  4549. def test_fromutc(self):
  4550. self.assertRaises(TypeError, Eastern.fromutc) # not enough args
  4551. now = datetime.utcnow().replace(tzinfo=utc_real)
  4552. self.assertRaises(ValueError, Eastern.fromutc, now) # wrong tzinfo
  4553. now = now.replace(tzinfo=Eastern) # insert correct tzinfo
  4554. enow = Eastern.fromutc(now) # doesn't blow up
  4555. self.assertEqual(enow.tzinfo, Eastern) # has right tzinfo member
  4556. self.assertRaises(TypeError, Eastern.fromutc, now, now) # too many args
  4557. self.assertRaises(TypeError, Eastern.fromutc, date.today()) # wrong type
  4558. # Always converts UTC to standard time.
  4559. class FauxUSTimeZone(USTimeZone):
  4560. def fromutc(self, dt):
  4561. return dt + self.stdoffset
  4562. FEastern = FauxUSTimeZone(-5, "FEastern", "FEST", "FEDT")
  4563. # UTC 4:MM 5:MM 6:MM 7:MM 8:MM 9:MM
  4564. # EST 23:MM 0:MM 1:MM 2:MM 3:MM 4:MM
  4565. # EDT 0:MM 1:MM 2:MM 3:MM 4:MM 5:MM
  4566. # Check around DST start.
  4567. start = self.dston.replace(hour=4, tzinfo=Eastern)
  4568. fstart = start.replace(tzinfo=FEastern)
  4569. for wall in 23, 0, 1, 3, 4, 5:
  4570. expected = start.replace(hour=wall)
  4571. if wall == 23:
  4572. expected -= timedelta(days=1)
  4573. got = Eastern.fromutc(start)
  4574. self.assertEqual(expected, got)
  4575. expected = fstart + FEastern.stdoffset
  4576. got = FEastern.fromutc(fstart)
  4577. self.assertEqual(expected, got)
  4578. # Ensure astimezone() calls fromutc() too.
  4579. got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
  4580. self.assertEqual(expected, got)
  4581. start += HOUR
  4582. fstart += HOUR
  4583. # Check around DST end.
  4584. start = self.dstoff.replace(hour=4, tzinfo=Eastern)
  4585. fstart = start.replace(tzinfo=FEastern)
  4586. for wall in 0, 1, 1, 2, 3, 4:
  4587. expected = start.replace(hour=wall)
  4588. got = Eastern.fromutc(start)
  4589. self.assertEqual(expected, got)
  4590. expected = fstart + FEastern.stdoffset
  4591. got = FEastern.fromutc(fstart)
  4592. self.assertEqual(expected, got)
  4593. # Ensure astimezone() calls fromutc() too.
  4594. got = fstart.replace(tzinfo=utc_real).astimezone(FEastern)
  4595. self.assertEqual(expected, got)
  4596. start += HOUR
  4597. fstart += HOUR
  4598. #############################################################################
  4599. # oddballs
  4600. class Oddballs(unittest.TestCase):
  4601. def test_bug_1028306(self):
  4602. # Trying to compare a date to a datetime should act like a mixed-
  4603. # type comparison, despite that datetime is a subclass of date.
  4604. as_date = date.today()
  4605. as_datetime = datetime.combine(as_date, time())
  4606. self.assertTrue(as_date != as_datetime)
  4607. self.assertTrue(as_datetime != as_date)
  4608. self.assertFalse(as_date == as_datetime)
  4609. self.assertFalse(as_datetime == as_date)
  4610. self.assertRaises(TypeError, lambda: as_date < as_datetime)
  4611. self.assertRaises(TypeError, lambda: as_datetime < as_date)
  4612. self.assertRaises(TypeError, lambda: as_date <= as_datetime)
  4613. self.assertRaises(TypeError, lambda: as_datetime <= as_date)
  4614. self.assertRaises(TypeError, lambda: as_date > as_datetime)
  4615. self.assertRaises(TypeError, lambda: as_datetime > as_date)
  4616. self.assertRaises(TypeError, lambda: as_date >= as_datetime)
  4617. self.assertRaises(TypeError, lambda: as_datetime >= as_date)
  4618. # Nevertheless, comparison should work with the base-class (date)
  4619. # projection if use of a date method is forced.
  4620. self.assertEqual(as_date.__eq__(as_datetime), True)
  4621. different_day = (as_date.day + 1) % 20 + 1
  4622. as_different = as_datetime.replace(day= different_day)
  4623. self.assertEqual(as_date.__eq__(as_different), False)
  4624. # And date should compare with other subclasses of date. If a
  4625. # subclass wants to stop this, it's up to the subclass to do so.
  4626. date_sc = SubclassDate(as_date.year, as_date.month, as_date.day)
  4627. self.assertEqual(as_date, date_sc)
  4628. self.assertEqual(date_sc, as_date)
  4629. # Ditto for datetimes.
  4630. datetime_sc = SubclassDatetime(as_datetime.year, as_datetime.month,
  4631. as_date.day, 0, 0, 0)
  4632. self.assertEqual(as_datetime, datetime_sc)
  4633. self.assertEqual(datetime_sc, as_datetime)
  4634. def test_extra_attributes(self):
  4635. for x in [date.today(),
  4636. time(),
  4637. datetime.utcnow(),
  4638. timedelta(),
  4639. tzinfo(),
  4640. timezone(timedelta())]:
  4641. with self.assertRaises(AttributeError):
  4642. x.abc = 1
  4643. def test_check_arg_types(self):
  4644. class Number:
  4645. def __init__(self, value):
  4646. self.value = value
  4647. def __int__(self):
  4648. return self.value
  4649. class Float(float):
  4650. pass
  4651. for xx in [10.0, Float(10.9),
  4652. decimal.Decimal(10), decimal.Decimal('10.9'),
  4653. Number(10), Number(10.9),
  4654. '10']:
  4655. self.assertRaises(TypeError, datetime, xx, 10, 10, 10, 10, 10, 10)
  4656. self.assertRaises(TypeError, datetime, 10, xx, 10, 10, 10, 10, 10)
  4657. self.assertRaises(TypeError, datetime, 10, 10, xx, 10, 10, 10, 10)
  4658. self.assertRaises(TypeError, datetime, 10, 10, 10, xx, 10, 10, 10)
  4659. self.assertRaises(TypeError, datetime, 10, 10, 10, 10, xx, 10, 10)
  4660. self.assertRaises(TypeError, datetime, 10, 10, 10, 10, 10, xx, 10)
  4661. self.assertRaises(TypeError, datetime, 10, 10, 10, 10, 10, 10, xx)
  4662. #############################################################################
  4663. # Local Time Disambiguation
  4664. # An experimental reimplementation of fromutc that respects the "fold" flag.
  4665. class tzinfo2(tzinfo):
  4666. def fromutc(self, dt):
  4667. "datetime in UTC -> datetime in local time."
  4668. if not isinstance(dt, datetime):
  4669. raise TypeError("fromutc() requires a datetime argument")
  4670. if dt.tzinfo is not self:
  4671. raise ValueError("dt.tzinfo is not self")
  4672. # Returned value satisfies
  4673. # dt + ldt.utcoffset() = ldt
  4674. off0 = dt.replace(fold=0).utcoffset()
  4675. off1 = dt.replace(fold=1).utcoffset()
  4676. if off0 is None or off1 is None or dt.dst() is None:
  4677. raise ValueError
  4678. if off0 == off1:
  4679. ldt = dt + off0
  4680. off1 = ldt.utcoffset()
  4681. if off0 == off1:
  4682. return ldt
  4683. # Now, we discovered both possible offsets, so
  4684. # we can just try four possible solutions:
  4685. for off in [off0, off1]:
  4686. ldt = dt + off
  4687. if ldt.utcoffset() == off:
  4688. return ldt
  4689. ldt = ldt.replace(fold=1)
  4690. if ldt.utcoffset() == off:
  4691. return ldt
  4692. raise ValueError("No suitable local time found")
  4693. # Reimplementing simplified US timezones to respect the "fold" flag:
  4694. class USTimeZone2(tzinfo2):
  4695. def __init__(self, hours, reprname, stdname, dstname):
  4696. self.stdoffset = timedelta(hours=hours)
  4697. self.reprname = reprname
  4698. self.stdname = stdname
  4699. self.dstname = dstname
  4700. def __repr__(self):
  4701. return self.reprname
  4702. def tzname(self, dt):
  4703. if self.dst(dt):
  4704. return self.dstname
  4705. else:
  4706. return self.stdname
  4707. def utcoffset(self, dt):
  4708. return self.stdoffset + self.dst(dt)
  4709. def dst(self, dt):
  4710. if dt is None or dt.tzinfo is None:
  4711. # An exception instead may be sensible here, in one or more of
  4712. # the cases.
  4713. return ZERO
  4714. assert dt.tzinfo is self
  4715. # Find first Sunday in April.
  4716. start = first_sunday_on_or_after(DSTSTART.replace(year=dt.year))
  4717. assert start.weekday() == 6 and start.month == 4 and start.day <= 7
  4718. # Find last Sunday in October.
  4719. end = first_sunday_on_or_after(DSTEND.replace(year=dt.year))
  4720. assert end.weekday() == 6 and end.month == 10 and end.day >= 25
  4721. # Can't compare naive to aware objects, so strip the timezone from
  4722. # dt first.
  4723. dt = dt.replace(tzinfo=None)
  4724. if start + HOUR <= dt < end:
  4725. # DST is in effect.
  4726. return HOUR
  4727. elif end <= dt < end + HOUR:
  4728. # Fold (an ambiguous hour): use dt.fold to disambiguate.
  4729. return ZERO if dt.fold else HOUR
  4730. elif start <= dt < start + HOUR:
  4731. # Gap (a non-existent hour): reverse the fold rule.
  4732. return HOUR if dt.fold else ZERO
  4733. else:
  4734. # DST is off.
  4735. return ZERO
  4736. Eastern2 = USTimeZone2(-5, "Eastern2", "EST", "EDT")
  4737. Central2 = USTimeZone2(-6, "Central2", "CST", "CDT")
  4738. Mountain2 = USTimeZone2(-7, "Mountain2", "MST", "MDT")
  4739. Pacific2 = USTimeZone2(-8, "Pacific2", "PST", "PDT")
  4740. # Europe_Vilnius_1941 tzinfo implementation reproduces the following
  4741. # 1941 transition from Olson's tzdist:
  4742. #
  4743. # Zone NAME GMTOFF RULES FORMAT [UNTIL]
  4744. # ZoneEurope/Vilnius 1:00 - CET 1940 Aug 3
  4745. # 3:00 - MSK 1941 Jun 24
  4746. # 1:00 C-Eur CE%sT 1944 Aug
  4747. #
  4748. # $ zdump -v Europe/Vilnius | grep 1941
  4749. # Europe/Vilnius Mon Jun 23 20:59:59 1941 UTC = Mon Jun 23 23:59:59 1941 MSK isdst=0 gmtoff=10800
  4750. # Europe/Vilnius Mon Jun 23 21:00:00 1941 UTC = Mon Jun 23 23:00:00 1941 CEST isdst=1 gmtoff=7200
  4751. class Europe_Vilnius_1941(tzinfo):
  4752. def _utc_fold(self):
  4753. return [datetime(1941, 6, 23, 21, tzinfo=self), # Mon Jun 23 21:00:00 1941 UTC
  4754. datetime(1941, 6, 23, 22, tzinfo=self)] # Mon Jun 23 22:00:00 1941 UTC
  4755. def _loc_fold(self):
  4756. return [datetime(1941, 6, 23, 23, tzinfo=self), # Mon Jun 23 23:00:00 1941 MSK / CEST
  4757. datetime(1941, 6, 24, 0, tzinfo=self)] # Mon Jun 24 00:00:00 1941 CEST
  4758. def utcoffset(self, dt):
  4759. fold_start, fold_stop = self._loc_fold()
  4760. if dt < fold_start:
  4761. return 3 * HOUR
  4762. if dt < fold_stop:
  4763. return (2 if dt.fold else 3) * HOUR
  4764. # if dt >= fold_stop
  4765. return 2 * HOUR
  4766. def dst(self, dt):
  4767. fold_start, fold_stop = self._loc_fold()
  4768. if dt < fold_start:
  4769. return 0 * HOUR
  4770. if dt < fold_stop:
  4771. return (1 if dt.fold else 0) * HOUR
  4772. # if dt >= fold_stop
  4773. return 1 * HOUR
  4774. def tzname(self, dt):
  4775. fold_start, fold_stop = self._loc_fold()
  4776. if dt < fold_start:
  4777. return 'MSK'
  4778. if dt < fold_stop:
  4779. return ('MSK', 'CEST')[dt.fold]
  4780. # if dt >= fold_stop
  4781. return 'CEST'
  4782. def fromutc(self, dt):
  4783. assert dt.fold == 0
  4784. assert dt.tzinfo is self
  4785. if dt.year != 1941:
  4786. raise NotImplementedError
  4787. fold_start, fold_stop = self._utc_fold()
  4788. if dt < fold_start:
  4789. return dt + 3 * HOUR
  4790. if dt < fold_stop:
  4791. return (dt + 2 * HOUR).replace(fold=1)
  4792. # if dt >= fold_stop
  4793. return dt + 2 * HOUR
  4794. class TestLocalTimeDisambiguation(unittest.TestCase):
  4795. def test_vilnius_1941_fromutc(self):
  4796. Vilnius = Europe_Vilnius_1941()
  4797. gdt = datetime(1941, 6, 23, 20, 59, 59, tzinfo=timezone.utc)
  4798. ldt = gdt.astimezone(Vilnius)
  4799. self.assertEqual(ldt.strftime("%c %Z%z"),
  4800. 'Mon Jun 23 23:59:59 1941 MSK+0300')
  4801. self.assertEqual(ldt.fold, 0)
  4802. self.assertFalse(ldt.dst())
  4803. gdt = datetime(1941, 6, 23, 21, tzinfo=timezone.utc)
  4804. ldt = gdt.astimezone(Vilnius)
  4805. self.assertEqual(ldt.strftime("%c %Z%z"),
  4806. 'Mon Jun 23 23:00:00 1941 CEST+0200')
  4807. self.assertEqual(ldt.fold, 1)
  4808. self.assertTrue(ldt.dst())
  4809. gdt = datetime(1941, 6, 23, 22, tzinfo=timezone.utc)
  4810. ldt = gdt.astimezone(Vilnius)
  4811. self.assertEqual(ldt.strftime("%c %Z%z"),
  4812. 'Tue Jun 24 00:00:00 1941 CEST+0200')
  4813. self.assertEqual(ldt.fold, 0)
  4814. self.assertTrue(ldt.dst())
  4815. def test_vilnius_1941_toutc(self):
  4816. Vilnius = Europe_Vilnius_1941()
  4817. ldt = datetime(1941, 6, 23, 22, 59, 59, tzinfo=Vilnius)
  4818. gdt = ldt.astimezone(timezone.utc)
  4819. self.assertEqual(gdt.strftime("%c %Z"),
  4820. 'Mon Jun 23 19:59:59 1941 UTC')
  4821. ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius)
  4822. gdt = ldt.astimezone(timezone.utc)
  4823. self.assertEqual(gdt.strftime("%c %Z"),
  4824. 'Mon Jun 23 20:59:59 1941 UTC')
  4825. ldt = datetime(1941, 6, 23, 23, 59, 59, tzinfo=Vilnius, fold=1)
  4826. gdt = ldt.astimezone(timezone.utc)
  4827. self.assertEqual(gdt.strftime("%c %Z"),
  4828. 'Mon Jun 23 21:59:59 1941 UTC')
  4829. ldt = datetime(1941, 6, 24, 0, tzinfo=Vilnius)
  4830. gdt = ldt.astimezone(timezone.utc)
  4831. self.assertEqual(gdt.strftime("%c %Z"),
  4832. 'Mon Jun 23 22:00:00 1941 UTC')
  4833. def test_constructors(self):
  4834. t = time(0, fold=1)
  4835. dt = datetime(1, 1, 1, fold=1)
  4836. self.assertEqual(t.fold, 1)
  4837. self.assertEqual(dt.fold, 1)
  4838. with self.assertRaises(TypeError):
  4839. time(0, 0, 0, 0, None, 0)
  4840. def test_member(self):
  4841. dt = datetime(1, 1, 1, fold=1)
  4842. t = dt.time()
  4843. self.assertEqual(t.fold, 1)
  4844. t = dt.timetz()
  4845. self.assertEqual(t.fold, 1)
  4846. def test_replace(self):
  4847. t = time(0)
  4848. dt = datetime(1, 1, 1)
  4849. self.assertEqual(t.replace(fold=1).fold, 1)
  4850. self.assertEqual(dt.replace(fold=1).fold, 1)
  4851. self.assertEqual(t.replace(fold=0).fold, 0)
  4852. self.assertEqual(dt.replace(fold=0).fold, 0)
  4853. # Check that replacement of other fields does not change "fold".
  4854. t = t.replace(fold=1, tzinfo=Eastern)
  4855. dt = dt.replace(fold=1, tzinfo=Eastern)
  4856. self.assertEqual(t.replace(tzinfo=None).fold, 1)
  4857. self.assertEqual(dt.replace(tzinfo=None).fold, 1)
  4858. # Out of bounds.
  4859. with self.assertRaises(ValueError):
  4860. t.replace(fold=2)
  4861. with self.assertRaises(ValueError):
  4862. dt.replace(fold=2)
  4863. # Check that fold is a keyword-only argument
  4864. with self.assertRaises(TypeError):
  4865. t.replace(1, 1, 1, None, 1)
  4866. with self.assertRaises(TypeError):
  4867. dt.replace(1, 1, 1, 1, 1, 1, 1, None, 1)
  4868. def test_comparison(self):
  4869. t = time(0)
  4870. dt = datetime(1, 1, 1)
  4871. self.assertEqual(t, t.replace(fold=1))
  4872. self.assertEqual(dt, dt.replace(fold=1))
  4873. def test_hash(self):
  4874. t = time(0)
  4875. dt = datetime(1, 1, 1)
  4876. self.assertEqual(hash(t), hash(t.replace(fold=1)))
  4877. self.assertEqual(hash(dt), hash(dt.replace(fold=1)))
  4878. @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
  4879. def test_fromtimestamp(self):
  4880. s = 1414906200
  4881. dt0 = datetime.fromtimestamp(s)
  4882. dt1 = datetime.fromtimestamp(s + 3600)
  4883. self.assertEqual(dt0.fold, 0)
  4884. self.assertEqual(dt1.fold, 1)
  4885. @support.run_with_tz('Australia/Lord_Howe')
  4886. def test_fromtimestamp_lord_howe(self):
  4887. tm = _time.localtime(1.4e9)
  4888. if _time.strftime('%Z%z', tm) != 'LHST+1030':
  4889. self.skipTest('Australia/Lord_Howe timezone is not supported on this platform')
  4890. # $ TZ=Australia/Lord_Howe date -r 1428158700
  4891. # Sun Apr 5 01:45:00 LHDT 2015
  4892. # $ TZ=Australia/Lord_Howe date -r 1428160500
  4893. # Sun Apr 5 01:45:00 LHST 2015
  4894. s = 1428158700
  4895. t0 = datetime.fromtimestamp(s)
  4896. t1 = datetime.fromtimestamp(s + 1800)
  4897. self.assertEqual(t0, t1)
  4898. self.assertEqual(t0.fold, 0)
  4899. self.assertEqual(t1.fold, 1)
  4900. def test_fromtimestamp_low_fold_detection(self):
  4901. # Ensure that fold detection doesn't cause an
  4902. # OSError for really low values, see bpo-29097
  4903. self.assertEqual(datetime.fromtimestamp(0).fold, 0)
  4904. @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
  4905. def test_timestamp(self):
  4906. dt0 = datetime(2014, 11, 2, 1, 30)
  4907. dt1 = dt0.replace(fold=1)
  4908. self.assertEqual(dt0.timestamp() + 3600,
  4909. dt1.timestamp())
  4910. @support.run_with_tz('Australia/Lord_Howe')
  4911. def test_timestamp_lord_howe(self):
  4912. tm = _time.localtime(1.4e9)
  4913. if _time.strftime('%Z%z', tm) != 'LHST+1030':
  4914. self.skipTest('Australia/Lord_Howe timezone is not supported on this platform')
  4915. t = datetime(2015, 4, 5, 1, 45)
  4916. s0 = t.replace(fold=0).timestamp()
  4917. s1 = t.replace(fold=1).timestamp()
  4918. self.assertEqual(s0 + 1800, s1)
  4919. @support.run_with_tz('EST+05EDT,M3.2.0,M11.1.0')
  4920. def test_astimezone(self):
  4921. dt0 = datetime(2014, 11, 2, 1, 30)
  4922. dt1 = dt0.replace(fold=1)
  4923. # Convert both naive instances to aware.
  4924. adt0 = dt0.astimezone()
  4925. adt1 = dt1.astimezone()
  4926. # Check that the first instance in DST zone and the second in STD
  4927. self.assertEqual(adt0.tzname(), 'EDT')
  4928. self.assertEqual(adt1.tzname(), 'EST')
  4929. self.assertEqual(adt0 + HOUR, adt1)
  4930. # Aware instances with fixed offset tzinfo's always have fold=0
  4931. self.assertEqual(adt0.fold, 0)
  4932. self.assertEqual(adt1.fold, 0)
  4933. def test_pickle_fold(self):
  4934. t = time(fold=1)
  4935. dt = datetime(1, 1, 1, fold=1)
  4936. for pickler, unpickler, proto in pickle_choices:
  4937. for x in [t, dt]:
  4938. s = pickler.dumps(x, proto)
  4939. y = unpickler.loads(s)
  4940. self.assertEqual(x, y)
  4941. self.assertEqual((0 if proto < 4 else x.fold), y.fold)
  4942. def test_repr(self):
  4943. t = time(fold=1)
  4944. dt = datetime(1, 1, 1, fold=1)
  4945. self.assertEqual(repr(t), 'datetime.time(0, 0, fold=1)')
  4946. self.assertEqual(repr(dt),
  4947. 'datetime.datetime(1, 1, 1, 0, 0, fold=1)')
  4948. def test_dst(self):
  4949. # Let's first establish that things work in regular times.
  4950. dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resolution
  4951. dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2)
  4952. self.assertEqual(dt_summer.dst(), HOUR)
  4953. self.assertEqual(dt_winter.dst(), ZERO)
  4954. # The disambiguation flag is ignored
  4955. self.assertEqual(dt_summer.replace(fold=1).dst(), HOUR)
  4956. self.assertEqual(dt_winter.replace(fold=1).dst(), ZERO)
  4957. # Pick local time in the fold.
  4958. for minute in [0, 30, 59]:
  4959. dt = datetime(2002, 10, 27, 1, minute, tzinfo=Eastern2)
  4960. # With fold=0 (the default) it is in DST.
  4961. self.assertEqual(dt.dst(), HOUR)
  4962. # With fold=1 it is in STD.
  4963. self.assertEqual(dt.replace(fold=1).dst(), ZERO)
  4964. # Pick local time in the gap.
  4965. for minute in [0, 30, 59]:
  4966. dt = datetime(2002, 4, 7, 2, minute, tzinfo=Eastern2)
  4967. # With fold=0 (the default) it is in STD.
  4968. self.assertEqual(dt.dst(), ZERO)
  4969. # With fold=1 it is in DST.
  4970. self.assertEqual(dt.replace(fold=1).dst(), HOUR)
  4971. def test_utcoffset(self):
  4972. # Let's first establish that things work in regular times.
  4973. dt_summer = datetime(2002, 10, 27, 1, tzinfo=Eastern2) - timedelta.resolution
  4974. dt_winter = datetime(2002, 10, 27, 2, tzinfo=Eastern2)
  4975. self.assertEqual(dt_summer.utcoffset(), -4 * HOUR)
  4976. self.assertEqual(dt_winter.utcoffset(), -5 * HOUR)
  4977. # The disambiguation flag is ignored
  4978. self.assertEqual(dt_summer.replace(fold=1).utcoffset(), -4 * HOUR)
  4979. self.assertEqual(dt_winter.replace(fold=1).utcoffset(), -5 * HOUR)
  4980. def test_fromutc(self):
  4981. # Let's first establish that things work in regular times.
  4982. u_summer = datetime(2002, 10, 27, 6, tzinfo=Eastern2) - timedelta.resolution
  4983. u_winter = datetime(2002, 10, 27, 7, tzinfo=Eastern2)
  4984. t_summer = Eastern2.fromutc(u_summer)
  4985. t_winter = Eastern2.fromutc(u_winter)
  4986. self.assertEqual(t_summer, u_summer - 4 * HOUR)
  4987. self.assertEqual(t_winter, u_winter - 5 * HOUR)
  4988. self.assertEqual(t_summer.fold, 0)
  4989. self.assertEqual(t_winter.fold, 0)
  4990. # What happens in the fall-back fold?
  4991. u = datetime(2002, 10, 27, 5, 30, tzinfo=Eastern2)
  4992. t0 = Eastern2.fromutc(u)
  4993. u += HOUR
  4994. t1 = Eastern2.fromutc(u)
  4995. self.assertEqual(t0, t1)
  4996. self.assertEqual(t0.fold, 0)
  4997. self.assertEqual(t1.fold, 1)
  4998. # The tricky part is when u is in the local fold:
  4999. u = datetime(2002, 10, 27, 1, 30, tzinfo=Eastern2)
  5000. t = Eastern2.fromutc(u)
  5001. self.assertEqual((t.day, t.hour), (26, 21))
  5002. # .. or gets into the local fold after a standard time adjustment
  5003. u = datetime(2002, 10, 27, 6, 30, tzinfo=Eastern2)
  5004. t = Eastern2.fromutc(u)
  5005. self.assertEqual((t.day, t.hour), (27, 1))
  5006. # What happens in the spring-forward gap?
  5007. u = datetime(2002, 4, 7, 2, 0, tzinfo=Eastern2)
  5008. t = Eastern2.fromutc(u)
  5009. self.assertEqual((t.day, t.hour), (6, 21))
  5010. def test_mixed_compare_regular(self):
  5011. t = datetime(2000, 1, 1, tzinfo=Eastern2)
  5012. self.assertEqual(t, t.astimezone(timezone.utc))
  5013. t = datetime(2000, 6, 1, tzinfo=Eastern2)
  5014. self.assertEqual(t, t.astimezone(timezone.utc))
  5015. def test_mixed_compare_fold(self):
  5016. t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2)
  5017. t_fold_utc = t_fold.astimezone(timezone.utc)
  5018. self.assertNotEqual(t_fold, t_fold_utc)
  5019. self.assertNotEqual(t_fold_utc, t_fold)
  5020. def test_mixed_compare_gap(self):
  5021. t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2)
  5022. t_gap_utc = t_gap.astimezone(timezone.utc)
  5023. self.assertNotEqual(t_gap, t_gap_utc)
  5024. self.assertNotEqual(t_gap_utc, t_gap)
  5025. def test_hash_aware(self):
  5026. t = datetime(2000, 1, 1, tzinfo=Eastern2)
  5027. self.assertEqual(hash(t), hash(t.replace(fold=1)))
  5028. t_fold = datetime(2002, 10, 27, 1, 45, tzinfo=Eastern2)
  5029. t_gap = datetime(2002, 4, 7, 2, 45, tzinfo=Eastern2)
  5030. self.assertEqual(hash(t_fold), hash(t_fold.replace(fold=1)))
  5031. self.assertEqual(hash(t_gap), hash(t_gap.replace(fold=1)))
  5032. SEC = timedelta(0, 1)
  5033. def pairs(iterable):
  5034. a, b = itertools.tee(iterable)
  5035. next(b, None)
  5036. return zip(a, b)
  5037. class ZoneInfo(tzinfo):
  5038. zoneroot = '/usr/share/zoneinfo'
  5039. def __init__(self, ut, ti):
  5040. """
  5041. :param ut: array
  5042. Array of transition point timestamps
  5043. :param ti: list
  5044. A list of (offset, isdst, abbr) tuples
  5045. :return: None
  5046. """
  5047. self.ut = ut
  5048. self.ti = ti
  5049. self.lt = self.invert(ut, ti)
  5050. @staticmethod
  5051. def invert(ut, ti):
  5052. lt = (array('q', ut), array('q', ut))
  5053. if ut:
  5054. offset = ti[0][0] // SEC
  5055. lt[0][0] += offset
  5056. lt[1][0] += offset
  5057. for i in range(1, len(ut)):
  5058. lt[0][i] += ti[i-1][0] // SEC
  5059. lt[1][i] += ti[i][0] // SEC
  5060. return lt
  5061. @classmethod
  5062. def fromfile(cls, fileobj):
  5063. if fileobj.read(4).decode() != "TZif":
  5064. raise ValueError("not a zoneinfo file")
  5065. fileobj.seek(32)
  5066. counts = array('i')
  5067. counts.fromfile(fileobj, 3)
  5068. if sys.byteorder != 'big':
  5069. counts.byteswap()
  5070. ut = array('i')
  5071. ut.fromfile(fileobj, counts[0])
  5072. if sys.byteorder != 'big':
  5073. ut.byteswap()
  5074. type_indices = array('B')
  5075. type_indices.fromfile(fileobj, counts[0])
  5076. ttis = []
  5077. for i in range(counts[1]):
  5078. ttis.append(struct.unpack(">lbb", fileobj.read(6)))
  5079. abbrs = fileobj.read(counts[2])
  5080. # Convert ttis
  5081. for i, (gmtoff, isdst, abbrind) in enumerate(ttis):
  5082. abbr = abbrs[abbrind:abbrs.find(0, abbrind)].decode()
  5083. ttis[i] = (timedelta(0, gmtoff), isdst, abbr)
  5084. ti = [None] * len(ut)
  5085. for i, idx in enumerate(type_indices):
  5086. ti[i] = ttis[idx]
  5087. self = cls(ut, ti)
  5088. return self
  5089. @classmethod
  5090. def fromname(cls, name):
  5091. path = os.path.join(cls.zoneroot, name)
  5092. with open(path, 'rb') as f:
  5093. return cls.fromfile(f)
  5094. EPOCHORDINAL = date(1970, 1, 1).toordinal()
  5095. def fromutc(self, dt):
  5096. """datetime in UTC -> datetime in local time."""
  5097. if not isinstance(dt, datetime):
  5098. raise TypeError("fromutc() requires a datetime argument")
  5099. if dt.tzinfo is not self:
  5100. raise ValueError("dt.tzinfo is not self")
  5101. timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400
  5102. + dt.hour * 3600
  5103. + dt.minute * 60
  5104. + dt.second)
  5105. if timestamp < self.ut[1]:
  5106. tti = self.ti[0]
  5107. fold = 0
  5108. else:
  5109. idx = bisect.bisect_right(self.ut, timestamp)
  5110. assert self.ut[idx-1] <= timestamp
  5111. assert idx == len(self.ut) or timestamp < self.ut[idx]
  5112. tti_prev, tti = self.ti[idx-2:idx]
  5113. # Detect fold
  5114. shift = tti_prev[0] - tti[0]
  5115. fold = (shift > timedelta(0, timestamp - self.ut[idx-1]))
  5116. dt += tti[0]
  5117. if fold:
  5118. return dt.replace(fold=1)
  5119. else:
  5120. return dt
  5121. def _find_ti(self, dt, i):
  5122. timestamp = ((dt.toordinal() - self.EPOCHORDINAL) * 86400
  5123. + dt.hour * 3600
  5124. + dt.minute * 60
  5125. + dt.second)
  5126. lt = self.lt[dt.fold]
  5127. idx = bisect.bisect_right(lt, timestamp)
  5128. return self.ti[max(0, idx - 1)][i]
  5129. def utcoffset(self, dt):
  5130. return self._find_ti(dt, 0)
  5131. def dst(self, dt):
  5132. isdst = self._find_ti(dt, 1)
  5133. # XXX: We cannot accurately determine the "save" value,
  5134. # so let's return 1h whenever DST is in effect. Since
  5135. # we don't use dst() in fromutc(), it is unlikely that
  5136. # it will be needed for anything more than bool(dst()).
  5137. return ZERO if isdst else HOUR
  5138. def tzname(self, dt):
  5139. return self._find_ti(dt, 2)
  5140. @classmethod
  5141. def zonenames(cls, zonedir=None):
  5142. if zonedir is None:
  5143. zonedir = cls.zoneroot
  5144. zone_tab = os.path.join(zonedir, 'zone.tab')
  5145. try:
  5146. f = open(zone_tab)
  5147. except OSError:
  5148. return
  5149. with f:
  5150. for line in f:
  5151. line = line.strip()
  5152. if line and not line.startswith('#'):
  5153. yield line.split()[2]
  5154. @classmethod
  5155. def stats(cls, start_year=1):
  5156. count = gap_count = fold_count = zeros_count = 0
  5157. min_gap = min_fold = timedelta.max
  5158. max_gap = max_fold = ZERO
  5159. min_gap_datetime = max_gap_datetime = datetime.min
  5160. min_gap_zone = max_gap_zone = None
  5161. min_fold_datetime = max_fold_datetime = datetime.min
  5162. min_fold_zone = max_fold_zone = None
  5163. stats_since = datetime(start_year, 1, 1) # Starting from 1970 eliminates a lot of noise
  5164. for zonename in cls.zonenames():
  5165. count += 1
  5166. tz = cls.fromname(zonename)
  5167. for dt, shift in tz.transitions():
  5168. if dt < stats_since:
  5169. continue
  5170. if shift > ZERO:
  5171. gap_count += 1
  5172. if (shift, dt) > (max_gap, max_gap_datetime):
  5173. max_gap = shift
  5174. max_gap_zone = zonename
  5175. max_gap_datetime = dt
  5176. if (shift, datetime.max - dt) < (min_gap, datetime.max - min_gap_datetime):
  5177. min_gap = shift
  5178. min_gap_zone = zonename
  5179. min_gap_datetime = dt
  5180. elif shift < ZERO:
  5181. fold_count += 1
  5182. shift = -shift
  5183. if (shift, dt) > (max_fold, max_fold_datetime):
  5184. max_fold = shift
  5185. max_fold_zone = zonename
  5186. max_fold_datetime = dt
  5187. if (shift, datetime.max - dt) < (min_fold, datetime.max - min_fold_datetime):
  5188. min_fold = shift
  5189. min_fold_zone = zonename
  5190. min_fold_datetime = dt
  5191. else:
  5192. zeros_count += 1
  5193. trans_counts = (gap_count, fold_count, zeros_count)
  5194. print("Number of zones: %5d" % count)
  5195. print("Number of transitions: %5d = %d (gaps) + %d (folds) + %d (zeros)" %
  5196. ((sum(trans_counts),) + trans_counts))
  5197. print("Min gap: %16s at %s in %s" % (min_gap, min_gap_datetime, min_gap_zone))
  5198. print("Max gap: %16s at %s in %s" % (max_gap, max_gap_datetime, max_gap_zone))
  5199. print("Min fold: %16s at %s in %s" % (min_fold, min_fold_datetime, min_fold_zone))
  5200. print("Max fold: %16s at %s in %s" % (max_fold, max_fold_datetime, max_fold_zone))
  5201. def transitions(self):
  5202. for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)):
  5203. shift = ti[0] - prev_ti[0]
  5204. yield datetime.utcfromtimestamp(t), shift
  5205. def nondst_folds(self):
  5206. """Find all folds with the same value of isdst on both sides of the transition."""
  5207. for (_, prev_ti), (t, ti) in pairs(zip(self.ut, self.ti)):
  5208. shift = ti[0] - prev_ti[0]
  5209. if shift < ZERO and ti[1] == prev_ti[1]:
  5210. yield datetime.utcfromtimestamp(t), -shift, prev_ti[2], ti[2]
  5211. @classmethod
  5212. def print_all_nondst_folds(cls, same_abbr=False, start_year=1):
  5213. count = 0
  5214. for zonename in cls.zonenames():
  5215. tz = cls.fromname(zonename)
  5216. for dt, shift, prev_abbr, abbr in tz.nondst_folds():
  5217. if dt.year < start_year or same_abbr and prev_abbr != abbr:
  5218. continue
  5219. count += 1
  5220. print("%3d) %-30s %s %10s %5s -> %s" %
  5221. (count, zonename, dt, shift, prev_abbr, abbr))
  5222. def folds(self):
  5223. for t, shift in self.transitions():
  5224. if shift < ZERO:
  5225. yield t, -shift
  5226. def gaps(self):
  5227. for t, shift in self.transitions():
  5228. if shift > ZERO:
  5229. yield t, shift
  5230. def zeros(self):
  5231. for t, shift in self.transitions():
  5232. if not shift:
  5233. yield t
  5234. class ZoneInfoTest(unittest.TestCase):
  5235. zonename = 'America/New_York'
  5236. def setUp(self):
  5237. if sys.platform == "vxworks":
  5238. self.skipTest("Skipping zoneinfo tests on VxWorks")
  5239. if sys.platform == "win32":
  5240. self.skipTest("Skipping zoneinfo tests on Windows")
  5241. try:
  5242. self.tz = ZoneInfo.fromname(self.zonename)
  5243. except FileNotFoundError as err:
  5244. self.skipTest("Skipping %s: %s" % (self.zonename, err))
  5245. def assertEquivDatetimes(self, a, b):
  5246. self.assertEqual((a.replace(tzinfo=None), a.fold, id(a.tzinfo)),
  5247. (b.replace(tzinfo=None), b.fold, id(b.tzinfo)))
  5248. def test_folds(self):
  5249. tz = self.tz
  5250. for dt, shift in tz.folds():
  5251. for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]:
  5252. udt = dt + x
  5253. ldt = tz.fromutc(udt.replace(tzinfo=tz))
  5254. self.assertEqual(ldt.fold, 1)
  5255. adt = udt.replace(tzinfo=timezone.utc).astimezone(tz)
  5256. self.assertEquivDatetimes(adt, ldt)
  5257. utcoffset = ldt.utcoffset()
  5258. self.assertEqual(ldt.replace(tzinfo=None), udt + utcoffset)
  5259. # Round trip
  5260. self.assertEquivDatetimes(ldt.astimezone(timezone.utc),
  5261. udt.replace(tzinfo=timezone.utc))
  5262. for x in [-timedelta.resolution, shift]:
  5263. udt = dt + x
  5264. udt = udt.replace(tzinfo=tz)
  5265. ldt = tz.fromutc(udt)
  5266. self.assertEqual(ldt.fold, 0)
  5267. def test_gaps(self):
  5268. tz = self.tz
  5269. for dt, shift in tz.gaps():
  5270. for x in [0 * shift, 0.5 * shift, shift - timedelta.resolution]:
  5271. udt = dt + x
  5272. udt = udt.replace(tzinfo=tz)
  5273. ldt = tz.fromutc(udt)
  5274. self.assertEqual(ldt.fold, 0)
  5275. adt = udt.replace(tzinfo=timezone.utc).astimezone(tz)
  5276. self.assertEquivDatetimes(adt, ldt)
  5277. utcoffset = ldt.utcoffset()
  5278. self.assertEqual(ldt.replace(tzinfo=None), udt.replace(tzinfo=None) + utcoffset)
  5279. # Create a local time inside the gap
  5280. ldt = tz.fromutc(dt.replace(tzinfo=tz)) - shift + x
  5281. self.assertLess(ldt.replace(fold=1).utcoffset(),
  5282. ldt.replace(fold=0).utcoffset(),
  5283. "At %s." % ldt)
  5284. for x in [-timedelta.resolution, shift]:
  5285. udt = dt + x
  5286. ldt = tz.fromutc(udt.replace(tzinfo=tz))
  5287. self.assertEqual(ldt.fold, 0)
  5288. @unittest.skipUnless(
  5289. hasattr(time, "tzset"), "time module has no attribute tzset"
  5290. )
  5291. def test_system_transitions(self):
  5292. if ('Riyadh8' in self.zonename or
  5293. # From tzdata NEWS file:
  5294. # The files solar87, solar88, and solar89 are no longer distributed.
  5295. # They were a negative experiment - that is, a demonstration that
  5296. # tz data can represent solar time only with some difficulty and error.
  5297. # Their presence in the distribution caused confusion, as Riyadh
  5298. # civil time was generally not solar time in those years.
  5299. self.zonename.startswith('right/')):
  5300. self.skipTest("Skipping %s" % self.zonename)
  5301. tz = self.tz
  5302. TZ = os.environ.get('TZ')
  5303. os.environ['TZ'] = self.zonename
  5304. try:
  5305. _time.tzset()
  5306. for udt, shift in tz.transitions():
  5307. if udt.year >= 2037:
  5308. # System support for times around the end of 32-bit time_t
  5309. # and later is flaky on many systems.
  5310. break
  5311. s0 = (udt - datetime(1970, 1, 1)) // SEC
  5312. ss = shift // SEC # shift seconds
  5313. for x in [-40 * 3600, -20*3600, -1, 0,
  5314. ss - 1, ss + 20 * 3600, ss + 40 * 3600]:
  5315. s = s0 + x
  5316. sdt = datetime.fromtimestamp(s)
  5317. tzdt = datetime.fromtimestamp(s, tz).replace(tzinfo=None)
  5318. self.assertEquivDatetimes(sdt, tzdt)
  5319. s1 = sdt.timestamp()
  5320. self.assertEqual(s, s1)
  5321. if ss > 0: # gap
  5322. # Create local time inside the gap
  5323. dt = datetime.fromtimestamp(s0) - shift / 2
  5324. ts0 = dt.timestamp()
  5325. ts1 = dt.replace(fold=1).timestamp()
  5326. self.assertEqual(ts0, s0 + ss / 2)
  5327. self.assertEqual(ts1, s0 - ss / 2)
  5328. finally:
  5329. if TZ is None:
  5330. del os.environ['TZ']
  5331. else:
  5332. os.environ['TZ'] = TZ
  5333. _time.tzset()
  5334. class ZoneInfoCompleteTest(unittest.TestSuite):
  5335. def __init__(self):
  5336. tests = []
  5337. if is_resource_enabled('tzdata'):
  5338. for name in ZoneInfo.zonenames():
  5339. Test = type('ZoneInfoTest[%s]' % name, (ZoneInfoTest,), {})
  5340. Test.zonename = name
  5341. for method in dir(Test):
  5342. if method.startswith('test_'):
  5343. tests.append(Test(method))
  5344. super().__init__(tests)
  5345. # Iran had a sub-minute UTC offset before 1946.
  5346. class IranTest(ZoneInfoTest):
  5347. zonename = 'Asia/Tehran'
  5348. @unittest.skipIf(_testcapi is None, 'need _testcapi module')
  5349. class CapiTest(unittest.TestCase):
  5350. def setUp(self):
  5351. # Since the C API is not present in the _Pure tests, skip all tests
  5352. if self.__class__.__name__.endswith('Pure'):
  5353. self.skipTest('Not relevant in pure Python')
  5354. # This *must* be called, and it must be called first, so until either
  5355. # restriction is loosened, we'll call it as part of test setup
  5356. _testcapi.test_datetime_capi()
  5357. def test_utc_capi(self):
  5358. for use_macro in (True, False):
  5359. capi_utc = _testcapi.get_timezone_utc_capi(use_macro)
  5360. with self.subTest(use_macro=use_macro):
  5361. self.assertIs(capi_utc, timezone.utc)
  5362. def test_timezones_capi(self):
  5363. est_capi, est_macro, est_macro_nn = _testcapi.make_timezones_capi()
  5364. exp_named = timezone(timedelta(hours=-5), "EST")
  5365. exp_unnamed = timezone(timedelta(hours=-5))
  5366. cases = [
  5367. ('est_capi', est_capi, exp_named),
  5368. ('est_macro', est_macro, exp_named),
  5369. ('est_macro_nn', est_macro_nn, exp_unnamed)
  5370. ]
  5371. for name, tz_act, tz_exp in cases:
  5372. with self.subTest(name=name):
  5373. self.assertEqual(tz_act, tz_exp)
  5374. dt1 = datetime(2000, 2, 4, tzinfo=tz_act)
  5375. dt2 = datetime(2000, 2, 4, tzinfo=tz_exp)
  5376. self.assertEqual(dt1, dt2)
  5377. self.assertEqual(dt1.tzname(), dt2.tzname())
  5378. dt_utc = datetime(2000, 2, 4, 5, tzinfo=timezone.utc)
  5379. self.assertEqual(dt1.astimezone(timezone.utc), dt_utc)
  5380. def test_PyDateTime_DELTA_GET(self):
  5381. class TimeDeltaSubclass(timedelta):
  5382. pass
  5383. for klass in [timedelta, TimeDeltaSubclass]:
  5384. for args in [(26, 55, 99999), (26, 55, 99999)]:
  5385. d = klass(*args)
  5386. with self.subTest(cls=klass, date=args):
  5387. days, seconds, microseconds = _testcapi.PyDateTime_DELTA_GET(d)
  5388. self.assertEqual(days, d.days)
  5389. self.assertEqual(seconds, d.seconds)
  5390. self.assertEqual(microseconds, d.microseconds)
  5391. def test_PyDateTime_GET(self):
  5392. class DateSubclass(date):
  5393. pass
  5394. for klass in [date, DateSubclass]:
  5395. for args in [(2000, 1, 2), (2012, 2, 29)]:
  5396. d = klass(*args)
  5397. with self.subTest(cls=klass, date=args):
  5398. year, month, day = _testcapi.PyDateTime_GET(d)
  5399. self.assertEqual(year, d.year)
  5400. self.assertEqual(month, d.month)
  5401. self.assertEqual(day, d.day)
  5402. def test_PyDateTime_DATE_GET(self):
  5403. class DateTimeSubclass(datetime):
  5404. pass
  5405. for klass in [datetime, DateTimeSubclass]:
  5406. for args in [(1993, 8, 26, 22, 12, 55, 99999),
  5407. (1993, 8, 26, 22, 12, 55, 99999,
  5408. timezone.utc)]:
  5409. d = klass(*args)
  5410. with self.subTest(cls=klass, date=args):
  5411. hour, minute, second, microsecond, tzinfo = \
  5412. _testcapi.PyDateTime_DATE_GET(d)
  5413. self.assertEqual(hour, d.hour)
  5414. self.assertEqual(minute, d.minute)
  5415. self.assertEqual(second, d.second)
  5416. self.assertEqual(microsecond, d.microsecond)
  5417. self.assertIs(tzinfo, d.tzinfo)
  5418. def test_PyDateTime_TIME_GET(self):
  5419. class TimeSubclass(time):
  5420. pass
  5421. for klass in [time, TimeSubclass]:
  5422. for args in [(12, 30, 20, 10),
  5423. (12, 30, 20, 10, timezone.utc)]:
  5424. d = klass(*args)
  5425. with self.subTest(cls=klass, date=args):
  5426. hour, minute, second, microsecond, tzinfo = \
  5427. _testcapi.PyDateTime_TIME_GET(d)
  5428. self.assertEqual(hour, d.hour)
  5429. self.assertEqual(minute, d.minute)
  5430. self.assertEqual(second, d.second)
  5431. self.assertEqual(microsecond, d.microsecond)
  5432. self.assertIs(tzinfo, d.tzinfo)
  5433. def test_timezones_offset_zero(self):
  5434. utc0, utc1, non_utc = _testcapi.get_timezones_offset_zero()
  5435. with self.subTest(testname="utc0"):
  5436. self.assertIs(utc0, timezone.utc)
  5437. with self.subTest(testname="utc1"):
  5438. self.assertIs(utc1, timezone.utc)
  5439. with self.subTest(testname="non_utc"):
  5440. self.assertIsNot(non_utc, timezone.utc)
  5441. non_utc_exp = timezone(timedelta(hours=0), "")
  5442. self.assertEqual(non_utc, non_utc_exp)
  5443. dt1 = datetime(2000, 2, 4, tzinfo=non_utc)
  5444. dt2 = datetime(2000, 2, 4, tzinfo=non_utc_exp)
  5445. self.assertEqual(dt1, dt2)
  5446. self.assertEqual(dt1.tzname(), dt2.tzname())
  5447. def test_check_date(self):
  5448. class DateSubclass(date):
  5449. pass
  5450. d = date(2011, 1, 1)
  5451. ds = DateSubclass(2011, 1, 1)
  5452. dt = datetime(2011, 1, 1)
  5453. is_date = _testcapi.datetime_check_date
  5454. # Check the ones that should be valid
  5455. self.assertTrue(is_date(d))
  5456. self.assertTrue(is_date(dt))
  5457. self.assertTrue(is_date(ds))
  5458. self.assertTrue(is_date(d, True))
  5459. # Check that the subclasses do not match exactly
  5460. self.assertFalse(is_date(dt, True))
  5461. self.assertFalse(is_date(ds, True))
  5462. # Check that various other things are not dates at all
  5463. args = [tuple(), list(), 1, '2011-01-01',
  5464. timedelta(1), timezone.utc, time(12, 00)]
  5465. for arg in args:
  5466. for exact in (True, False):
  5467. with self.subTest(arg=arg, exact=exact):
  5468. self.assertFalse(is_date(arg, exact))
  5469. def test_check_time(self):
  5470. class TimeSubclass(time):
  5471. pass
  5472. t = time(12, 30)
  5473. ts = TimeSubclass(12, 30)
  5474. is_time = _testcapi.datetime_check_time
  5475. # Check the ones that should be valid
  5476. self.assertTrue(is_time(t))
  5477. self.assertTrue(is_time(ts))
  5478. self.assertTrue(is_time(t, True))
  5479. # Check that the subclass does not match exactly
  5480. self.assertFalse(is_time(ts, True))
  5481. # Check that various other things are not times
  5482. args = [tuple(), list(), 1, '2011-01-01',
  5483. timedelta(1), timezone.utc, date(2011, 1, 1)]
  5484. for arg in args:
  5485. for exact in (True, False):
  5486. with self.subTest(arg=arg, exact=exact):
  5487. self.assertFalse(is_time(arg, exact))
  5488. def test_check_datetime(self):
  5489. class DateTimeSubclass(datetime):
  5490. pass
  5491. dt = datetime(2011, 1, 1, 12, 30)
  5492. dts = DateTimeSubclass(2011, 1, 1, 12, 30)
  5493. is_datetime = _testcapi.datetime_check_datetime
  5494. # Check the ones that should be valid
  5495. self.assertTrue(is_datetime(dt))
  5496. self.assertTrue(is_datetime(dts))
  5497. self.assertTrue(is_datetime(dt, True))
  5498. # Check that the subclass does not match exactly
  5499. self.assertFalse(is_datetime(dts, True))
  5500. # Check that various other things are not datetimes
  5501. args = [tuple(), list(), 1, '2011-01-01',
  5502. timedelta(1), timezone.utc, date(2011, 1, 1)]
  5503. for arg in args:
  5504. for exact in (True, False):
  5505. with self.subTest(arg=arg, exact=exact):
  5506. self.assertFalse(is_datetime(arg, exact))
  5507. def test_check_delta(self):
  5508. class TimeDeltaSubclass(timedelta):
  5509. pass
  5510. td = timedelta(1)
  5511. tds = TimeDeltaSubclass(1)
  5512. is_timedelta = _testcapi.datetime_check_delta
  5513. # Check the ones that should be valid
  5514. self.assertTrue(is_timedelta(td))
  5515. self.assertTrue(is_timedelta(tds))
  5516. self.assertTrue(is_timedelta(td, True))
  5517. # Check that the subclass does not match exactly
  5518. self.assertFalse(is_timedelta(tds, True))
  5519. # Check that various other things are not timedeltas
  5520. args = [tuple(), list(), 1, '2011-01-01',
  5521. timezone.utc, date(2011, 1, 1), datetime(2011, 1, 1)]
  5522. for arg in args:
  5523. for exact in (True, False):
  5524. with self.subTest(arg=arg, exact=exact):
  5525. self.assertFalse(is_timedelta(arg, exact))
  5526. def test_check_tzinfo(self):
  5527. class TZInfoSubclass(tzinfo):
  5528. pass
  5529. tzi = tzinfo()
  5530. tzis = TZInfoSubclass()
  5531. tz = timezone(timedelta(hours=-5))
  5532. is_tzinfo = _testcapi.datetime_check_tzinfo
  5533. # Check the ones that should be valid
  5534. self.assertTrue(is_tzinfo(tzi))
  5535. self.assertTrue(is_tzinfo(tz))
  5536. self.assertTrue(is_tzinfo(tzis))
  5537. self.assertTrue(is_tzinfo(tzi, True))
  5538. # Check that the subclasses do not match exactly
  5539. self.assertFalse(is_tzinfo(tz, True))
  5540. self.assertFalse(is_tzinfo(tzis, True))
  5541. # Check that various other things are not tzinfos
  5542. args = [tuple(), list(), 1, '2011-01-01',
  5543. date(2011, 1, 1), datetime(2011, 1, 1)]
  5544. for arg in args:
  5545. for exact in (True, False):
  5546. with self.subTest(arg=arg, exact=exact):
  5547. self.assertFalse(is_tzinfo(arg, exact))
  5548. def test_date_from_date(self):
  5549. exp_date = date(1993, 8, 26)
  5550. for macro in False, True:
  5551. with self.subTest(macro=macro):
  5552. c_api_date = _testcapi.get_date_fromdate(
  5553. macro,
  5554. exp_date.year,
  5555. exp_date.month,
  5556. exp_date.day)
  5557. self.assertEqual(c_api_date, exp_date)
  5558. def test_datetime_from_dateandtime(self):
  5559. exp_date = datetime(1993, 8, 26, 22, 12, 55, 99999)
  5560. for macro in False, True:
  5561. with self.subTest(macro=macro):
  5562. c_api_date = _testcapi.get_datetime_fromdateandtime(
  5563. macro,
  5564. exp_date.year,
  5565. exp_date.month,
  5566. exp_date.day,
  5567. exp_date.hour,
  5568. exp_date.minute,
  5569. exp_date.second,
  5570. exp_date.microsecond)
  5571. self.assertEqual(c_api_date, exp_date)
  5572. def test_datetime_from_dateandtimeandfold(self):
  5573. exp_date = datetime(1993, 8, 26, 22, 12, 55, 99999)
  5574. for fold in [0, 1]:
  5575. for macro in False, True:
  5576. with self.subTest(macro=macro, fold=fold):
  5577. c_api_date = _testcapi.get_datetime_fromdateandtimeandfold(
  5578. macro,
  5579. exp_date.year,
  5580. exp_date.month,
  5581. exp_date.day,
  5582. exp_date.hour,
  5583. exp_date.minute,
  5584. exp_date.second,
  5585. exp_date.microsecond,
  5586. exp_date.fold)
  5587. self.assertEqual(c_api_date, exp_date)
  5588. self.assertEqual(c_api_date.fold, exp_date.fold)
  5589. def test_time_from_time(self):
  5590. exp_time = time(22, 12, 55, 99999)
  5591. for macro in False, True:
  5592. with self.subTest(macro=macro):
  5593. c_api_time = _testcapi.get_time_fromtime(
  5594. macro,
  5595. exp_time.hour,
  5596. exp_time.minute,
  5597. exp_time.second,
  5598. exp_time.microsecond)
  5599. self.assertEqual(c_api_time, exp_time)
  5600. def test_time_from_timeandfold(self):
  5601. exp_time = time(22, 12, 55, 99999)
  5602. for fold in [0, 1]:
  5603. for macro in False, True:
  5604. with self.subTest(macro=macro, fold=fold):
  5605. c_api_time = _testcapi.get_time_fromtimeandfold(
  5606. macro,
  5607. exp_time.hour,
  5608. exp_time.minute,
  5609. exp_time.second,
  5610. exp_time.microsecond,
  5611. exp_time.fold)
  5612. self.assertEqual(c_api_time, exp_time)
  5613. self.assertEqual(c_api_time.fold, exp_time.fold)
  5614. def test_delta_from_dsu(self):
  5615. exp_delta = timedelta(26, 55, 99999)
  5616. for macro in False, True:
  5617. with self.subTest(macro=macro):
  5618. c_api_delta = _testcapi.get_delta_fromdsu(
  5619. macro,
  5620. exp_delta.days,
  5621. exp_delta.seconds,
  5622. exp_delta.microseconds)
  5623. self.assertEqual(c_api_delta, exp_delta)
  5624. def test_date_from_timestamp(self):
  5625. ts = datetime(1995, 4, 12).timestamp()
  5626. for macro in False, True:
  5627. with self.subTest(macro=macro):
  5628. d = _testcapi.get_date_fromtimestamp(int(ts), macro)
  5629. self.assertEqual(d, date(1995, 4, 12))
  5630. def test_datetime_from_timestamp(self):
  5631. cases = [
  5632. ((1995, 4, 12), None, False),
  5633. ((1995, 4, 12), None, True),
  5634. ((1995, 4, 12), timezone(timedelta(hours=1)), True),
  5635. ((1995, 4, 12, 14, 30), None, False),
  5636. ((1995, 4, 12, 14, 30), None, True),
  5637. ((1995, 4, 12, 14, 30), timezone(timedelta(hours=1)), True),
  5638. ]
  5639. from_timestamp = _testcapi.get_datetime_fromtimestamp
  5640. for case in cases:
  5641. for macro in False, True:
  5642. with self.subTest(case=case, macro=macro):
  5643. dtup, tzinfo, usetz = case
  5644. dt_orig = datetime(*dtup, tzinfo=tzinfo)
  5645. ts = int(dt_orig.timestamp())
  5646. dt_rt = from_timestamp(ts, tzinfo, usetz, macro)
  5647. self.assertEqual(dt_orig, dt_rt)
  5648. def load_tests(loader, standard_tests, pattern):
  5649. standard_tests.addTest(ZoneInfoCompleteTest())
  5650. return standard_tests
  5651. if __name__ == "__main__":
  5652. unittest.main()