test_offsets.py 127 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143
  1. from datetime import date, datetime, timedelta
  2. from distutils.version import LooseVersion
  3. import numpy as np
  4. import pytest
  5. from pandas._libs.tslibs import (
  6. NaT, OutOfBoundsDatetime, Timestamp, conversion, timezones)
  7. from pandas._libs.tslibs.frequencies import (
  8. INVALID_FREQ_ERR_MSG, get_freq_code, get_freq_str)
  9. import pandas._libs.tslibs.offsets as liboffsets
  10. import pandas.compat as compat
  11. from pandas.compat import range
  12. from pandas.compat.numpy import np_datetime64_compat
  13. from pandas.core.indexes.datetimes import DatetimeIndex, _to_M8, date_range
  14. from pandas.core.series import Series
  15. import pandas.util.testing as tm
  16. from pandas.io.pickle import read_pickle
  17. from pandas.tseries.frequencies import _offset_map, get_offset
  18. from pandas.tseries.holiday import USFederalHolidayCalendar
  19. import pandas.tseries.offsets as offsets
  20. from pandas.tseries.offsets import (
  21. FY5253, BDay, BMonthBegin, BMonthEnd, BQuarterBegin, BQuarterEnd,
  22. BusinessHour, BYearBegin, BYearEnd, CBMonthBegin, CBMonthEnd, CDay,
  23. CustomBusinessHour, DateOffset, Day, Easter, FY5253Quarter,
  24. LastWeekOfMonth, MonthBegin, MonthEnd, Nano, QuarterBegin, QuarterEnd,
  25. SemiMonthBegin, SemiMonthEnd, Tick, Week, WeekOfMonth, YearBegin, YearEnd)
  26. from .common import assert_offset_equal, assert_onOffset
  27. class WeekDay(object):
  28. # TODO: Remove: This is not used outside of tests
  29. MON = 0
  30. TUE = 1
  31. WED = 2
  32. THU = 3
  33. FRI = 4
  34. SAT = 5
  35. SUN = 6
  36. ####
  37. # Misc function tests
  38. ####
  39. def test_to_M8():
  40. valb = datetime(2007, 10, 1)
  41. valu = _to_M8(valb)
  42. assert isinstance(valu, np.datetime64)
  43. #####
  44. # DateOffset Tests
  45. #####
  46. class Base(object):
  47. _offset = None
  48. d = Timestamp(datetime(2008, 1, 2))
  49. timezones = [None, 'UTC', 'Asia/Tokyo', 'US/Eastern',
  50. 'dateutil/Asia/Tokyo', 'dateutil/US/Pacific']
  51. def _get_offset(self, klass, value=1, normalize=False):
  52. # create instance from offset class
  53. if klass is FY5253:
  54. klass = klass(n=value, startingMonth=1, weekday=1,
  55. variation='last', normalize=normalize)
  56. elif klass is FY5253Quarter:
  57. klass = klass(n=value, startingMonth=1, weekday=1,
  58. qtr_with_extra_week=1, variation='last',
  59. normalize=normalize)
  60. elif klass is LastWeekOfMonth:
  61. klass = klass(n=value, weekday=5, normalize=normalize)
  62. elif klass is WeekOfMonth:
  63. klass = klass(n=value, week=1, weekday=5, normalize=normalize)
  64. elif klass is Week:
  65. klass = klass(n=value, weekday=5, normalize=normalize)
  66. elif klass is DateOffset:
  67. klass = klass(days=value, normalize=normalize)
  68. else:
  69. try:
  70. klass = klass(value, normalize=normalize)
  71. except Exception:
  72. klass = klass(normalize=normalize)
  73. return klass
  74. def test_apply_out_of_range(self, tz_naive_fixture):
  75. tz = tz_naive_fixture
  76. if self._offset is None:
  77. return
  78. # try to create an out-of-bounds result timestamp; if we can't create
  79. # the offset skip
  80. try:
  81. if self._offset in (BusinessHour, CustomBusinessHour):
  82. # Using 10000 in BusinessHour fails in tz check because of DST
  83. # difference
  84. offset = self._get_offset(self._offset, value=100000)
  85. else:
  86. offset = self._get_offset(self._offset, value=10000)
  87. result = Timestamp('20080101') + offset
  88. assert isinstance(result, datetime)
  89. assert result.tzinfo is None
  90. # Check tz is preserved
  91. t = Timestamp('20080101', tz=tz)
  92. result = t + offset
  93. assert isinstance(result, datetime)
  94. assert t.tzinfo == result.tzinfo
  95. except OutOfBoundsDatetime:
  96. raise
  97. except (ValueError, KeyError):
  98. # we are creating an invalid offset
  99. # so ignore
  100. pass
  101. def test_offsets_compare_equal(self):
  102. # root cause of GH#456: __ne__ was not implemented
  103. if self._offset is None:
  104. return
  105. offset1 = self._offset()
  106. offset2 = self._offset()
  107. assert not offset1 != offset2
  108. assert offset1 == offset2
  109. def test_rsub(self):
  110. if self._offset is None or not hasattr(self, "offset2"):
  111. # i.e. skip for TestCommon and YQM subclasses that do not have
  112. # offset2 attr
  113. return
  114. assert self.d - self.offset2 == (-self.offset2).apply(self.d)
  115. def test_radd(self):
  116. if self._offset is None or not hasattr(self, "offset2"):
  117. # i.e. skip for TestCommon and YQM subclasses that do not have
  118. # offset2 attr
  119. return
  120. assert self.d + self.offset2 == self.offset2 + self.d
  121. def test_sub(self):
  122. if self._offset is None or not hasattr(self, "offset2"):
  123. # i.e. skip for TestCommon and YQM subclasses that do not have
  124. # offset2 attr
  125. return
  126. off = self.offset2
  127. with pytest.raises(Exception):
  128. off - self.d
  129. assert 2 * off - off == off
  130. assert self.d - self.offset2 == self.d + self._offset(-2)
  131. assert self.d - self.offset2 == self.d - (2 * off - off)
  132. def testMult1(self):
  133. if self._offset is None or not hasattr(self, "offset1"):
  134. # i.e. skip for TestCommon and YQM subclasses that do not have
  135. # offset1 attr
  136. return
  137. assert self.d + 10 * self.offset1 == self.d + self._offset(10)
  138. assert self.d + 5 * self.offset1 == self.d + self._offset(5)
  139. def testMult2(self):
  140. if self._offset is None:
  141. return
  142. assert self.d + (-5 * self._offset(-10)) == self.d + self._offset(50)
  143. assert self.d + (-3 * self._offset(-2)) == self.d + self._offset(6)
  144. def test_compare_str(self):
  145. # GH#23524
  146. # comparing to strings that cannot be cast to DateOffsets should
  147. # not raise for __eq__ or __ne__
  148. if self._offset is None:
  149. return
  150. off = self._get_offset(self._offset)
  151. assert not off == "infer"
  152. assert off != "foo"
  153. # Note: inequalities are only implemented for Tick subclasses;
  154. # tests for this are in test_ticks
  155. class TestCommon(Base):
  156. # exected value created by Base._get_offset
  157. # are applied to 2011/01/01 09:00 (Saturday)
  158. # used for .apply and .rollforward
  159. expecteds = {'Day': Timestamp('2011-01-02 09:00:00'),
  160. 'DateOffset': Timestamp('2011-01-02 09:00:00'),
  161. 'BusinessDay': Timestamp('2011-01-03 09:00:00'),
  162. 'CustomBusinessDay': Timestamp('2011-01-03 09:00:00'),
  163. 'CustomBusinessMonthEnd': Timestamp('2011-01-31 09:00:00'),
  164. 'CustomBusinessMonthBegin': Timestamp('2011-01-03 09:00:00'),
  165. 'MonthBegin': Timestamp('2011-02-01 09:00:00'),
  166. 'BusinessMonthBegin': Timestamp('2011-01-03 09:00:00'),
  167. 'MonthEnd': Timestamp('2011-01-31 09:00:00'),
  168. 'SemiMonthEnd': Timestamp('2011-01-15 09:00:00'),
  169. 'SemiMonthBegin': Timestamp('2011-01-15 09:00:00'),
  170. 'BusinessMonthEnd': Timestamp('2011-01-31 09:00:00'),
  171. 'YearBegin': Timestamp('2012-01-01 09:00:00'),
  172. 'BYearBegin': Timestamp('2011-01-03 09:00:00'),
  173. 'YearEnd': Timestamp('2011-12-31 09:00:00'),
  174. 'BYearEnd': Timestamp('2011-12-30 09:00:00'),
  175. 'QuarterBegin': Timestamp('2011-03-01 09:00:00'),
  176. 'BQuarterBegin': Timestamp('2011-03-01 09:00:00'),
  177. 'QuarterEnd': Timestamp('2011-03-31 09:00:00'),
  178. 'BQuarterEnd': Timestamp('2011-03-31 09:00:00'),
  179. 'BusinessHour': Timestamp('2011-01-03 10:00:00'),
  180. 'CustomBusinessHour': Timestamp('2011-01-03 10:00:00'),
  181. 'WeekOfMonth': Timestamp('2011-01-08 09:00:00'),
  182. 'LastWeekOfMonth': Timestamp('2011-01-29 09:00:00'),
  183. 'FY5253Quarter': Timestamp('2011-01-25 09:00:00'),
  184. 'FY5253': Timestamp('2011-01-25 09:00:00'),
  185. 'Week': Timestamp('2011-01-08 09:00:00'),
  186. 'Easter': Timestamp('2011-04-24 09:00:00'),
  187. 'Hour': Timestamp('2011-01-01 10:00:00'),
  188. 'Minute': Timestamp('2011-01-01 09:01:00'),
  189. 'Second': Timestamp('2011-01-01 09:00:01'),
  190. 'Milli': Timestamp('2011-01-01 09:00:00.001000'),
  191. 'Micro': Timestamp('2011-01-01 09:00:00.000001'),
  192. 'Nano': Timestamp(np_datetime64_compat(
  193. '2011-01-01T09:00:00.000000001Z'))}
  194. def test_immutable(self, offset_types):
  195. # GH#21341 check that __setattr__ raises
  196. offset = self._get_offset(offset_types)
  197. with pytest.raises(AttributeError):
  198. offset.normalize = True
  199. with pytest.raises(AttributeError):
  200. offset.n = 91
  201. def test_return_type(self, offset_types):
  202. offset = self._get_offset(offset_types)
  203. # make sure that we are returning a Timestamp
  204. result = Timestamp('20080101') + offset
  205. assert isinstance(result, Timestamp)
  206. # make sure that we are returning NaT
  207. assert NaT + offset is NaT
  208. assert offset + NaT is NaT
  209. assert NaT - offset is NaT
  210. assert (-offset).apply(NaT) is NaT
  211. def test_offset_n(self, offset_types):
  212. offset = self._get_offset(offset_types)
  213. assert offset.n == 1
  214. neg_offset = offset * -1
  215. assert neg_offset.n == -1
  216. mul_offset = offset * 3
  217. assert mul_offset.n == 3
  218. def test_offset_freqstr(self, offset_types):
  219. offset = self._get_offset(offset_types)
  220. freqstr = offset.freqstr
  221. if freqstr not in ('<Easter>',
  222. "<DateOffset: days=1>",
  223. 'LWOM-SAT', ):
  224. code = get_offset(freqstr)
  225. assert offset.rule_code == code
  226. def _check_offsetfunc_works(self, offset, funcname, dt, expected,
  227. normalize=False):
  228. if normalize and issubclass(offset, Tick):
  229. # normalize=True disallowed for Tick subclasses GH#21427
  230. return
  231. offset_s = self._get_offset(offset, normalize=normalize)
  232. func = getattr(offset_s, funcname)
  233. result = func(dt)
  234. assert isinstance(result, Timestamp)
  235. assert result == expected
  236. result = func(Timestamp(dt))
  237. assert isinstance(result, Timestamp)
  238. assert result == expected
  239. # see gh-14101
  240. exp_warning = None
  241. ts = Timestamp(dt) + Nano(5)
  242. if (offset_s.__class__.__name__ == 'DateOffset' and
  243. (funcname == 'apply' or normalize) and
  244. ts.nanosecond > 0):
  245. exp_warning = UserWarning
  246. # test nanosecond is preserved
  247. with tm.assert_produces_warning(exp_warning,
  248. check_stacklevel=False):
  249. result = func(ts)
  250. assert isinstance(result, Timestamp)
  251. if normalize is False:
  252. assert result == expected + Nano(5)
  253. else:
  254. assert result == expected
  255. if isinstance(dt, np.datetime64):
  256. # test tz when input is datetime or Timestamp
  257. return
  258. for tz in self.timezones:
  259. expected_localize = expected.tz_localize(tz)
  260. tz_obj = timezones.maybe_get_tz(tz)
  261. dt_tz = conversion.localize_pydatetime(dt, tz_obj)
  262. result = func(dt_tz)
  263. assert isinstance(result, Timestamp)
  264. assert result == expected_localize
  265. result = func(Timestamp(dt, tz=tz))
  266. assert isinstance(result, Timestamp)
  267. assert result == expected_localize
  268. # see gh-14101
  269. exp_warning = None
  270. ts = Timestamp(dt, tz=tz) + Nano(5)
  271. if (offset_s.__class__.__name__ == 'DateOffset' and
  272. (funcname == 'apply' or normalize) and
  273. ts.nanosecond > 0):
  274. exp_warning = UserWarning
  275. # test nanosecond is preserved
  276. with tm.assert_produces_warning(exp_warning,
  277. check_stacklevel=False):
  278. result = func(ts)
  279. assert isinstance(result, Timestamp)
  280. if normalize is False:
  281. assert result == expected_localize + Nano(5)
  282. else:
  283. assert result == expected_localize
  284. def test_apply(self, offset_types):
  285. sdt = datetime(2011, 1, 1, 9, 0)
  286. ndt = np_datetime64_compat('2011-01-01 09:00Z')
  287. for dt in [sdt, ndt]:
  288. expected = self.expecteds[offset_types.__name__]
  289. self._check_offsetfunc_works(offset_types, 'apply', dt, expected)
  290. expected = Timestamp(expected.date())
  291. self._check_offsetfunc_works(offset_types, 'apply', dt, expected,
  292. normalize=True)
  293. def test_rollforward(self, offset_types):
  294. expecteds = self.expecteds.copy()
  295. # result will not be changed if the target is on the offset
  296. no_changes = ['Day', 'MonthBegin', 'SemiMonthBegin', 'YearBegin',
  297. 'Week', 'Hour', 'Minute', 'Second', 'Milli', 'Micro',
  298. 'Nano', 'DateOffset']
  299. for n in no_changes:
  300. expecteds[n] = Timestamp('2011/01/01 09:00')
  301. expecteds['BusinessHour'] = Timestamp('2011-01-03 09:00:00')
  302. expecteds['CustomBusinessHour'] = Timestamp('2011-01-03 09:00:00')
  303. # but be changed when normalize=True
  304. norm_expected = expecteds.copy()
  305. for k in norm_expected:
  306. norm_expected[k] = Timestamp(norm_expected[k].date())
  307. normalized = {'Day': Timestamp('2011-01-02 00:00:00'),
  308. 'DateOffset': Timestamp('2011-01-02 00:00:00'),
  309. 'MonthBegin': Timestamp('2011-02-01 00:00:00'),
  310. 'SemiMonthBegin': Timestamp('2011-01-15 00:00:00'),
  311. 'YearBegin': Timestamp('2012-01-01 00:00:00'),
  312. 'Week': Timestamp('2011-01-08 00:00:00'),
  313. 'Hour': Timestamp('2011-01-01 00:00:00'),
  314. 'Minute': Timestamp('2011-01-01 00:00:00'),
  315. 'Second': Timestamp('2011-01-01 00:00:00'),
  316. 'Milli': Timestamp('2011-01-01 00:00:00'),
  317. 'Micro': Timestamp('2011-01-01 00:00:00')}
  318. norm_expected.update(normalized)
  319. sdt = datetime(2011, 1, 1, 9, 0)
  320. ndt = np_datetime64_compat('2011-01-01 09:00Z')
  321. for dt in [sdt, ndt]:
  322. expected = expecteds[offset_types.__name__]
  323. self._check_offsetfunc_works(offset_types, 'rollforward', dt,
  324. expected)
  325. expected = norm_expected[offset_types.__name__]
  326. self._check_offsetfunc_works(offset_types, 'rollforward', dt,
  327. expected, normalize=True)
  328. def test_rollback(self, offset_types):
  329. expecteds = {'BusinessDay': Timestamp('2010-12-31 09:00:00'),
  330. 'CustomBusinessDay': Timestamp('2010-12-31 09:00:00'),
  331. 'CustomBusinessMonthEnd':
  332. Timestamp('2010-12-31 09:00:00'),
  333. 'CustomBusinessMonthBegin':
  334. Timestamp('2010-12-01 09:00:00'),
  335. 'BusinessMonthBegin': Timestamp('2010-12-01 09:00:00'),
  336. 'MonthEnd': Timestamp('2010-12-31 09:00:00'),
  337. 'SemiMonthEnd': Timestamp('2010-12-31 09:00:00'),
  338. 'BusinessMonthEnd': Timestamp('2010-12-31 09:00:00'),
  339. 'BYearBegin': Timestamp('2010-01-01 09:00:00'),
  340. 'YearEnd': Timestamp('2010-12-31 09:00:00'),
  341. 'BYearEnd': Timestamp('2010-12-31 09:00:00'),
  342. 'QuarterBegin': Timestamp('2010-12-01 09:00:00'),
  343. 'BQuarterBegin': Timestamp('2010-12-01 09:00:00'),
  344. 'QuarterEnd': Timestamp('2010-12-31 09:00:00'),
  345. 'BQuarterEnd': Timestamp('2010-12-31 09:00:00'),
  346. 'BusinessHour': Timestamp('2010-12-31 17:00:00'),
  347. 'CustomBusinessHour': Timestamp('2010-12-31 17:00:00'),
  348. 'WeekOfMonth': Timestamp('2010-12-11 09:00:00'),
  349. 'LastWeekOfMonth': Timestamp('2010-12-25 09:00:00'),
  350. 'FY5253Quarter': Timestamp('2010-10-26 09:00:00'),
  351. 'FY5253': Timestamp('2010-01-26 09:00:00'),
  352. 'Easter': Timestamp('2010-04-04 09:00:00')}
  353. # result will not be changed if the target is on the offset
  354. for n in ['Day', 'MonthBegin', 'SemiMonthBegin', 'YearBegin', 'Week',
  355. 'Hour', 'Minute', 'Second', 'Milli', 'Micro', 'Nano',
  356. 'DateOffset']:
  357. expecteds[n] = Timestamp('2011/01/01 09:00')
  358. # but be changed when normalize=True
  359. norm_expected = expecteds.copy()
  360. for k in norm_expected:
  361. norm_expected[k] = Timestamp(norm_expected[k].date())
  362. normalized = {'Day': Timestamp('2010-12-31 00:00:00'),
  363. 'DateOffset': Timestamp('2010-12-31 00:00:00'),
  364. 'MonthBegin': Timestamp('2010-12-01 00:00:00'),
  365. 'SemiMonthBegin': Timestamp('2010-12-15 00:00:00'),
  366. 'YearBegin': Timestamp('2010-01-01 00:00:00'),
  367. 'Week': Timestamp('2010-12-25 00:00:00'),
  368. 'Hour': Timestamp('2011-01-01 00:00:00'),
  369. 'Minute': Timestamp('2011-01-01 00:00:00'),
  370. 'Second': Timestamp('2011-01-01 00:00:00'),
  371. 'Milli': Timestamp('2011-01-01 00:00:00'),
  372. 'Micro': Timestamp('2011-01-01 00:00:00')}
  373. norm_expected.update(normalized)
  374. sdt = datetime(2011, 1, 1, 9, 0)
  375. ndt = np_datetime64_compat('2011-01-01 09:00Z')
  376. for dt in [sdt, ndt]:
  377. expected = expecteds[offset_types.__name__]
  378. self._check_offsetfunc_works(offset_types, 'rollback', dt,
  379. expected)
  380. expected = norm_expected[offset_types.__name__]
  381. self._check_offsetfunc_works(offset_types, 'rollback', dt,
  382. expected, normalize=True)
  383. def test_onOffset(self, offset_types):
  384. dt = self.expecteds[offset_types.__name__]
  385. offset_s = self._get_offset(offset_types)
  386. assert offset_s.onOffset(dt)
  387. # when normalize=True, onOffset checks time is 00:00:00
  388. if issubclass(offset_types, Tick):
  389. # normalize=True disallowed for Tick subclasses GH#21427
  390. return
  391. offset_n = self._get_offset(offset_types, normalize=True)
  392. assert not offset_n.onOffset(dt)
  393. if offset_types in (BusinessHour, CustomBusinessHour):
  394. # In default BusinessHour (9:00-17:00), normalized time
  395. # cannot be in business hour range
  396. return
  397. date = datetime(dt.year, dt.month, dt.day)
  398. assert offset_n.onOffset(date)
  399. def test_add(self, offset_types, tz_naive_fixture):
  400. tz = tz_naive_fixture
  401. dt = datetime(2011, 1, 1, 9, 0)
  402. offset_s = self._get_offset(offset_types)
  403. expected = self.expecteds[offset_types.__name__]
  404. result_dt = dt + offset_s
  405. result_ts = Timestamp(dt) + offset_s
  406. for result in [result_dt, result_ts]:
  407. assert isinstance(result, Timestamp)
  408. assert result == expected
  409. expected_localize = expected.tz_localize(tz)
  410. result = Timestamp(dt, tz=tz) + offset_s
  411. assert isinstance(result, Timestamp)
  412. assert result == expected_localize
  413. # normalize=True, disallowed for Tick subclasses GH#21427
  414. if issubclass(offset_types, Tick):
  415. return
  416. offset_s = self._get_offset(offset_types, normalize=True)
  417. expected = Timestamp(expected.date())
  418. result_dt = dt + offset_s
  419. result_ts = Timestamp(dt) + offset_s
  420. for result in [result_dt, result_ts]:
  421. assert isinstance(result, Timestamp)
  422. assert result == expected
  423. expected_localize = expected.tz_localize(tz)
  424. result = Timestamp(dt, tz=tz) + offset_s
  425. assert isinstance(result, Timestamp)
  426. assert result == expected_localize
  427. def test_pickle_v0_15_2(self, datapath):
  428. offsets = {'DateOffset': DateOffset(years=1),
  429. 'MonthBegin': MonthBegin(1),
  430. 'Day': Day(1),
  431. 'YearBegin': YearBegin(1),
  432. 'Week': Week(1)}
  433. pickle_path = datapath('tseries', 'offsets', 'data',
  434. 'dateoffset_0_15_2.pickle')
  435. # This code was executed once on v0.15.2 to generate the pickle:
  436. # with open(pickle_path, 'wb') as f: pickle.dump(offsets, f)
  437. #
  438. tm.assert_dict_equal(offsets, read_pickle(pickle_path))
  439. class TestDateOffset(Base):
  440. def setup_method(self, method):
  441. self.d = Timestamp(datetime(2008, 1, 2))
  442. _offset_map.clear()
  443. def test_repr(self):
  444. repr(DateOffset())
  445. repr(DateOffset(2))
  446. repr(2 * DateOffset())
  447. repr(2 * DateOffset(months=2))
  448. def test_mul(self):
  449. assert DateOffset(2) == 2 * DateOffset(1)
  450. assert DateOffset(2) == DateOffset(1) * 2
  451. def test_constructor(self):
  452. assert ((self.d + DateOffset(months=2)) == datetime(2008, 3, 2))
  453. assert ((self.d - DateOffset(months=2)) == datetime(2007, 11, 2))
  454. assert ((self.d + DateOffset(2)) == datetime(2008, 1, 4))
  455. assert not DateOffset(2).isAnchored()
  456. assert DateOffset(1).isAnchored()
  457. d = datetime(2008, 1, 31)
  458. assert ((d + DateOffset(months=1)) == datetime(2008, 2, 29))
  459. def test_copy(self):
  460. assert (DateOffset(months=2).copy() == DateOffset(months=2))
  461. def test_eq(self):
  462. offset1 = DateOffset(days=1)
  463. offset2 = DateOffset(days=365)
  464. assert offset1 != offset2
  465. class TestBusinessDay(Base):
  466. _offset = BDay
  467. def setup_method(self, method):
  468. self.d = datetime(2008, 1, 1)
  469. self.offset = BDay()
  470. self.offset1 = self.offset
  471. self.offset2 = BDay(2)
  472. def test_different_normalize_equals(self):
  473. # GH#21404 changed __eq__ to return False when `normalize` doesnt match
  474. offset = self._offset()
  475. offset2 = self._offset(normalize=True)
  476. assert offset != offset2
  477. def test_repr(self):
  478. assert repr(self.offset) == '<BusinessDay>'
  479. assert repr(self.offset2) == '<2 * BusinessDays>'
  480. if compat.PY37:
  481. expected = '<BusinessDay: offset=datetime.timedelta(days=1)>'
  482. else:
  483. expected = '<BusinessDay: offset=datetime.timedelta(1)>'
  484. assert repr(self.offset + timedelta(1)) == expected
  485. def test_with_offset(self):
  486. offset = self.offset + timedelta(hours=2)
  487. assert (self.d + offset) == datetime(2008, 1, 2, 2)
  488. def test_eq(self):
  489. assert self.offset2 == self.offset2
  490. def test_mul(self):
  491. pass
  492. def test_hash(self):
  493. assert hash(self.offset2) == hash(self.offset2)
  494. def test_call(self):
  495. assert self.offset2(self.d) == datetime(2008, 1, 3)
  496. def testRollback1(self):
  497. assert BDay(10).rollback(self.d) == self.d
  498. def testRollback2(self):
  499. assert (BDay(10).rollback(datetime(2008, 1, 5)) ==
  500. datetime(2008, 1, 4))
  501. def testRollforward1(self):
  502. assert BDay(10).rollforward(self.d) == self.d
  503. def testRollforward2(self):
  504. assert (BDay(10).rollforward(datetime(2008, 1, 5)) ==
  505. datetime(2008, 1, 7))
  506. def test_roll_date_object(self):
  507. offset = BDay()
  508. dt = date(2012, 9, 15)
  509. result = offset.rollback(dt)
  510. assert result == datetime(2012, 9, 14)
  511. result = offset.rollforward(dt)
  512. assert result == datetime(2012, 9, 17)
  513. offset = offsets.Day()
  514. result = offset.rollback(dt)
  515. assert result == datetime(2012, 9, 15)
  516. result = offset.rollforward(dt)
  517. assert result == datetime(2012, 9, 15)
  518. def test_onOffset(self):
  519. tests = [(BDay(), datetime(2008, 1, 1), True),
  520. (BDay(), datetime(2008, 1, 5), False)]
  521. for offset, d, expected in tests:
  522. assert_onOffset(offset, d, expected)
  523. apply_cases = []
  524. apply_cases.append((BDay(), {
  525. datetime(2008, 1, 1): datetime(2008, 1, 2),
  526. datetime(2008, 1, 4): datetime(2008, 1, 7),
  527. datetime(2008, 1, 5): datetime(2008, 1, 7),
  528. datetime(2008, 1, 6): datetime(2008, 1, 7),
  529. datetime(2008, 1, 7): datetime(2008, 1, 8)}))
  530. apply_cases.append((2 * BDay(), {
  531. datetime(2008, 1, 1): datetime(2008, 1, 3),
  532. datetime(2008, 1, 4): datetime(2008, 1, 8),
  533. datetime(2008, 1, 5): datetime(2008, 1, 8),
  534. datetime(2008, 1, 6): datetime(2008, 1, 8),
  535. datetime(2008, 1, 7): datetime(2008, 1, 9)}))
  536. apply_cases.append((-BDay(), {
  537. datetime(2008, 1, 1): datetime(2007, 12, 31),
  538. datetime(2008, 1, 4): datetime(2008, 1, 3),
  539. datetime(2008, 1, 5): datetime(2008, 1, 4),
  540. datetime(2008, 1, 6): datetime(2008, 1, 4),
  541. datetime(2008, 1, 7): datetime(2008, 1, 4),
  542. datetime(2008, 1, 8): datetime(2008, 1, 7)}))
  543. apply_cases.append((-2 * BDay(), {
  544. datetime(2008, 1, 1): datetime(2007, 12, 28),
  545. datetime(2008, 1, 4): datetime(2008, 1, 2),
  546. datetime(2008, 1, 5): datetime(2008, 1, 3),
  547. datetime(2008, 1, 6): datetime(2008, 1, 3),
  548. datetime(2008, 1, 7): datetime(2008, 1, 3),
  549. datetime(2008, 1, 8): datetime(2008, 1, 4),
  550. datetime(2008, 1, 9): datetime(2008, 1, 7)}))
  551. apply_cases.append((BDay(0), {
  552. datetime(2008, 1, 1): datetime(2008, 1, 1),
  553. datetime(2008, 1, 4): datetime(2008, 1, 4),
  554. datetime(2008, 1, 5): datetime(2008, 1, 7),
  555. datetime(2008, 1, 6): datetime(2008, 1, 7),
  556. datetime(2008, 1, 7): datetime(2008, 1, 7)}))
  557. @pytest.mark.parametrize('case', apply_cases)
  558. def test_apply(self, case):
  559. offset, cases = case
  560. for base, expected in compat.iteritems(cases):
  561. assert_offset_equal(offset, base, expected)
  562. def test_apply_large_n(self):
  563. dt = datetime(2012, 10, 23)
  564. result = dt + BDay(10)
  565. assert result == datetime(2012, 11, 6)
  566. result = dt + BDay(100) - BDay(100)
  567. assert result == dt
  568. off = BDay() * 6
  569. rs = datetime(2012, 1, 1) - off
  570. xp = datetime(2011, 12, 23)
  571. assert rs == xp
  572. st = datetime(2011, 12, 18)
  573. rs = st + off
  574. xp = datetime(2011, 12, 26)
  575. assert rs == xp
  576. off = BDay() * 10
  577. rs = datetime(2014, 1, 5) + off # see #5890
  578. xp = datetime(2014, 1, 17)
  579. assert rs == xp
  580. def test_apply_corner(self):
  581. pytest.raises(TypeError, BDay().apply, BMonthEnd())
  582. class TestBusinessHour(Base):
  583. _offset = BusinessHour
  584. def setup_method(self, method):
  585. self.d = datetime(2014, 7, 1, 10, 00)
  586. self.offset1 = BusinessHour()
  587. self.offset2 = BusinessHour(n=3)
  588. self.offset3 = BusinessHour(n=-1)
  589. self.offset4 = BusinessHour(n=-4)
  590. from datetime import time as dt_time
  591. self.offset5 = BusinessHour(start=dt_time(11, 0), end=dt_time(14, 30))
  592. self.offset6 = BusinessHour(start='20:00', end='05:00')
  593. self.offset7 = BusinessHour(n=-2, start=dt_time(21, 30),
  594. end=dt_time(6, 30))
  595. def test_constructor_errors(self):
  596. from datetime import time as dt_time
  597. with pytest.raises(ValueError):
  598. BusinessHour(start=dt_time(11, 0, 5))
  599. with pytest.raises(ValueError):
  600. BusinessHour(start='AAA')
  601. with pytest.raises(ValueError):
  602. BusinessHour(start='14:00:05')
  603. def test_different_normalize_equals(self):
  604. # GH#21404 changed __eq__ to return False when `normalize` doesnt match
  605. offset = self._offset()
  606. offset2 = self._offset(normalize=True)
  607. assert offset != offset2
  608. def test_repr(self):
  609. assert repr(self.offset1) == '<BusinessHour: BH=09:00-17:00>'
  610. assert repr(self.offset2) == '<3 * BusinessHours: BH=09:00-17:00>'
  611. assert repr(self.offset3) == '<-1 * BusinessHour: BH=09:00-17:00>'
  612. assert repr(self.offset4) == '<-4 * BusinessHours: BH=09:00-17:00>'
  613. assert repr(self.offset5) == '<BusinessHour: BH=11:00-14:30>'
  614. assert repr(self.offset6) == '<BusinessHour: BH=20:00-05:00>'
  615. assert repr(self.offset7) == '<-2 * BusinessHours: BH=21:30-06:30>'
  616. def test_with_offset(self):
  617. expected = Timestamp('2014-07-01 13:00')
  618. assert self.d + BusinessHour() * 3 == expected
  619. assert self.d + BusinessHour(n=3) == expected
  620. def test_eq(self):
  621. for offset in [self.offset1, self.offset2, self.offset3, self.offset4]:
  622. assert offset == offset
  623. assert BusinessHour() != BusinessHour(-1)
  624. assert BusinessHour(start='09:00') == BusinessHour()
  625. assert BusinessHour(start='09:00') != BusinessHour(start='09:01')
  626. assert (BusinessHour(start='09:00', end='17:00') !=
  627. BusinessHour(start='17:00', end='09:01'))
  628. def test_hash(self):
  629. for offset in [self.offset1, self.offset2, self.offset3, self.offset4]:
  630. assert hash(offset) == hash(offset)
  631. def test_call(self):
  632. assert self.offset1(self.d) == datetime(2014, 7, 1, 11)
  633. assert self.offset2(self.d) == datetime(2014, 7, 1, 13)
  634. assert self.offset3(self.d) == datetime(2014, 6, 30, 17)
  635. assert self.offset4(self.d) == datetime(2014, 6, 30, 14)
  636. def test_sub(self):
  637. # we have to override test_sub here becasue self.offset2 is not
  638. # defined as self._offset(2)
  639. off = self.offset2
  640. with pytest.raises(Exception):
  641. off - self.d
  642. assert 2 * off - off == off
  643. assert self.d - self.offset2 == self.d + self._offset(-3)
  644. def testRollback1(self):
  645. assert self.offset1.rollback(self.d) == self.d
  646. assert self.offset2.rollback(self.d) == self.d
  647. assert self.offset3.rollback(self.d) == self.d
  648. assert self.offset4.rollback(self.d) == self.d
  649. assert self.offset5.rollback(self.d) == datetime(2014, 6, 30, 14, 30)
  650. assert self.offset6.rollback(self.d) == datetime(2014, 7, 1, 5, 0)
  651. assert self.offset7.rollback(self.d) == datetime(2014, 7, 1, 6, 30)
  652. d = datetime(2014, 7, 1, 0)
  653. assert self.offset1.rollback(d) == datetime(2014, 6, 30, 17)
  654. assert self.offset2.rollback(d) == datetime(2014, 6, 30, 17)
  655. assert self.offset3.rollback(d) == datetime(2014, 6, 30, 17)
  656. assert self.offset4.rollback(d) == datetime(2014, 6, 30, 17)
  657. assert self.offset5.rollback(d) == datetime(2014, 6, 30, 14, 30)
  658. assert self.offset6.rollback(d) == d
  659. assert self.offset7.rollback(d) == d
  660. assert self._offset(5).rollback(self.d) == self.d
  661. def testRollback2(self):
  662. assert (self._offset(-3).rollback(datetime(2014, 7, 5, 15, 0)) ==
  663. datetime(2014, 7, 4, 17, 0))
  664. def testRollforward1(self):
  665. assert self.offset1.rollforward(self.d) == self.d
  666. assert self.offset2.rollforward(self.d) == self.d
  667. assert self.offset3.rollforward(self.d) == self.d
  668. assert self.offset4.rollforward(self.d) == self.d
  669. assert (self.offset5.rollforward(self.d) ==
  670. datetime(2014, 7, 1, 11, 0))
  671. assert (self.offset6.rollforward(self.d) ==
  672. datetime(2014, 7, 1, 20, 0))
  673. assert (self.offset7.rollforward(self.d) ==
  674. datetime(2014, 7, 1, 21, 30))
  675. d = datetime(2014, 7, 1, 0)
  676. assert self.offset1.rollforward(d) == datetime(2014, 7, 1, 9)
  677. assert self.offset2.rollforward(d) == datetime(2014, 7, 1, 9)
  678. assert self.offset3.rollforward(d) == datetime(2014, 7, 1, 9)
  679. assert self.offset4.rollforward(d) == datetime(2014, 7, 1, 9)
  680. assert self.offset5.rollforward(d) == datetime(2014, 7, 1, 11)
  681. assert self.offset6.rollforward(d) == d
  682. assert self.offset7.rollforward(d) == d
  683. assert self._offset(5).rollforward(self.d) == self.d
  684. def testRollforward2(self):
  685. assert (self._offset(-3).rollforward(datetime(2014, 7, 5, 16, 0)) ==
  686. datetime(2014, 7, 7, 9))
  687. def test_roll_date_object(self):
  688. offset = BusinessHour()
  689. dt = datetime(2014, 7, 6, 15, 0)
  690. result = offset.rollback(dt)
  691. assert result == datetime(2014, 7, 4, 17)
  692. result = offset.rollforward(dt)
  693. assert result == datetime(2014, 7, 7, 9)
  694. normalize_cases = []
  695. normalize_cases.append((BusinessHour(normalize=True), {
  696. datetime(2014, 7, 1, 8): datetime(2014, 7, 1),
  697. datetime(2014, 7, 1, 17): datetime(2014, 7, 2),
  698. datetime(2014, 7, 1, 16): datetime(2014, 7, 2),
  699. datetime(2014, 7, 1, 23): datetime(2014, 7, 2),
  700. datetime(2014, 7, 1, 0): datetime(2014, 7, 1),
  701. datetime(2014, 7, 4, 15): datetime(2014, 7, 4),
  702. datetime(2014, 7, 4, 15, 59): datetime(2014, 7, 4),
  703. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7),
  704. datetime(2014, 7, 5, 23): datetime(2014, 7, 7),
  705. datetime(2014, 7, 6, 10): datetime(2014, 7, 7)}))
  706. normalize_cases.append((BusinessHour(-1, normalize=True), {
  707. datetime(2014, 7, 1, 8): datetime(2014, 6, 30),
  708. datetime(2014, 7, 1, 17): datetime(2014, 7, 1),
  709. datetime(2014, 7, 1, 16): datetime(2014, 7, 1),
  710. datetime(2014, 7, 1, 10): datetime(2014, 6, 30),
  711. datetime(2014, 7, 1, 0): datetime(2014, 6, 30),
  712. datetime(2014, 7, 7, 10): datetime(2014, 7, 4),
  713. datetime(2014, 7, 7, 10, 1): datetime(2014, 7, 7),
  714. datetime(2014, 7, 5, 23): datetime(2014, 7, 4),
  715. datetime(2014, 7, 6, 10): datetime(2014, 7, 4)}))
  716. normalize_cases.append((BusinessHour(1, normalize=True, start='17:00',
  717. end='04:00'), {
  718. datetime(2014, 7, 1, 8): datetime(2014, 7, 1),
  719. datetime(2014, 7, 1, 17): datetime(2014, 7, 1),
  720. datetime(2014, 7, 1, 23): datetime(2014, 7, 2),
  721. datetime(2014, 7, 2, 2): datetime(2014, 7, 2),
  722. datetime(2014, 7, 2, 3): datetime(2014, 7, 2),
  723. datetime(2014, 7, 4, 23): datetime(2014, 7, 5),
  724. datetime(2014, 7, 5, 2): datetime(2014, 7, 5),
  725. datetime(2014, 7, 7, 2): datetime(2014, 7, 7),
  726. datetime(2014, 7, 7, 17): datetime(2014, 7, 7)}))
  727. @pytest.mark.parametrize('case', normalize_cases)
  728. def test_normalize(self, case):
  729. offset, cases = case
  730. for dt, expected in compat.iteritems(cases):
  731. assert offset.apply(dt) == expected
  732. on_offset_cases = []
  733. on_offset_cases.append((BusinessHour(), {
  734. datetime(2014, 7, 1, 9): True,
  735. datetime(2014, 7, 1, 8, 59): False,
  736. datetime(2014, 7, 1, 8): False,
  737. datetime(2014, 7, 1, 17): True,
  738. datetime(2014, 7, 1, 17, 1): False,
  739. datetime(2014, 7, 1, 18): False,
  740. datetime(2014, 7, 5, 9): False,
  741. datetime(2014, 7, 6, 12): False}))
  742. on_offset_cases.append((BusinessHour(start='10:00', end='15:00'), {
  743. datetime(2014, 7, 1, 9): False,
  744. datetime(2014, 7, 1, 10): True,
  745. datetime(2014, 7, 1, 15): True,
  746. datetime(2014, 7, 1, 15, 1): False,
  747. datetime(2014, 7, 5, 12): False,
  748. datetime(2014, 7, 6, 12): False}))
  749. on_offset_cases.append((BusinessHour(start='19:00', end='05:00'), {
  750. datetime(2014, 7, 1, 9, 0): False,
  751. datetime(2014, 7, 1, 10, 0): False,
  752. datetime(2014, 7, 1, 15): False,
  753. datetime(2014, 7, 1, 15, 1): False,
  754. datetime(2014, 7, 5, 12, 0): False,
  755. datetime(2014, 7, 6, 12, 0): False,
  756. datetime(2014, 7, 1, 19, 0): True,
  757. datetime(2014, 7, 2, 0, 0): True,
  758. datetime(2014, 7, 4, 23): True,
  759. datetime(2014, 7, 5, 1): True,
  760. datetime(2014, 7, 5, 5, 0): True,
  761. datetime(2014, 7, 6, 23, 0): False,
  762. datetime(2014, 7, 7, 3, 0): False}))
  763. @pytest.mark.parametrize('case', on_offset_cases)
  764. def test_onOffset(self, case):
  765. offset, cases = case
  766. for dt, expected in compat.iteritems(cases):
  767. assert offset.onOffset(dt) == expected
  768. opening_time_cases = []
  769. # opening time should be affected by sign of n, not by n's value and
  770. # end
  771. opening_time_cases.append(([BusinessHour(), BusinessHour(n=2),
  772. BusinessHour(n=4), BusinessHour(end='10:00'),
  773. BusinessHour(n=2, end='4:00'),
  774. BusinessHour(n=4, end='15:00')], {
  775. datetime(2014, 7, 1, 11): (datetime(2014, 7, 2, 9),
  776. datetime(2014, 7, 1, 9)),
  777. datetime(2014, 7, 1, 18): (datetime(2014, 7, 2, 9),
  778. datetime(2014, 7, 1, 9)),
  779. datetime(2014, 7, 1, 23): (datetime(2014, 7, 2, 9),
  780. datetime(2014, 7, 1, 9)),
  781. datetime(2014, 7, 2, 8): (datetime(2014, 7, 2, 9),
  782. datetime(2014, 7, 1, 9)),
  783. # if timestamp is on opening time, next opening time is
  784. # as it is
  785. datetime(2014, 7, 2, 9): (datetime(2014, 7, 2, 9),
  786. datetime(2014, 7, 2, 9)),
  787. datetime(2014, 7, 2, 10): (datetime(2014, 7, 3, 9),
  788. datetime(2014, 7, 2, 9)),
  789. # 2014-07-05 is saturday
  790. datetime(2014, 7, 5, 10): (datetime(2014, 7, 7, 9),
  791. datetime(2014, 7, 4, 9)),
  792. datetime(2014, 7, 4, 10): (datetime(2014, 7, 7, 9),
  793. datetime(2014, 7, 4, 9)),
  794. datetime(2014, 7, 4, 23): (datetime(2014, 7, 7, 9),
  795. datetime(2014, 7, 4, 9)),
  796. datetime(2014, 7, 6, 10): (datetime(2014, 7, 7, 9),
  797. datetime(2014, 7, 4, 9)),
  798. datetime(2014, 7, 7, 5): (datetime(2014, 7, 7, 9),
  799. datetime(2014, 7, 4, 9)),
  800. datetime(2014, 7, 7, 9, 1): (datetime(2014, 7, 8, 9),
  801. datetime(2014, 7, 7, 9))}))
  802. opening_time_cases.append(([BusinessHour(start='11:15'),
  803. BusinessHour(n=2, start='11:15'),
  804. BusinessHour(n=3, start='11:15'),
  805. BusinessHour(start='11:15', end='10:00'),
  806. BusinessHour(n=2, start='11:15', end='4:00'),
  807. BusinessHour(n=3, start='11:15',
  808. end='15:00')], {
  809. datetime(2014, 7, 1, 11): (datetime(2014, 7, 1, 11, 15),
  810. datetime(2014, 6, 30, 11, 15)),
  811. datetime(2014, 7, 1, 18): (datetime(2014, 7, 2, 11, 15),
  812. datetime(2014, 7, 1, 11, 15)),
  813. datetime(2014, 7, 1, 23): (datetime(2014, 7, 2, 11, 15),
  814. datetime(2014, 7, 1, 11, 15)),
  815. datetime(2014, 7, 2, 8): (datetime(2014, 7, 2, 11, 15),
  816. datetime(2014, 7, 1, 11, 15)),
  817. datetime(2014, 7, 2, 9): (datetime(2014, 7, 2, 11, 15),
  818. datetime(2014, 7, 1, 11, 15)),
  819. datetime(2014, 7, 2, 10): (datetime(2014, 7, 2, 11, 15),
  820. datetime(2014, 7, 1, 11, 15)),
  821. datetime(2014, 7, 2, 11, 15): (datetime(2014, 7, 2, 11, 15),
  822. datetime(2014, 7, 2, 11, 15)),
  823. datetime(2014, 7, 2, 11, 15, 1): (datetime(2014, 7, 3, 11, 15),
  824. datetime(2014, 7, 2, 11, 15)),
  825. datetime(2014, 7, 5, 10): (datetime(2014, 7, 7, 11, 15),
  826. datetime(2014, 7, 4, 11, 15)),
  827. datetime(2014, 7, 4, 10): (datetime(2014, 7, 4, 11, 15),
  828. datetime(2014, 7, 3, 11, 15)),
  829. datetime(2014, 7, 4, 23): (datetime(2014, 7, 7, 11, 15),
  830. datetime(2014, 7, 4, 11, 15)),
  831. datetime(2014, 7, 6, 10): (datetime(2014, 7, 7, 11, 15),
  832. datetime(2014, 7, 4, 11, 15)),
  833. datetime(2014, 7, 7, 5): (datetime(2014, 7, 7, 11, 15),
  834. datetime(2014, 7, 4, 11, 15)),
  835. datetime(2014, 7, 7, 9, 1): (datetime(2014, 7, 7, 11, 15),
  836. datetime(2014, 7, 4, 11, 15))}))
  837. opening_time_cases.append(([BusinessHour(-1), BusinessHour(n=-2),
  838. BusinessHour(n=-4),
  839. BusinessHour(n=-1, end='10:00'),
  840. BusinessHour(n=-2, end='4:00'),
  841. BusinessHour(n=-4, end='15:00')], {
  842. datetime(2014, 7, 1, 11): (datetime(2014, 7, 1, 9),
  843. datetime(2014, 7, 2, 9)),
  844. datetime(2014, 7, 1, 18): (datetime(2014, 7, 1, 9),
  845. datetime(2014, 7, 2, 9)),
  846. datetime(2014, 7, 1, 23): (datetime(2014, 7, 1, 9),
  847. datetime(2014, 7, 2, 9)),
  848. datetime(2014, 7, 2, 8): (datetime(2014, 7, 1, 9),
  849. datetime(2014, 7, 2, 9)),
  850. datetime(2014, 7, 2, 9): (datetime(2014, 7, 2, 9),
  851. datetime(2014, 7, 2, 9)),
  852. datetime(2014, 7, 2, 10): (datetime(2014, 7, 2, 9),
  853. datetime(2014, 7, 3, 9)),
  854. datetime(2014, 7, 5, 10): (datetime(2014, 7, 4, 9),
  855. datetime(2014, 7, 7, 9)),
  856. datetime(2014, 7, 4, 10): (datetime(2014, 7, 4, 9),
  857. datetime(2014, 7, 7, 9)),
  858. datetime(2014, 7, 4, 23): (datetime(2014, 7, 4, 9),
  859. datetime(2014, 7, 7, 9)),
  860. datetime(2014, 7, 6, 10): (datetime(2014, 7, 4, 9),
  861. datetime(2014, 7, 7, 9)),
  862. datetime(2014, 7, 7, 5): (datetime(2014, 7, 4, 9),
  863. datetime(2014, 7, 7, 9)),
  864. datetime(2014, 7, 7, 9): (datetime(2014, 7, 7, 9),
  865. datetime(2014, 7, 7, 9)),
  866. datetime(2014, 7, 7, 9, 1): (datetime(2014, 7, 7, 9),
  867. datetime(2014, 7, 8, 9))}))
  868. opening_time_cases.append(([BusinessHour(start='17:00', end='05:00'),
  869. BusinessHour(n=3, start='17:00',
  870. end='03:00')], {
  871. datetime(2014, 7, 1, 11): (datetime(2014, 7, 1, 17),
  872. datetime(2014, 6, 30, 17)),
  873. datetime(2014, 7, 1, 18): (datetime(2014, 7, 2, 17),
  874. datetime(2014, 7, 1, 17)),
  875. datetime(2014, 7, 1, 23): (datetime(2014, 7, 2, 17),
  876. datetime(2014, 7, 1, 17)),
  877. datetime(2014, 7, 2, 8): (datetime(2014, 7, 2, 17),
  878. datetime(2014, 7, 1, 17)),
  879. datetime(2014, 7, 2, 9): (datetime(2014, 7, 2, 17),
  880. datetime(2014, 7, 1, 17)),
  881. datetime(2014, 7, 4, 17): (datetime(2014, 7, 4, 17),
  882. datetime(2014, 7, 4, 17)),
  883. datetime(2014, 7, 5, 10): (datetime(2014, 7, 7, 17),
  884. datetime(2014, 7, 4, 17)),
  885. datetime(2014, 7, 4, 10): (datetime(2014, 7, 4, 17),
  886. datetime(2014, 7, 3, 17)),
  887. datetime(2014, 7, 4, 23): (datetime(2014, 7, 7, 17),
  888. datetime(2014, 7, 4, 17)),
  889. datetime(2014, 7, 6, 10): (datetime(2014, 7, 7, 17),
  890. datetime(2014, 7, 4, 17)),
  891. datetime(2014, 7, 7, 5): (datetime(2014, 7, 7, 17),
  892. datetime(2014, 7, 4, 17)),
  893. datetime(2014, 7, 7, 17, 1): (datetime(2014, 7, 8, 17),
  894. datetime(2014, 7, 7, 17)), }))
  895. opening_time_cases.append(([BusinessHour(-1, start='17:00', end='05:00'),
  896. BusinessHour(n=-2, start='17:00',
  897. end='03:00')], {
  898. datetime(2014, 7, 1, 11): (datetime(2014, 6, 30, 17),
  899. datetime(2014, 7, 1, 17)),
  900. datetime(2014, 7, 1, 18): (datetime(2014, 7, 1, 17),
  901. datetime(2014, 7, 2, 17)),
  902. datetime(2014, 7, 1, 23): (datetime(2014, 7, 1, 17),
  903. datetime(2014, 7, 2, 17)),
  904. datetime(2014, 7, 2, 8): (datetime(2014, 7, 1, 17),
  905. datetime(2014, 7, 2, 17)),
  906. datetime(2014, 7, 2, 9): (datetime(2014, 7, 1, 17),
  907. datetime(2014, 7, 2, 17)),
  908. datetime(2014, 7, 2, 16, 59): (datetime(2014, 7, 1, 17),
  909. datetime(2014, 7, 2, 17)),
  910. datetime(2014, 7, 5, 10): (datetime(2014, 7, 4, 17),
  911. datetime(2014, 7, 7, 17)),
  912. datetime(2014, 7, 4, 10): (datetime(2014, 7, 3, 17),
  913. datetime(2014, 7, 4, 17)),
  914. datetime(2014, 7, 4, 23): (datetime(2014, 7, 4, 17),
  915. datetime(2014, 7, 7, 17)),
  916. datetime(2014, 7, 6, 10): (datetime(2014, 7, 4, 17),
  917. datetime(2014, 7, 7, 17)),
  918. datetime(2014, 7, 7, 5): (datetime(2014, 7, 4, 17),
  919. datetime(2014, 7, 7, 17)),
  920. datetime(2014, 7, 7, 18): (datetime(2014, 7, 7, 17),
  921. datetime(2014, 7, 8, 17))}))
  922. @pytest.mark.parametrize('case', opening_time_cases)
  923. def test_opening_time(self, case):
  924. _offsets, cases = case
  925. for offset in _offsets:
  926. for dt, (exp_next, exp_prev) in compat.iteritems(cases):
  927. assert offset._next_opening_time(dt) == exp_next
  928. assert offset._prev_opening_time(dt) == exp_prev
  929. apply_cases = []
  930. apply_cases.append((BusinessHour(), {
  931. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 12),
  932. datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14),
  933. datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16),
  934. datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 10),
  935. datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 9),
  936. datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 2, 9, 30, 15),
  937. datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 10),
  938. datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 12),
  939. # out of business hours
  940. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 10),
  941. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10),
  942. datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10),
  943. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10),
  944. # saturday
  945. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10),
  946. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 10),
  947. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 9, 30),
  948. datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 9, 30, 30)}))
  949. apply_cases.append((BusinessHour(4), {
  950. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 15),
  951. datetime(2014, 7, 1, 13): datetime(2014, 7, 2, 9),
  952. datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 11),
  953. datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 12),
  954. datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 13),
  955. datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 15),
  956. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 13),
  957. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 13),
  958. datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 13),
  959. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 13),
  960. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 13),
  961. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 13),
  962. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 12, 30),
  963. datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 12, 30, 30)}))
  964. apply_cases.append((BusinessHour(-1), {
  965. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 10),
  966. datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 12),
  967. datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 14),
  968. datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 15),
  969. datetime(2014, 7, 1, 10): datetime(2014, 6, 30, 17),
  970. datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 1, 15, 30, 15),
  971. datetime(2014, 7, 1, 9, 30, 15): datetime(2014, 6, 30, 16, 30, 15),
  972. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 16),
  973. datetime(2014, 7, 1, 5): datetime(2014, 6, 30, 16),
  974. datetime(2014, 7, 2, 11): datetime(2014, 7, 2, 10),
  975. # out of business hours
  976. datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 16),
  977. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 16),
  978. datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 16),
  979. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 16),
  980. # saturday
  981. datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 16),
  982. datetime(2014, 7, 7, 9): datetime(2014, 7, 4, 16),
  983. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 16, 30),
  984. datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 16, 30, 30)}))
  985. apply_cases.append((BusinessHour(-4), {
  986. datetime(2014, 7, 1, 11): datetime(2014, 6, 30, 15),
  987. datetime(2014, 7, 1, 13): datetime(2014, 6, 30, 17),
  988. datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 11),
  989. datetime(2014, 7, 1, 16): datetime(2014, 7, 1, 12),
  990. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 13),
  991. datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 15),
  992. datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 13),
  993. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 13),
  994. datetime(2014, 7, 2, 23): datetime(2014, 7, 2, 13),
  995. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 13),
  996. datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 13),
  997. datetime(2014, 7, 4, 18): datetime(2014, 7, 4, 13),
  998. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 4, 13, 30),
  999. datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 4, 13, 30, 30)}))
  1000. apply_cases.append((BusinessHour(start='13:00', end='16:00'), {
  1001. datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 14),
  1002. datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14),
  1003. datetime(2014, 7, 1, 15): datetime(2014, 7, 2, 13),
  1004. datetime(2014, 7, 1, 19): datetime(2014, 7, 2, 14),
  1005. datetime(2014, 7, 1, 16): datetime(2014, 7, 2, 14),
  1006. datetime(2014, 7, 1, 15, 30, 15): datetime(2014, 7, 2, 13, 30, 15),
  1007. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 14),
  1008. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 14)}))
  1009. apply_cases.append((BusinessHour(n=2, start='13:00', end='16:00'), {
  1010. datetime(2014, 7, 1, 17): datetime(2014, 7, 2, 15),
  1011. datetime(2014, 7, 2, 14): datetime(2014, 7, 3, 13),
  1012. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 15),
  1013. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 15),
  1014. datetime(2014, 7, 2, 14, 30): datetime(2014, 7, 3, 13, 30),
  1015. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 15),
  1016. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 15),
  1017. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 15),
  1018. datetime(2014, 7, 4, 14, 30): datetime(2014, 7, 7, 13, 30),
  1019. datetime(2014, 7, 4, 14, 30, 30): datetime(2014, 7, 7, 13, 30, 30)}))
  1020. apply_cases.append((BusinessHour(n=-1, start='13:00', end='16:00'), {
  1021. datetime(2014, 7, 2, 11): datetime(2014, 7, 1, 15),
  1022. datetime(2014, 7, 2, 13): datetime(2014, 7, 1, 15),
  1023. datetime(2014, 7, 2, 14): datetime(2014, 7, 1, 16),
  1024. datetime(2014, 7, 2, 15): datetime(2014, 7, 2, 14),
  1025. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 15),
  1026. datetime(2014, 7, 2, 16): datetime(2014, 7, 2, 15),
  1027. datetime(2014, 7, 2, 13, 30, 15): datetime(2014, 7, 1, 15, 30, 15),
  1028. datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 15),
  1029. datetime(2014, 7, 7, 11): datetime(2014, 7, 4, 15)}))
  1030. apply_cases.append((BusinessHour(n=-3, start='10:00', end='16:00'), {
  1031. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 13),
  1032. datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 11),
  1033. datetime(2014, 7, 2, 8): datetime(2014, 7, 1, 13),
  1034. datetime(2014, 7, 2, 13): datetime(2014, 7, 1, 16),
  1035. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 13),
  1036. datetime(2014, 7, 2, 11, 30): datetime(2014, 7, 1, 14, 30),
  1037. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 13),
  1038. datetime(2014, 7, 4, 10): datetime(2014, 7, 3, 13),
  1039. datetime(2014, 7, 5, 15): datetime(2014, 7, 4, 13),
  1040. datetime(2014, 7, 4, 16): datetime(2014, 7, 4, 13),
  1041. datetime(2014, 7, 4, 12, 30): datetime(2014, 7, 3, 15, 30),
  1042. datetime(2014, 7, 4, 12, 30, 30): datetime(2014, 7, 3, 15, 30, 30)}))
  1043. apply_cases.append((BusinessHour(start='19:00', end='05:00'), {
  1044. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 20),
  1045. datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 20),
  1046. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 20),
  1047. datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 20),
  1048. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 20),
  1049. datetime(2014, 7, 2, 4, 30): datetime(2014, 7, 2, 19, 30),
  1050. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 1),
  1051. datetime(2014, 7, 4, 10): datetime(2014, 7, 4, 20),
  1052. datetime(2014, 7, 4, 23): datetime(2014, 7, 5, 0),
  1053. datetime(2014, 7, 5, 0): datetime(2014, 7, 5, 1),
  1054. datetime(2014, 7, 5, 4): datetime(2014, 7, 7, 19),
  1055. datetime(2014, 7, 5, 4, 30): datetime(2014, 7, 7, 19, 30),
  1056. datetime(2014, 7, 5, 4, 30, 30): datetime(2014, 7, 7, 19, 30, 30)}))
  1057. apply_cases.append((BusinessHour(n=-1, start='19:00', end='05:00'), {
  1058. datetime(2014, 7, 1, 17): datetime(2014, 7, 1, 4),
  1059. datetime(2014, 7, 2, 14): datetime(2014, 7, 2, 4),
  1060. datetime(2014, 7, 2, 8): datetime(2014, 7, 2, 4),
  1061. datetime(2014, 7, 2, 13): datetime(2014, 7, 2, 4),
  1062. datetime(2014, 7, 2, 20): datetime(2014, 7, 2, 5),
  1063. datetime(2014, 7, 2, 19): datetime(2014, 7, 2, 4),
  1064. datetime(2014, 7, 2, 19, 30): datetime(2014, 7, 2, 4, 30),
  1065. datetime(2014, 7, 3, 0): datetime(2014, 7, 2, 23),
  1066. datetime(2014, 7, 3, 6): datetime(2014, 7, 3, 4),
  1067. datetime(2014, 7, 4, 23): datetime(2014, 7, 4, 22),
  1068. datetime(2014, 7, 5, 0): datetime(2014, 7, 4, 23),
  1069. datetime(2014, 7, 5, 4): datetime(2014, 7, 5, 3),
  1070. datetime(2014, 7, 7, 19, 30): datetime(2014, 7, 5, 4, 30),
  1071. datetime(2014, 7, 7, 19, 30, 30): datetime(2014, 7, 5, 4, 30, 30)}))
  1072. @pytest.mark.parametrize('case', apply_cases)
  1073. def test_apply(self, case):
  1074. offset, cases = case
  1075. for base, expected in compat.iteritems(cases):
  1076. assert_offset_equal(offset, base, expected)
  1077. apply_large_n_cases = []
  1078. # A week later
  1079. apply_large_n_cases.append((BusinessHour(40), {
  1080. datetime(2014, 7, 1, 11): datetime(2014, 7, 8, 11),
  1081. datetime(2014, 7, 1, 13): datetime(2014, 7, 8, 13),
  1082. datetime(2014, 7, 1, 15): datetime(2014, 7, 8, 15),
  1083. datetime(2014, 7, 1, 16): datetime(2014, 7, 8, 16),
  1084. datetime(2014, 7, 1, 17): datetime(2014, 7, 9, 9),
  1085. datetime(2014, 7, 2, 11): datetime(2014, 7, 9, 11),
  1086. datetime(2014, 7, 2, 8): datetime(2014, 7, 9, 9),
  1087. datetime(2014, 7, 2, 19): datetime(2014, 7, 10, 9),
  1088. datetime(2014, 7, 2, 23): datetime(2014, 7, 10, 9),
  1089. datetime(2014, 7, 3, 0): datetime(2014, 7, 10, 9),
  1090. datetime(2014, 7, 5, 15): datetime(2014, 7, 14, 9),
  1091. datetime(2014, 7, 4, 18): datetime(2014, 7, 14, 9),
  1092. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 14, 9, 30),
  1093. datetime(2014, 7, 7, 9, 30, 30): datetime(2014, 7, 14, 9, 30, 30)}))
  1094. # 3 days and 1 hour before
  1095. apply_large_n_cases.append((BusinessHour(-25), {
  1096. datetime(2014, 7, 1, 11): datetime(2014, 6, 26, 10),
  1097. datetime(2014, 7, 1, 13): datetime(2014, 6, 26, 12),
  1098. datetime(2014, 7, 1, 9): datetime(2014, 6, 25, 16),
  1099. datetime(2014, 7, 1, 10): datetime(2014, 6, 25, 17),
  1100. datetime(2014, 7, 3, 11): datetime(2014, 6, 30, 10),
  1101. datetime(2014, 7, 3, 8): datetime(2014, 6, 27, 16),
  1102. datetime(2014, 7, 3, 19): datetime(2014, 6, 30, 16),
  1103. datetime(2014, 7, 3, 23): datetime(2014, 6, 30, 16),
  1104. datetime(2014, 7, 4, 9): datetime(2014, 6, 30, 16),
  1105. datetime(2014, 7, 5, 15): datetime(2014, 7, 1, 16),
  1106. datetime(2014, 7, 6, 18): datetime(2014, 7, 1, 16),
  1107. datetime(2014, 7, 7, 9, 30): datetime(2014, 7, 1, 16, 30),
  1108. datetime(2014, 7, 7, 10, 30, 30): datetime(2014, 7, 2, 9, 30, 30)}))
  1109. # 5 days and 3 hours later
  1110. apply_large_n_cases.append((BusinessHour(28, start='21:00', end='02:00'), {
  1111. datetime(2014, 7, 1, 11): datetime(2014, 7, 9, 0),
  1112. datetime(2014, 7, 1, 22): datetime(2014, 7, 9, 1),
  1113. datetime(2014, 7, 1, 23): datetime(2014, 7, 9, 21),
  1114. datetime(2014, 7, 2, 2): datetime(2014, 7, 10, 0),
  1115. datetime(2014, 7, 3, 21): datetime(2014, 7, 11, 0),
  1116. datetime(2014, 7, 4, 1): datetime(2014, 7, 11, 23),
  1117. datetime(2014, 7, 4, 2): datetime(2014, 7, 12, 0),
  1118. datetime(2014, 7, 4, 3): datetime(2014, 7, 12, 0),
  1119. datetime(2014, 7, 5, 1): datetime(2014, 7, 14, 23),
  1120. datetime(2014, 7, 5, 15): datetime(2014, 7, 15, 0),
  1121. datetime(2014, 7, 6, 18): datetime(2014, 7, 15, 0),
  1122. datetime(2014, 7, 7, 1): datetime(2014, 7, 15, 0),
  1123. datetime(2014, 7, 7, 23, 30): datetime(2014, 7, 15, 21, 30)}))
  1124. @pytest.mark.parametrize('case', apply_large_n_cases)
  1125. def test_apply_large_n(self, case):
  1126. offset, cases = case
  1127. for base, expected in compat.iteritems(cases):
  1128. assert_offset_equal(offset, base, expected)
  1129. def test_apply_nanoseconds(self):
  1130. tests = []
  1131. tests.append((BusinessHour(),
  1132. {Timestamp('2014-07-04 15:00') + Nano(5): Timestamp(
  1133. '2014-07-04 16:00') + Nano(5),
  1134. Timestamp('2014-07-04 16:00') + Nano(5): Timestamp(
  1135. '2014-07-07 09:00') + Nano(5),
  1136. Timestamp('2014-07-04 16:00') - Nano(5): Timestamp(
  1137. '2014-07-04 17:00') - Nano(5)}))
  1138. tests.append((BusinessHour(-1),
  1139. {Timestamp('2014-07-04 15:00') + Nano(5): Timestamp(
  1140. '2014-07-04 14:00') + Nano(5),
  1141. Timestamp('2014-07-04 10:00') + Nano(5): Timestamp(
  1142. '2014-07-04 09:00') + Nano(5),
  1143. Timestamp('2014-07-04 10:00') - Nano(5): Timestamp(
  1144. '2014-07-03 17:00') - Nano(5), }))
  1145. for offset, cases in tests:
  1146. for base, expected in compat.iteritems(cases):
  1147. assert_offset_equal(offset, base, expected)
  1148. def test_datetimeindex(self):
  1149. idx1 = date_range(start='2014-07-04 15:00', end='2014-07-08 10:00',
  1150. freq='BH')
  1151. idx2 = date_range(start='2014-07-04 15:00', periods=12, freq='BH')
  1152. idx3 = date_range(end='2014-07-08 10:00', periods=12, freq='BH')
  1153. expected = DatetimeIndex(['2014-07-04 15:00', '2014-07-04 16:00',
  1154. '2014-07-07 09:00',
  1155. '2014-07-07 10:00', '2014-07-07 11:00',
  1156. '2014-07-07 12:00',
  1157. '2014-07-07 13:00', '2014-07-07 14:00',
  1158. '2014-07-07 15:00',
  1159. '2014-07-07 16:00', '2014-07-08 09:00',
  1160. '2014-07-08 10:00'],
  1161. freq='BH')
  1162. for idx in [idx1, idx2, idx3]:
  1163. tm.assert_index_equal(idx, expected)
  1164. idx1 = date_range(start='2014-07-04 15:45', end='2014-07-08 10:45',
  1165. freq='BH')
  1166. idx2 = date_range(start='2014-07-04 15:45', periods=12, freq='BH')
  1167. idx3 = date_range(end='2014-07-08 10:45', periods=12, freq='BH')
  1168. expected = DatetimeIndex(['2014-07-04 15:45', '2014-07-04 16:45',
  1169. '2014-07-07 09:45',
  1170. '2014-07-07 10:45', '2014-07-07 11:45',
  1171. '2014-07-07 12:45',
  1172. '2014-07-07 13:45', '2014-07-07 14:45',
  1173. '2014-07-07 15:45',
  1174. '2014-07-07 16:45', '2014-07-08 09:45',
  1175. '2014-07-08 10:45'],
  1176. freq='BH')
  1177. expected = idx1
  1178. for idx in [idx1, idx2, idx3]:
  1179. tm.assert_index_equal(idx, expected)
  1180. class TestCustomBusinessHour(Base):
  1181. _offset = CustomBusinessHour
  1182. holidays = ['2014-06-27', datetime(2014, 6, 30),
  1183. np.datetime64('2014-07-02')]
  1184. def setup_method(self, method):
  1185. # 2014 Calendar to check custom holidays
  1186. # Sun Mon Tue Wed Thu Fri Sat
  1187. # 6/22 23 24 25 26 27 28
  1188. # 29 30 7/1 2 3 4 5
  1189. # 6 7 8 9 10 11 12
  1190. self.d = datetime(2014, 7, 1, 10, 00)
  1191. self.offset1 = CustomBusinessHour(weekmask='Tue Wed Thu Fri')
  1192. self.offset2 = CustomBusinessHour(holidays=self.holidays)
  1193. def test_constructor_errors(self):
  1194. from datetime import time as dt_time
  1195. with pytest.raises(ValueError):
  1196. CustomBusinessHour(start=dt_time(11, 0, 5))
  1197. with pytest.raises(ValueError):
  1198. CustomBusinessHour(start='AAA')
  1199. with pytest.raises(ValueError):
  1200. CustomBusinessHour(start='14:00:05')
  1201. def test_different_normalize_equals(self):
  1202. # GH#21404 changed __eq__ to return False when `normalize` doesnt match
  1203. offset = self._offset()
  1204. offset2 = self._offset(normalize=True)
  1205. assert offset != offset2
  1206. def test_repr(self):
  1207. assert repr(self.offset1) == '<CustomBusinessHour: CBH=09:00-17:00>'
  1208. assert repr(self.offset2) == '<CustomBusinessHour: CBH=09:00-17:00>'
  1209. def test_with_offset(self):
  1210. expected = Timestamp('2014-07-01 13:00')
  1211. assert self.d + CustomBusinessHour() * 3 == expected
  1212. assert self.d + CustomBusinessHour(n=3) == expected
  1213. def test_eq(self):
  1214. for offset in [self.offset1, self.offset2]:
  1215. assert offset == offset
  1216. assert CustomBusinessHour() != CustomBusinessHour(-1)
  1217. assert (CustomBusinessHour(start='09:00') ==
  1218. CustomBusinessHour())
  1219. assert (CustomBusinessHour(start='09:00') !=
  1220. CustomBusinessHour(start='09:01'))
  1221. assert (CustomBusinessHour(start='09:00', end='17:00') !=
  1222. CustomBusinessHour(start='17:00', end='09:01'))
  1223. assert (CustomBusinessHour(weekmask='Tue Wed Thu Fri') !=
  1224. CustomBusinessHour(weekmask='Mon Tue Wed Thu Fri'))
  1225. assert (CustomBusinessHour(holidays=['2014-06-27']) !=
  1226. CustomBusinessHour(holidays=['2014-06-28']))
  1227. def test_sub(self):
  1228. # override the Base.test_sub implementation because self.offset2 is
  1229. # defined differently in this class than the test expects
  1230. pass
  1231. def test_hash(self):
  1232. assert hash(self.offset1) == hash(self.offset1)
  1233. assert hash(self.offset2) == hash(self.offset2)
  1234. def test_call(self):
  1235. assert self.offset1(self.d) == datetime(2014, 7, 1, 11)
  1236. assert self.offset2(self.d) == datetime(2014, 7, 1, 11)
  1237. def testRollback1(self):
  1238. assert self.offset1.rollback(self.d) == self.d
  1239. assert self.offset2.rollback(self.d) == self.d
  1240. d = datetime(2014, 7, 1, 0)
  1241. # 2014/07/01 is Tuesday, 06/30 is Monday(holiday)
  1242. assert self.offset1.rollback(d) == datetime(2014, 6, 27, 17)
  1243. # 2014/6/30 and 2014/6/27 are holidays
  1244. assert self.offset2.rollback(d) == datetime(2014, 6, 26, 17)
  1245. def testRollback2(self):
  1246. assert (self._offset(-3).rollback(datetime(2014, 7, 5, 15, 0)) ==
  1247. datetime(2014, 7, 4, 17, 0))
  1248. def testRollforward1(self):
  1249. assert self.offset1.rollforward(self.d) == self.d
  1250. assert self.offset2.rollforward(self.d) == self.d
  1251. d = datetime(2014, 7, 1, 0)
  1252. assert self.offset1.rollforward(d) == datetime(2014, 7, 1, 9)
  1253. assert self.offset2.rollforward(d) == datetime(2014, 7, 1, 9)
  1254. def testRollforward2(self):
  1255. assert (self._offset(-3).rollforward(datetime(2014, 7, 5, 16, 0)) ==
  1256. datetime(2014, 7, 7, 9))
  1257. def test_roll_date_object(self):
  1258. offset = BusinessHour()
  1259. dt = datetime(2014, 7, 6, 15, 0)
  1260. result = offset.rollback(dt)
  1261. assert result == datetime(2014, 7, 4, 17)
  1262. result = offset.rollforward(dt)
  1263. assert result == datetime(2014, 7, 7, 9)
  1264. normalize_cases = []
  1265. normalize_cases.append((
  1266. CustomBusinessHour(normalize=True, holidays=holidays),
  1267. {datetime(2014, 7, 1, 8): datetime(2014, 7, 1),
  1268. datetime(2014, 7, 1, 17): datetime(2014, 7, 3),
  1269. datetime(2014, 7, 1, 16): datetime(2014, 7, 3),
  1270. datetime(2014, 7, 1, 23): datetime(2014, 7, 3),
  1271. datetime(2014, 7, 1, 0): datetime(2014, 7, 1),
  1272. datetime(2014, 7, 4, 15): datetime(2014, 7, 4),
  1273. datetime(2014, 7, 4, 15, 59): datetime(2014, 7, 4),
  1274. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7),
  1275. datetime(2014, 7, 5, 23): datetime(2014, 7, 7),
  1276. datetime(2014, 7, 6, 10): datetime(2014, 7, 7)}))
  1277. normalize_cases.append((
  1278. CustomBusinessHour(-1, normalize=True, holidays=holidays),
  1279. {datetime(2014, 7, 1, 8): datetime(2014, 6, 26),
  1280. datetime(2014, 7, 1, 17): datetime(2014, 7, 1),
  1281. datetime(2014, 7, 1, 16): datetime(2014, 7, 1),
  1282. datetime(2014, 7, 1, 10): datetime(2014, 6, 26),
  1283. datetime(2014, 7, 1, 0): datetime(2014, 6, 26),
  1284. datetime(2014, 7, 7, 10): datetime(2014, 7, 4),
  1285. datetime(2014, 7, 7, 10, 1): datetime(2014, 7, 7),
  1286. datetime(2014, 7, 5, 23): datetime(2014, 7, 4),
  1287. datetime(2014, 7, 6, 10): datetime(2014, 7, 4)}))
  1288. normalize_cases.append((
  1289. CustomBusinessHour(1, normalize=True,
  1290. start='17:00', end='04:00',
  1291. holidays=holidays),
  1292. {datetime(2014, 7, 1, 8): datetime(2014, 7, 1),
  1293. datetime(2014, 7, 1, 17): datetime(2014, 7, 1),
  1294. datetime(2014, 7, 1, 23): datetime(2014, 7, 2),
  1295. datetime(2014, 7, 2, 2): datetime(2014, 7, 2),
  1296. datetime(2014, 7, 2, 3): datetime(2014, 7, 3),
  1297. datetime(2014, 7, 4, 23): datetime(2014, 7, 5),
  1298. datetime(2014, 7, 5, 2): datetime(2014, 7, 5),
  1299. datetime(2014, 7, 7, 2): datetime(2014, 7, 7),
  1300. datetime(2014, 7, 7, 17): datetime(2014, 7, 7)}))
  1301. @pytest.mark.parametrize('norm_cases', normalize_cases)
  1302. def test_normalize(self, norm_cases):
  1303. offset, cases = norm_cases
  1304. for dt, expected in compat.iteritems(cases):
  1305. assert offset.apply(dt) == expected
  1306. def test_onOffset(self):
  1307. tests = []
  1308. tests.append((CustomBusinessHour(start='10:00', end='15:00',
  1309. holidays=self.holidays),
  1310. {datetime(2014, 7, 1, 9): False,
  1311. datetime(2014, 7, 1, 10): True,
  1312. datetime(2014, 7, 1, 15): True,
  1313. datetime(2014, 7, 1, 15, 1): False,
  1314. datetime(2014, 7, 5, 12): False,
  1315. datetime(2014, 7, 6, 12): False}))
  1316. for offset, cases in tests:
  1317. for dt, expected in compat.iteritems(cases):
  1318. assert offset.onOffset(dt) == expected
  1319. apply_cases = []
  1320. apply_cases.append((
  1321. CustomBusinessHour(holidays=holidays),
  1322. {datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 12),
  1323. datetime(2014, 7, 1, 13): datetime(2014, 7, 1, 14),
  1324. datetime(2014, 7, 1, 15): datetime(2014, 7, 1, 16),
  1325. datetime(2014, 7, 1, 19): datetime(2014, 7, 3, 10),
  1326. datetime(2014, 7, 1, 16): datetime(2014, 7, 3, 9),
  1327. datetime(2014, 7, 1, 16, 30, 15): datetime(2014, 7, 3, 9, 30, 15),
  1328. datetime(2014, 7, 1, 17): datetime(2014, 7, 3, 10),
  1329. datetime(2014, 7, 2, 11): datetime(2014, 7, 3, 10),
  1330. # out of business hours
  1331. datetime(2014, 7, 2, 8): datetime(2014, 7, 3, 10),
  1332. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 10),
  1333. datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 10),
  1334. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 10),
  1335. # saturday
  1336. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 10),
  1337. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 10),
  1338. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 9, 30),
  1339. datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 9, 30, 30)}))
  1340. apply_cases.append((
  1341. CustomBusinessHour(4, holidays=holidays),
  1342. {datetime(2014, 7, 1, 11): datetime(2014, 7, 1, 15),
  1343. datetime(2014, 7, 1, 13): datetime(2014, 7, 3, 9),
  1344. datetime(2014, 7, 1, 15): datetime(2014, 7, 3, 11),
  1345. datetime(2014, 7, 1, 16): datetime(2014, 7, 3, 12),
  1346. datetime(2014, 7, 1, 17): datetime(2014, 7, 3, 13),
  1347. datetime(2014, 7, 2, 11): datetime(2014, 7, 3, 13),
  1348. datetime(2014, 7, 2, 8): datetime(2014, 7, 3, 13),
  1349. datetime(2014, 7, 2, 19): datetime(2014, 7, 3, 13),
  1350. datetime(2014, 7, 2, 23): datetime(2014, 7, 3, 13),
  1351. datetime(2014, 7, 3, 0): datetime(2014, 7, 3, 13),
  1352. datetime(2014, 7, 5, 15): datetime(2014, 7, 7, 13),
  1353. datetime(2014, 7, 4, 17): datetime(2014, 7, 7, 13),
  1354. datetime(2014, 7, 4, 16, 30): datetime(2014, 7, 7, 12, 30),
  1355. datetime(2014, 7, 4, 16, 30, 30): datetime(2014, 7, 7, 12, 30, 30)}))
  1356. @pytest.mark.parametrize('apply_case', apply_cases)
  1357. def test_apply(self, apply_case):
  1358. offset, cases = apply_case
  1359. for base, expected in compat.iteritems(cases):
  1360. assert_offset_equal(offset, base, expected)
  1361. nano_cases = []
  1362. nano_cases.append(
  1363. (CustomBusinessHour(holidays=holidays),
  1364. {Timestamp('2014-07-01 15:00') + Nano(5):
  1365. Timestamp('2014-07-01 16:00') + Nano(5),
  1366. Timestamp('2014-07-01 16:00') + Nano(5):
  1367. Timestamp('2014-07-03 09:00') + Nano(5),
  1368. Timestamp('2014-07-01 16:00') - Nano(5):
  1369. Timestamp('2014-07-01 17:00') - Nano(5)}))
  1370. nano_cases.append(
  1371. (CustomBusinessHour(-1, holidays=holidays),
  1372. {Timestamp('2014-07-01 15:00') + Nano(5):
  1373. Timestamp('2014-07-01 14:00') + Nano(5),
  1374. Timestamp('2014-07-01 10:00') + Nano(5):
  1375. Timestamp('2014-07-01 09:00') + Nano(5),
  1376. Timestamp('2014-07-01 10:00') - Nano(5):
  1377. Timestamp('2014-06-26 17:00') - Nano(5)}))
  1378. @pytest.mark.parametrize('nano_case', nano_cases)
  1379. def test_apply_nanoseconds(self, nano_case):
  1380. offset, cases = nano_case
  1381. for base, expected in compat.iteritems(cases):
  1382. assert_offset_equal(offset, base, expected)
  1383. class TestCustomBusinessDay(Base):
  1384. _offset = CDay
  1385. def setup_method(self, method):
  1386. self.d = datetime(2008, 1, 1)
  1387. self.nd = np_datetime64_compat('2008-01-01 00:00:00Z')
  1388. self.offset = CDay()
  1389. self.offset1 = self.offset
  1390. self.offset2 = CDay(2)
  1391. def test_different_normalize_equals(self):
  1392. # GH#21404 changed __eq__ to return False when `normalize` doesnt match
  1393. offset = self._offset()
  1394. offset2 = self._offset(normalize=True)
  1395. assert offset != offset2
  1396. def test_repr(self):
  1397. assert repr(self.offset) == '<CustomBusinessDay>'
  1398. assert repr(self.offset2) == '<2 * CustomBusinessDays>'
  1399. if compat.PY37:
  1400. expected = '<BusinessDay: offset=datetime.timedelta(days=1)>'
  1401. else:
  1402. expected = '<BusinessDay: offset=datetime.timedelta(1)>'
  1403. assert repr(self.offset + timedelta(1)) == expected
  1404. def test_with_offset(self):
  1405. offset = self.offset + timedelta(hours=2)
  1406. assert (self.d + offset) == datetime(2008, 1, 2, 2)
  1407. def test_eq(self):
  1408. assert self.offset2 == self.offset2
  1409. def test_mul(self):
  1410. pass
  1411. def test_hash(self):
  1412. assert hash(self.offset2) == hash(self.offset2)
  1413. def test_call(self):
  1414. assert self.offset2(self.d) == datetime(2008, 1, 3)
  1415. assert self.offset2(self.nd) == datetime(2008, 1, 3)
  1416. def testRollback1(self):
  1417. assert CDay(10).rollback(self.d) == self.d
  1418. def testRollback2(self):
  1419. assert (CDay(10).rollback(datetime(2008, 1, 5)) ==
  1420. datetime(2008, 1, 4))
  1421. def testRollforward1(self):
  1422. assert CDay(10).rollforward(self.d) == self.d
  1423. def testRollforward2(self):
  1424. assert (CDay(10).rollforward(datetime(2008, 1, 5)) ==
  1425. datetime(2008, 1, 7))
  1426. def test_roll_date_object(self):
  1427. offset = CDay()
  1428. dt = date(2012, 9, 15)
  1429. result = offset.rollback(dt)
  1430. assert result == datetime(2012, 9, 14)
  1431. result = offset.rollforward(dt)
  1432. assert result == datetime(2012, 9, 17)
  1433. offset = offsets.Day()
  1434. result = offset.rollback(dt)
  1435. assert result == datetime(2012, 9, 15)
  1436. result = offset.rollforward(dt)
  1437. assert result == datetime(2012, 9, 15)
  1438. on_offset_cases = [(CDay(), datetime(2008, 1, 1), True),
  1439. (CDay(), datetime(2008, 1, 5), False)]
  1440. @pytest.mark.parametrize('case', on_offset_cases)
  1441. def test_onOffset(self, case):
  1442. offset, d, expected = case
  1443. assert_onOffset(offset, d, expected)
  1444. apply_cases = []
  1445. apply_cases.append((CDay(), {
  1446. datetime(2008, 1, 1): datetime(2008, 1, 2),
  1447. datetime(2008, 1, 4): datetime(2008, 1, 7),
  1448. datetime(2008, 1, 5): datetime(2008, 1, 7),
  1449. datetime(2008, 1, 6): datetime(2008, 1, 7),
  1450. datetime(2008, 1, 7): datetime(2008, 1, 8)}))
  1451. apply_cases.append((2 * CDay(), {
  1452. datetime(2008, 1, 1): datetime(2008, 1, 3),
  1453. datetime(2008, 1, 4): datetime(2008, 1, 8),
  1454. datetime(2008, 1, 5): datetime(2008, 1, 8),
  1455. datetime(2008, 1, 6): datetime(2008, 1, 8),
  1456. datetime(2008, 1, 7): datetime(2008, 1, 9)}))
  1457. apply_cases.append((-CDay(), {
  1458. datetime(2008, 1, 1): datetime(2007, 12, 31),
  1459. datetime(2008, 1, 4): datetime(2008, 1, 3),
  1460. datetime(2008, 1, 5): datetime(2008, 1, 4),
  1461. datetime(2008, 1, 6): datetime(2008, 1, 4),
  1462. datetime(2008, 1, 7): datetime(2008, 1, 4),
  1463. datetime(2008, 1, 8): datetime(2008, 1, 7)}))
  1464. apply_cases.append((-2 * CDay(), {
  1465. datetime(2008, 1, 1): datetime(2007, 12, 28),
  1466. datetime(2008, 1, 4): datetime(2008, 1, 2),
  1467. datetime(2008, 1, 5): datetime(2008, 1, 3),
  1468. datetime(2008, 1, 6): datetime(2008, 1, 3),
  1469. datetime(2008, 1, 7): datetime(2008, 1, 3),
  1470. datetime(2008, 1, 8): datetime(2008, 1, 4),
  1471. datetime(2008, 1, 9): datetime(2008, 1, 7)}))
  1472. apply_cases.append((CDay(0), {
  1473. datetime(2008, 1, 1): datetime(2008, 1, 1),
  1474. datetime(2008, 1, 4): datetime(2008, 1, 4),
  1475. datetime(2008, 1, 5): datetime(2008, 1, 7),
  1476. datetime(2008, 1, 6): datetime(2008, 1, 7),
  1477. datetime(2008, 1, 7): datetime(2008, 1, 7)}))
  1478. @pytest.mark.parametrize('case', apply_cases)
  1479. def test_apply(self, case):
  1480. offset, cases = case
  1481. for base, expected in compat.iteritems(cases):
  1482. assert_offset_equal(offset, base, expected)
  1483. def test_apply_large_n(self):
  1484. dt = datetime(2012, 10, 23)
  1485. result = dt + CDay(10)
  1486. assert result == datetime(2012, 11, 6)
  1487. result = dt + CDay(100) - CDay(100)
  1488. assert result == dt
  1489. off = CDay() * 6
  1490. rs = datetime(2012, 1, 1) - off
  1491. xp = datetime(2011, 12, 23)
  1492. assert rs == xp
  1493. st = datetime(2011, 12, 18)
  1494. rs = st + off
  1495. xp = datetime(2011, 12, 26)
  1496. assert rs == xp
  1497. def test_apply_corner(self):
  1498. pytest.raises(Exception, CDay().apply, BMonthEnd())
  1499. def test_holidays(self):
  1500. # Define a TradingDay offset
  1501. holidays = ['2012-05-01', datetime(2013, 5, 1),
  1502. np.datetime64('2014-05-01')]
  1503. tday = CDay(holidays=holidays)
  1504. for year in range(2012, 2015):
  1505. dt = datetime(year, 4, 30)
  1506. xp = datetime(year, 5, 2)
  1507. rs = dt + tday
  1508. assert rs == xp
  1509. def test_weekmask(self):
  1510. weekmask_saudi = 'Sat Sun Mon Tue Wed' # Thu-Fri Weekend
  1511. weekmask_uae = '1111001' # Fri-Sat Weekend
  1512. weekmask_egypt = [1, 1, 1, 1, 0, 0, 1] # Fri-Sat Weekend
  1513. bday_saudi = CDay(weekmask=weekmask_saudi)
  1514. bday_uae = CDay(weekmask=weekmask_uae)
  1515. bday_egypt = CDay(weekmask=weekmask_egypt)
  1516. dt = datetime(2013, 5, 1)
  1517. xp_saudi = datetime(2013, 5, 4)
  1518. xp_uae = datetime(2013, 5, 2)
  1519. xp_egypt = datetime(2013, 5, 2)
  1520. assert xp_saudi == dt + bday_saudi
  1521. assert xp_uae == dt + bday_uae
  1522. assert xp_egypt == dt + bday_egypt
  1523. xp2 = datetime(2013, 5, 5)
  1524. assert xp2 == dt + 2 * bday_saudi
  1525. assert xp2 == dt + 2 * bday_uae
  1526. assert xp2 == dt + 2 * bday_egypt
  1527. def test_weekmask_and_holidays(self):
  1528. weekmask_egypt = 'Sun Mon Tue Wed Thu' # Fri-Sat Weekend
  1529. holidays = ['2012-05-01', datetime(2013, 5, 1),
  1530. np.datetime64('2014-05-01')]
  1531. bday_egypt = CDay(holidays=holidays, weekmask=weekmask_egypt)
  1532. dt = datetime(2013, 4, 30)
  1533. xp_egypt = datetime(2013, 5, 5)
  1534. assert xp_egypt == dt + 2 * bday_egypt
  1535. @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning")
  1536. def test_calendar(self):
  1537. calendar = USFederalHolidayCalendar()
  1538. dt = datetime(2014, 1, 17)
  1539. assert_offset_equal(CDay(calendar=calendar), dt, datetime(2014, 1, 21))
  1540. def test_roundtrip_pickle(self):
  1541. def _check_roundtrip(obj):
  1542. unpickled = tm.round_trip_pickle(obj)
  1543. assert unpickled == obj
  1544. _check_roundtrip(self.offset)
  1545. _check_roundtrip(self.offset2)
  1546. _check_roundtrip(self.offset * 2)
  1547. def test_pickle_compat_0_14_1(self, datapath):
  1548. hdays = [datetime(2013, 1, 1) for ele in range(4)]
  1549. pth = datapath('tseries', 'offsets', 'data', 'cday-0.14.1.pickle')
  1550. cday0_14_1 = read_pickle(pth)
  1551. cday = CDay(holidays=hdays)
  1552. assert cday == cday0_14_1
  1553. class CustomBusinessMonthBase(object):
  1554. def setup_method(self, method):
  1555. self.d = datetime(2008, 1, 1)
  1556. self.offset = self._offset()
  1557. self.offset1 = self.offset
  1558. self.offset2 = self._offset(2)
  1559. def test_eq(self):
  1560. assert self.offset2 == self.offset2
  1561. def test_mul(self):
  1562. pass
  1563. def test_hash(self):
  1564. assert hash(self.offset2) == hash(self.offset2)
  1565. def test_roundtrip_pickle(self):
  1566. def _check_roundtrip(obj):
  1567. unpickled = tm.round_trip_pickle(obj)
  1568. assert unpickled == obj
  1569. _check_roundtrip(self._offset())
  1570. _check_roundtrip(self._offset(2))
  1571. _check_roundtrip(self._offset() * 2)
  1572. def test_copy(self):
  1573. # GH 17452
  1574. off = self._offset(weekmask='Mon Wed Fri')
  1575. assert off == off.copy()
  1576. class TestCustomBusinessMonthEnd(CustomBusinessMonthBase, Base):
  1577. _offset = CBMonthEnd
  1578. def test_different_normalize_equals(self):
  1579. # GH#21404 changed __eq__ to return False when `normalize` doesnt match
  1580. offset = self._offset()
  1581. offset2 = self._offset(normalize=True)
  1582. assert offset != offset2
  1583. def test_repr(self):
  1584. assert repr(self.offset) == '<CustomBusinessMonthEnd>'
  1585. assert repr(self.offset2) == '<2 * CustomBusinessMonthEnds>'
  1586. def testCall(self):
  1587. assert self.offset2(self.d) == datetime(2008, 2, 29)
  1588. def testRollback1(self):
  1589. assert (CDay(10).rollback(datetime(2007, 12, 31)) ==
  1590. datetime(2007, 12, 31))
  1591. def testRollback2(self):
  1592. assert CBMonthEnd(10).rollback(self.d) == datetime(2007, 12, 31)
  1593. def testRollforward1(self):
  1594. assert CBMonthEnd(10).rollforward(self.d) == datetime(2008, 1, 31)
  1595. def test_roll_date_object(self):
  1596. offset = CBMonthEnd()
  1597. dt = date(2012, 9, 15)
  1598. result = offset.rollback(dt)
  1599. assert result == datetime(2012, 8, 31)
  1600. result = offset.rollforward(dt)
  1601. assert result == datetime(2012, 9, 28)
  1602. offset = offsets.Day()
  1603. result = offset.rollback(dt)
  1604. assert result == datetime(2012, 9, 15)
  1605. result = offset.rollforward(dt)
  1606. assert result == datetime(2012, 9, 15)
  1607. on_offset_cases = [(CBMonthEnd(), datetime(2008, 1, 31), True),
  1608. (CBMonthEnd(), datetime(2008, 1, 1), False)]
  1609. @pytest.mark.parametrize('case', on_offset_cases)
  1610. def test_onOffset(self, case):
  1611. offset, d, expected = case
  1612. assert_onOffset(offset, d, expected)
  1613. apply_cases = []
  1614. apply_cases.append((CBMonthEnd(), {
  1615. datetime(2008, 1, 1): datetime(2008, 1, 31),
  1616. datetime(2008, 2, 7): datetime(2008, 2, 29)}))
  1617. apply_cases.append((2 * CBMonthEnd(), {
  1618. datetime(2008, 1, 1): datetime(2008, 2, 29),
  1619. datetime(2008, 2, 7): datetime(2008, 3, 31)}))
  1620. apply_cases.append((-CBMonthEnd(), {
  1621. datetime(2008, 1, 1): datetime(2007, 12, 31),
  1622. datetime(2008, 2, 8): datetime(2008, 1, 31)}))
  1623. apply_cases.append((-2 * CBMonthEnd(), {
  1624. datetime(2008, 1, 1): datetime(2007, 11, 30),
  1625. datetime(2008, 2, 9): datetime(2007, 12, 31)}))
  1626. apply_cases.append((CBMonthEnd(0), {
  1627. datetime(2008, 1, 1): datetime(2008, 1, 31),
  1628. datetime(2008, 2, 7): datetime(2008, 2, 29)}))
  1629. @pytest.mark.parametrize('case', apply_cases)
  1630. def test_apply(self, case):
  1631. offset, cases = case
  1632. for base, expected in compat.iteritems(cases):
  1633. assert_offset_equal(offset, base, expected)
  1634. def test_apply_large_n(self):
  1635. dt = datetime(2012, 10, 23)
  1636. result = dt + CBMonthEnd(10)
  1637. assert result == datetime(2013, 7, 31)
  1638. result = dt + CDay(100) - CDay(100)
  1639. assert result == dt
  1640. off = CBMonthEnd() * 6
  1641. rs = datetime(2012, 1, 1) - off
  1642. xp = datetime(2011, 7, 29)
  1643. assert rs == xp
  1644. st = datetime(2011, 12, 18)
  1645. rs = st + off
  1646. xp = datetime(2012, 5, 31)
  1647. assert rs == xp
  1648. def test_holidays(self):
  1649. # Define a TradingDay offset
  1650. holidays = ['2012-01-31', datetime(2012, 2, 28),
  1651. np.datetime64('2012-02-29')]
  1652. bm_offset = CBMonthEnd(holidays=holidays)
  1653. dt = datetime(2012, 1, 1)
  1654. assert dt + bm_offset == datetime(2012, 1, 30)
  1655. assert dt + 2 * bm_offset == datetime(2012, 2, 27)
  1656. @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning")
  1657. def test_datetimeindex(self):
  1658. from pandas.tseries.holiday import USFederalHolidayCalendar
  1659. hcal = USFederalHolidayCalendar()
  1660. freq = CBMonthEnd(calendar=hcal)
  1661. assert (date_range(start='20120101', end='20130101',
  1662. freq=freq).tolist()[0] == datetime(2012, 1, 31))
  1663. class TestCustomBusinessMonthBegin(CustomBusinessMonthBase, Base):
  1664. _offset = CBMonthBegin
  1665. def test_different_normalize_equals(self):
  1666. # GH#21404 changed __eq__ to return False when `normalize` doesnt match
  1667. offset = self._offset()
  1668. offset2 = self._offset(normalize=True)
  1669. assert offset != offset2
  1670. def test_repr(self):
  1671. assert repr(self.offset) == '<CustomBusinessMonthBegin>'
  1672. assert repr(self.offset2) == '<2 * CustomBusinessMonthBegins>'
  1673. def testCall(self):
  1674. assert self.offset2(self.d) == datetime(2008, 3, 3)
  1675. def testRollback1(self):
  1676. assert (CDay(10).rollback(datetime(2007, 12, 31)) ==
  1677. datetime(2007, 12, 31))
  1678. def testRollback2(self):
  1679. assert CBMonthBegin(10).rollback(self.d) == datetime(2008, 1, 1)
  1680. def testRollforward1(self):
  1681. assert CBMonthBegin(10).rollforward(self.d) == datetime(2008, 1, 1)
  1682. def test_roll_date_object(self):
  1683. offset = CBMonthBegin()
  1684. dt = date(2012, 9, 15)
  1685. result = offset.rollback(dt)
  1686. assert result == datetime(2012, 9, 3)
  1687. result = offset.rollforward(dt)
  1688. assert result == datetime(2012, 10, 1)
  1689. offset = offsets.Day()
  1690. result = offset.rollback(dt)
  1691. assert result == datetime(2012, 9, 15)
  1692. result = offset.rollforward(dt)
  1693. assert result == datetime(2012, 9, 15)
  1694. on_offset_cases = [(CBMonthBegin(), datetime(2008, 1, 1), True),
  1695. (CBMonthBegin(), datetime(2008, 1, 31), False)]
  1696. @pytest.mark.parametrize('case', on_offset_cases)
  1697. def test_onOffset(self, case):
  1698. offset, dt, expected = case
  1699. assert_onOffset(offset, dt, expected)
  1700. apply_cases = []
  1701. apply_cases.append((CBMonthBegin(), {
  1702. datetime(2008, 1, 1): datetime(2008, 2, 1),
  1703. datetime(2008, 2, 7): datetime(2008, 3, 3)}))
  1704. apply_cases.append((2 * CBMonthBegin(), {
  1705. datetime(2008, 1, 1): datetime(2008, 3, 3),
  1706. datetime(2008, 2, 7): datetime(2008, 4, 1)}))
  1707. apply_cases.append((-CBMonthBegin(), {
  1708. datetime(2008, 1, 1): datetime(2007, 12, 3),
  1709. datetime(2008, 2, 8): datetime(2008, 2, 1)}))
  1710. apply_cases.append((-2 * CBMonthBegin(), {
  1711. datetime(2008, 1, 1): datetime(2007, 11, 1),
  1712. datetime(2008, 2, 9): datetime(2008, 1, 1)}))
  1713. apply_cases.append((CBMonthBegin(0), {
  1714. datetime(2008, 1, 1): datetime(2008, 1, 1),
  1715. datetime(2008, 1, 7): datetime(2008, 2, 1)}))
  1716. @pytest.mark.parametrize('case', apply_cases)
  1717. def test_apply(self, case):
  1718. offset, cases = case
  1719. for base, expected in compat.iteritems(cases):
  1720. assert_offset_equal(offset, base, expected)
  1721. def test_apply_large_n(self):
  1722. dt = datetime(2012, 10, 23)
  1723. result = dt + CBMonthBegin(10)
  1724. assert result == datetime(2013, 8, 1)
  1725. result = dt + CDay(100) - CDay(100)
  1726. assert result == dt
  1727. off = CBMonthBegin() * 6
  1728. rs = datetime(2012, 1, 1) - off
  1729. xp = datetime(2011, 7, 1)
  1730. assert rs == xp
  1731. st = datetime(2011, 12, 18)
  1732. rs = st + off
  1733. xp = datetime(2012, 6, 1)
  1734. assert rs == xp
  1735. def test_holidays(self):
  1736. # Define a TradingDay offset
  1737. holidays = ['2012-02-01', datetime(2012, 2, 2),
  1738. np.datetime64('2012-03-01')]
  1739. bm_offset = CBMonthBegin(holidays=holidays)
  1740. dt = datetime(2012, 1, 1)
  1741. assert dt + bm_offset == datetime(2012, 1, 2)
  1742. assert dt + 2 * bm_offset == datetime(2012, 2, 3)
  1743. @pytest.mark.filterwarnings("ignore:Non:pandas.errors.PerformanceWarning")
  1744. def test_datetimeindex(self):
  1745. hcal = USFederalHolidayCalendar()
  1746. cbmb = CBMonthBegin(calendar=hcal)
  1747. assert (date_range(start='20120101', end='20130101',
  1748. freq=cbmb).tolist()[0] == datetime(2012, 1, 3))
  1749. class TestWeek(Base):
  1750. _offset = Week
  1751. d = Timestamp(datetime(2008, 1, 2))
  1752. offset1 = _offset()
  1753. offset2 = _offset(2)
  1754. def test_repr(self):
  1755. assert repr(Week(weekday=0)) == "<Week: weekday=0>"
  1756. assert repr(Week(n=-1, weekday=0)) == "<-1 * Week: weekday=0>"
  1757. assert repr(Week(n=-2, weekday=0)) == "<-2 * Weeks: weekday=0>"
  1758. def test_corner(self):
  1759. with pytest.raises(ValueError):
  1760. Week(weekday=7)
  1761. with pytest.raises(ValueError, match="Day must be"):
  1762. Week(weekday=-1)
  1763. def test_isAnchored(self):
  1764. assert Week(weekday=0).isAnchored()
  1765. assert not Week().isAnchored()
  1766. assert not Week(2, weekday=2).isAnchored()
  1767. assert not Week(2).isAnchored()
  1768. offset_cases = []
  1769. # not business week
  1770. offset_cases.append((Week(), {
  1771. datetime(2008, 1, 1): datetime(2008, 1, 8),
  1772. datetime(2008, 1, 4): datetime(2008, 1, 11),
  1773. datetime(2008, 1, 5): datetime(2008, 1, 12),
  1774. datetime(2008, 1, 6): datetime(2008, 1, 13),
  1775. datetime(2008, 1, 7): datetime(2008, 1, 14)}))
  1776. # Mon
  1777. offset_cases.append((Week(weekday=0), {
  1778. datetime(2007, 12, 31): datetime(2008, 1, 7),
  1779. datetime(2008, 1, 4): datetime(2008, 1, 7),
  1780. datetime(2008, 1, 5): datetime(2008, 1, 7),
  1781. datetime(2008, 1, 6): datetime(2008, 1, 7),
  1782. datetime(2008, 1, 7): datetime(2008, 1, 14)}))
  1783. # n=0 -> roll forward. Mon
  1784. offset_cases.append((Week(0, weekday=0), {
  1785. datetime(2007, 12, 31): datetime(2007, 12, 31),
  1786. datetime(2008, 1, 4): datetime(2008, 1, 7),
  1787. datetime(2008, 1, 5): datetime(2008, 1, 7),
  1788. datetime(2008, 1, 6): datetime(2008, 1, 7),
  1789. datetime(2008, 1, 7): datetime(2008, 1, 7)}))
  1790. # n=0 -> roll forward. Mon
  1791. offset_cases.append((Week(-2, weekday=1), {
  1792. datetime(2010, 4, 6): datetime(2010, 3, 23),
  1793. datetime(2010, 4, 8): datetime(2010, 3, 30),
  1794. datetime(2010, 4, 5): datetime(2010, 3, 23)}))
  1795. @pytest.mark.parametrize('case', offset_cases)
  1796. def test_offset(self, case):
  1797. offset, cases = case
  1798. for base, expected in compat.iteritems(cases):
  1799. assert_offset_equal(offset, base, expected)
  1800. @pytest.mark.parametrize('weekday', range(7))
  1801. def test_onOffset(self, weekday):
  1802. offset = Week(weekday=weekday)
  1803. for day in range(1, 8):
  1804. date = datetime(2008, 1, day)
  1805. if day % 7 == weekday:
  1806. expected = True
  1807. else:
  1808. expected = False
  1809. assert_onOffset(offset, date, expected)
  1810. class TestWeekOfMonth(Base):
  1811. _offset = WeekOfMonth
  1812. offset1 = _offset()
  1813. offset2 = _offset(2)
  1814. def test_constructor(self):
  1815. with pytest.raises(ValueError, match="^Week"):
  1816. WeekOfMonth(n=1, week=4, weekday=0)
  1817. with pytest.raises(ValueError, match="^Week"):
  1818. WeekOfMonth(n=1, week=-1, weekday=0)
  1819. with pytest.raises(ValueError, match="^Day"):
  1820. WeekOfMonth(n=1, week=0, weekday=-1)
  1821. with pytest.raises(ValueError, match="^Day"):
  1822. WeekOfMonth(n=1, week=0, weekday=-7)
  1823. def test_repr(self):
  1824. assert (repr(WeekOfMonth(weekday=1, week=2)) ==
  1825. "<WeekOfMonth: week=2, weekday=1>")
  1826. def test_offset(self):
  1827. date1 = datetime(2011, 1, 4) # 1st Tuesday of Month
  1828. date2 = datetime(2011, 1, 11) # 2nd Tuesday of Month
  1829. date3 = datetime(2011, 1, 18) # 3rd Tuesday of Month
  1830. date4 = datetime(2011, 1, 25) # 4th Tuesday of Month
  1831. # see for loop for structure
  1832. test_cases = [
  1833. (-2, 2, 1, date1, datetime(2010, 11, 16)),
  1834. (-2, 2, 1, date2, datetime(2010, 11, 16)),
  1835. (-2, 2, 1, date3, datetime(2010, 11, 16)),
  1836. (-2, 2, 1, date4, datetime(2010, 12, 21)),
  1837. (-1, 2, 1, date1, datetime(2010, 12, 21)),
  1838. (-1, 2, 1, date2, datetime(2010, 12, 21)),
  1839. (-1, 2, 1, date3, datetime(2010, 12, 21)),
  1840. (-1, 2, 1, date4, datetime(2011, 1, 18)),
  1841. (0, 0, 1, date1, datetime(2011, 1, 4)),
  1842. (0, 0, 1, date2, datetime(2011, 2, 1)),
  1843. (0, 0, 1, date3, datetime(2011, 2, 1)),
  1844. (0, 0, 1, date4, datetime(2011, 2, 1)),
  1845. (0, 1, 1, date1, datetime(2011, 1, 11)),
  1846. (0, 1, 1, date2, datetime(2011, 1, 11)),
  1847. (0, 1, 1, date3, datetime(2011, 2, 8)),
  1848. (0, 1, 1, date4, datetime(2011, 2, 8)),
  1849. (0, 0, 1, date1, datetime(2011, 1, 4)),
  1850. (0, 1, 1, date2, datetime(2011, 1, 11)),
  1851. (0, 2, 1, date3, datetime(2011, 1, 18)),
  1852. (0, 3, 1, date4, datetime(2011, 1, 25)),
  1853. (1, 0, 0, date1, datetime(2011, 2, 7)),
  1854. (1, 0, 0, date2, datetime(2011, 2, 7)),
  1855. (1, 0, 0, date3, datetime(2011, 2, 7)),
  1856. (1, 0, 0, date4, datetime(2011, 2, 7)),
  1857. (1, 0, 1, date1, datetime(2011, 2, 1)),
  1858. (1, 0, 1, date2, datetime(2011, 2, 1)),
  1859. (1, 0, 1, date3, datetime(2011, 2, 1)),
  1860. (1, 0, 1, date4, datetime(2011, 2, 1)),
  1861. (1, 0, 2, date1, datetime(2011, 1, 5)),
  1862. (1, 0, 2, date2, datetime(2011, 2, 2)),
  1863. (1, 0, 2, date3, datetime(2011, 2, 2)),
  1864. (1, 0, 2, date4, datetime(2011, 2, 2)),
  1865. (1, 2, 1, date1, datetime(2011, 1, 18)),
  1866. (1, 2, 1, date2, datetime(2011, 1, 18)),
  1867. (1, 2, 1, date3, datetime(2011, 2, 15)),
  1868. (1, 2, 1, date4, datetime(2011, 2, 15)),
  1869. (2, 2, 1, date1, datetime(2011, 2, 15)),
  1870. (2, 2, 1, date2, datetime(2011, 2, 15)),
  1871. (2, 2, 1, date3, datetime(2011, 3, 15)),
  1872. (2, 2, 1, date4, datetime(2011, 3, 15))]
  1873. for n, week, weekday, dt, expected in test_cases:
  1874. offset = WeekOfMonth(n, week=week, weekday=weekday)
  1875. assert_offset_equal(offset, dt, expected)
  1876. # try subtracting
  1877. result = datetime(2011, 2, 1) - WeekOfMonth(week=1, weekday=2)
  1878. assert result == datetime(2011, 1, 12)
  1879. result = datetime(2011, 2, 3) - WeekOfMonth(week=0, weekday=2)
  1880. assert result == datetime(2011, 2, 2)
  1881. on_offset_cases = [(0, 0, datetime(2011, 2, 7), True),
  1882. (0, 0, datetime(2011, 2, 6), False),
  1883. (0, 0, datetime(2011, 2, 14), False),
  1884. (1, 0, datetime(2011, 2, 14), True),
  1885. (0, 1, datetime(2011, 2, 1), True),
  1886. (0, 1, datetime(2011, 2, 8), False)]
  1887. @pytest.mark.parametrize('case', on_offset_cases)
  1888. def test_onOffset(self, case):
  1889. week, weekday, dt, expected = case
  1890. offset = WeekOfMonth(week=week, weekday=weekday)
  1891. assert offset.onOffset(dt) == expected
  1892. class TestLastWeekOfMonth(Base):
  1893. _offset = LastWeekOfMonth
  1894. offset1 = _offset()
  1895. offset2 = _offset(2)
  1896. def test_constructor(self):
  1897. with pytest.raises(ValueError, match="^N cannot be 0"):
  1898. LastWeekOfMonth(n=0, weekday=1)
  1899. with pytest.raises(ValueError, match="^Day"):
  1900. LastWeekOfMonth(n=1, weekday=-1)
  1901. with pytest.raises(ValueError, match="^Day"):
  1902. LastWeekOfMonth(n=1, weekday=7)
  1903. def test_offset(self):
  1904. # Saturday
  1905. last_sat = datetime(2013, 8, 31)
  1906. next_sat = datetime(2013, 9, 28)
  1907. offset_sat = LastWeekOfMonth(n=1, weekday=5)
  1908. one_day_before = (last_sat + timedelta(days=-1))
  1909. assert one_day_before + offset_sat == last_sat
  1910. one_day_after = (last_sat + timedelta(days=+1))
  1911. assert one_day_after + offset_sat == next_sat
  1912. # Test On that day
  1913. assert last_sat + offset_sat == next_sat
  1914. # Thursday
  1915. offset_thur = LastWeekOfMonth(n=1, weekday=3)
  1916. last_thurs = datetime(2013, 1, 31)
  1917. next_thurs = datetime(2013, 2, 28)
  1918. one_day_before = last_thurs + timedelta(days=-1)
  1919. assert one_day_before + offset_thur == last_thurs
  1920. one_day_after = last_thurs + timedelta(days=+1)
  1921. assert one_day_after + offset_thur == next_thurs
  1922. # Test on that day
  1923. assert last_thurs + offset_thur == next_thurs
  1924. three_before = last_thurs + timedelta(days=-3)
  1925. assert three_before + offset_thur == last_thurs
  1926. two_after = last_thurs + timedelta(days=+2)
  1927. assert two_after + offset_thur == next_thurs
  1928. offset_sunday = LastWeekOfMonth(n=1, weekday=WeekDay.SUN)
  1929. assert datetime(2013, 7, 31) + offset_sunday == datetime(2013, 8, 25)
  1930. on_offset_cases = [
  1931. (WeekDay.SUN, datetime(2013, 1, 27), True),
  1932. (WeekDay.SAT, datetime(2013, 3, 30), True),
  1933. (WeekDay.MON, datetime(2013, 2, 18), False), # Not the last Mon
  1934. (WeekDay.SUN, datetime(2013, 2, 25), False), # Not a SUN
  1935. (WeekDay.MON, datetime(2013, 2, 25), True),
  1936. (WeekDay.SAT, datetime(2013, 11, 30), True),
  1937. (WeekDay.SAT, datetime(2006, 8, 26), True),
  1938. (WeekDay.SAT, datetime(2007, 8, 25), True),
  1939. (WeekDay.SAT, datetime(2008, 8, 30), True),
  1940. (WeekDay.SAT, datetime(2009, 8, 29), True),
  1941. (WeekDay.SAT, datetime(2010, 8, 28), True),
  1942. (WeekDay.SAT, datetime(2011, 8, 27), True),
  1943. (WeekDay.SAT, datetime(2019, 8, 31), True)]
  1944. @pytest.mark.parametrize('case', on_offset_cases)
  1945. def test_onOffset(self, case):
  1946. weekday, dt, expected = case
  1947. offset = LastWeekOfMonth(weekday=weekday)
  1948. assert offset.onOffset(dt) == expected
  1949. class TestSemiMonthEnd(Base):
  1950. _offset = SemiMonthEnd
  1951. offset1 = _offset()
  1952. offset2 = _offset(2)
  1953. def test_offset_whole_year(self):
  1954. dates = (datetime(2007, 12, 31),
  1955. datetime(2008, 1, 15),
  1956. datetime(2008, 1, 31),
  1957. datetime(2008, 2, 15),
  1958. datetime(2008, 2, 29),
  1959. datetime(2008, 3, 15),
  1960. datetime(2008, 3, 31),
  1961. datetime(2008, 4, 15),
  1962. datetime(2008, 4, 30),
  1963. datetime(2008, 5, 15),
  1964. datetime(2008, 5, 31),
  1965. datetime(2008, 6, 15),
  1966. datetime(2008, 6, 30),
  1967. datetime(2008, 7, 15),
  1968. datetime(2008, 7, 31),
  1969. datetime(2008, 8, 15),
  1970. datetime(2008, 8, 31),
  1971. datetime(2008, 9, 15),
  1972. datetime(2008, 9, 30),
  1973. datetime(2008, 10, 15),
  1974. datetime(2008, 10, 31),
  1975. datetime(2008, 11, 15),
  1976. datetime(2008, 11, 30),
  1977. datetime(2008, 12, 15),
  1978. datetime(2008, 12, 31))
  1979. for base, exp_date in zip(dates[:-1], dates[1:]):
  1980. assert_offset_equal(SemiMonthEnd(), base, exp_date)
  1981. # ensure .apply_index works as expected
  1982. s = DatetimeIndex(dates[:-1])
  1983. with tm.assert_produces_warning(None):
  1984. # GH#22535 check that we don't get a FutureWarning from adding
  1985. # an integer array to PeriodIndex
  1986. result = SemiMonthEnd().apply_index(s)
  1987. exp = DatetimeIndex(dates[1:])
  1988. tm.assert_index_equal(result, exp)
  1989. # ensure generating a range with DatetimeIndex gives same result
  1990. result = date_range(start=dates[0], end=dates[-1], freq='SM')
  1991. exp = DatetimeIndex(dates)
  1992. tm.assert_index_equal(result, exp)
  1993. offset_cases = []
  1994. offset_cases.append((SemiMonthEnd(), {
  1995. datetime(2008, 1, 1): datetime(2008, 1, 15),
  1996. datetime(2008, 1, 15): datetime(2008, 1, 31),
  1997. datetime(2008, 1, 31): datetime(2008, 2, 15),
  1998. datetime(2006, 12, 14): datetime(2006, 12, 15),
  1999. datetime(2006, 12, 29): datetime(2006, 12, 31),
  2000. datetime(2006, 12, 31): datetime(2007, 1, 15),
  2001. datetime(2007, 1, 1): datetime(2007, 1, 15),
  2002. datetime(2006, 12, 1): datetime(2006, 12, 15),
  2003. datetime(2006, 12, 15): datetime(2006, 12, 31)}))
  2004. offset_cases.append((SemiMonthEnd(day_of_month=20), {
  2005. datetime(2008, 1, 1): datetime(2008, 1, 20),
  2006. datetime(2008, 1, 15): datetime(2008, 1, 20),
  2007. datetime(2008, 1, 21): datetime(2008, 1, 31),
  2008. datetime(2008, 1, 31): datetime(2008, 2, 20),
  2009. datetime(2006, 12, 14): datetime(2006, 12, 20),
  2010. datetime(2006, 12, 29): datetime(2006, 12, 31),
  2011. datetime(2006, 12, 31): datetime(2007, 1, 20),
  2012. datetime(2007, 1, 1): datetime(2007, 1, 20),
  2013. datetime(2006, 12, 1): datetime(2006, 12, 20),
  2014. datetime(2006, 12, 15): datetime(2006, 12, 20)}))
  2015. offset_cases.append((SemiMonthEnd(0), {
  2016. datetime(2008, 1, 1): datetime(2008, 1, 15),
  2017. datetime(2008, 1, 16): datetime(2008, 1, 31),
  2018. datetime(2008, 1, 15): datetime(2008, 1, 15),
  2019. datetime(2008, 1, 31): datetime(2008, 1, 31),
  2020. datetime(2006, 12, 29): datetime(2006, 12, 31),
  2021. datetime(2006, 12, 31): datetime(2006, 12, 31),
  2022. datetime(2007, 1, 1): datetime(2007, 1, 15)}))
  2023. offset_cases.append((SemiMonthEnd(0, day_of_month=16), {
  2024. datetime(2008, 1, 1): datetime(2008, 1, 16),
  2025. datetime(2008, 1, 16): datetime(2008, 1, 16),
  2026. datetime(2008, 1, 15): datetime(2008, 1, 16),
  2027. datetime(2008, 1, 31): datetime(2008, 1, 31),
  2028. datetime(2006, 12, 29): datetime(2006, 12, 31),
  2029. datetime(2006, 12, 31): datetime(2006, 12, 31),
  2030. datetime(2007, 1, 1): datetime(2007, 1, 16)}))
  2031. offset_cases.append((SemiMonthEnd(2), {
  2032. datetime(2008, 1, 1): datetime(2008, 1, 31),
  2033. datetime(2008, 1, 31): datetime(2008, 2, 29),
  2034. datetime(2006, 12, 29): datetime(2007, 1, 15),
  2035. datetime(2006, 12, 31): datetime(2007, 1, 31),
  2036. datetime(2007, 1, 1): datetime(2007, 1, 31),
  2037. datetime(2007, 1, 16): datetime(2007, 2, 15),
  2038. datetime(2006, 11, 1): datetime(2006, 11, 30)}))
  2039. offset_cases.append((SemiMonthEnd(-1), {
  2040. datetime(2007, 1, 1): datetime(2006, 12, 31),
  2041. datetime(2008, 6, 30): datetime(2008, 6, 15),
  2042. datetime(2008, 12, 31): datetime(2008, 12, 15),
  2043. datetime(2006, 12, 29): datetime(2006, 12, 15),
  2044. datetime(2006, 12, 30): datetime(2006, 12, 15),
  2045. datetime(2007, 1, 1): datetime(2006, 12, 31)}))
  2046. offset_cases.append((SemiMonthEnd(-1, day_of_month=4), {
  2047. datetime(2007, 1, 1): datetime(2006, 12, 31),
  2048. datetime(2007, 1, 4): datetime(2006, 12, 31),
  2049. datetime(2008, 6, 30): datetime(2008, 6, 4),
  2050. datetime(2008, 12, 31): datetime(2008, 12, 4),
  2051. datetime(2006, 12, 5): datetime(2006, 12, 4),
  2052. datetime(2006, 12, 30): datetime(2006, 12, 4),
  2053. datetime(2007, 1, 1): datetime(2006, 12, 31)}))
  2054. offset_cases.append((SemiMonthEnd(-2), {
  2055. datetime(2007, 1, 1): datetime(2006, 12, 15),
  2056. datetime(2008, 6, 30): datetime(2008, 5, 31),
  2057. datetime(2008, 3, 15): datetime(2008, 2, 15),
  2058. datetime(2008, 12, 31): datetime(2008, 11, 30),
  2059. datetime(2006, 12, 29): datetime(2006, 11, 30),
  2060. datetime(2006, 12, 14): datetime(2006, 11, 15),
  2061. datetime(2007, 1, 1): datetime(2006, 12, 15)}))
  2062. @pytest.mark.parametrize('case', offset_cases)
  2063. def test_offset(self, case):
  2064. offset, cases = case
  2065. for base, expected in compat.iteritems(cases):
  2066. assert_offset_equal(offset, base, expected)
  2067. @pytest.mark.parametrize('case', offset_cases)
  2068. def test_apply_index(self, case):
  2069. offset, cases = case
  2070. s = DatetimeIndex(cases.keys())
  2071. with tm.assert_produces_warning(None):
  2072. # GH#22535 check that we don't get a FutureWarning from adding
  2073. # an integer array to PeriodIndex
  2074. result = offset.apply_index(s)
  2075. exp = DatetimeIndex(cases.values())
  2076. tm.assert_index_equal(result, exp)
  2077. on_offset_cases = [(datetime(2007, 12, 31), True),
  2078. (datetime(2007, 12, 15), True),
  2079. (datetime(2007, 12, 14), False),
  2080. (datetime(2007, 12, 1), False),
  2081. (datetime(2008, 2, 29), True)]
  2082. @pytest.mark.parametrize('case', on_offset_cases)
  2083. def test_onOffset(self, case):
  2084. dt, expected = case
  2085. assert_onOffset(SemiMonthEnd(), dt, expected)
  2086. @pytest.mark.parametrize('klass', [Series, DatetimeIndex])
  2087. def test_vectorized_offset_addition(self, klass):
  2088. s = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
  2089. Timestamp('2000-02-15', tz='US/Central')], name='a')
  2090. with tm.assert_produces_warning(None):
  2091. # GH#22535 check that we don't get a FutureWarning from adding
  2092. # an integer array to PeriodIndex
  2093. result = s + SemiMonthEnd()
  2094. result2 = SemiMonthEnd() + s
  2095. exp = klass([Timestamp('2000-01-31 00:15:00', tz='US/Central'),
  2096. Timestamp('2000-02-29', tz='US/Central')], name='a')
  2097. tm.assert_equal(result, exp)
  2098. tm.assert_equal(result2, exp)
  2099. s = klass([Timestamp('2000-01-01 00:15:00', tz='US/Central'),
  2100. Timestamp('2000-02-01', tz='US/Central')], name='a')
  2101. with tm.assert_produces_warning(None):
  2102. # GH#22535 check that we don't get a FutureWarning from adding
  2103. # an integer array to PeriodIndex
  2104. result = s + SemiMonthEnd()
  2105. result2 = SemiMonthEnd() + s
  2106. exp = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
  2107. Timestamp('2000-02-15', tz='US/Central')], name='a')
  2108. tm.assert_equal(result, exp)
  2109. tm.assert_equal(result2, exp)
  2110. class TestSemiMonthBegin(Base):
  2111. _offset = SemiMonthBegin
  2112. offset1 = _offset()
  2113. offset2 = _offset(2)
  2114. def test_offset_whole_year(self):
  2115. dates = (datetime(2007, 12, 15),
  2116. datetime(2008, 1, 1),
  2117. datetime(2008, 1, 15),
  2118. datetime(2008, 2, 1),
  2119. datetime(2008, 2, 15),
  2120. datetime(2008, 3, 1),
  2121. datetime(2008, 3, 15),
  2122. datetime(2008, 4, 1),
  2123. datetime(2008, 4, 15),
  2124. datetime(2008, 5, 1),
  2125. datetime(2008, 5, 15),
  2126. datetime(2008, 6, 1),
  2127. datetime(2008, 6, 15),
  2128. datetime(2008, 7, 1),
  2129. datetime(2008, 7, 15),
  2130. datetime(2008, 8, 1),
  2131. datetime(2008, 8, 15),
  2132. datetime(2008, 9, 1),
  2133. datetime(2008, 9, 15),
  2134. datetime(2008, 10, 1),
  2135. datetime(2008, 10, 15),
  2136. datetime(2008, 11, 1),
  2137. datetime(2008, 11, 15),
  2138. datetime(2008, 12, 1),
  2139. datetime(2008, 12, 15))
  2140. for base, exp_date in zip(dates[:-1], dates[1:]):
  2141. assert_offset_equal(SemiMonthBegin(), base, exp_date)
  2142. # ensure .apply_index works as expected
  2143. s = DatetimeIndex(dates[:-1])
  2144. with tm.assert_produces_warning(None):
  2145. # GH#22535 check that we don't get a FutureWarning from adding
  2146. # an integer array to PeriodIndex
  2147. result = SemiMonthBegin().apply_index(s)
  2148. exp = DatetimeIndex(dates[1:])
  2149. tm.assert_index_equal(result, exp)
  2150. # ensure generating a range with DatetimeIndex gives same result
  2151. result = date_range(start=dates[0], end=dates[-1], freq='SMS')
  2152. exp = DatetimeIndex(dates)
  2153. tm.assert_index_equal(result, exp)
  2154. offset_cases = []
  2155. offset_cases.append((SemiMonthBegin(), {
  2156. datetime(2008, 1, 1): datetime(2008, 1, 15),
  2157. datetime(2008, 1, 15): datetime(2008, 2, 1),
  2158. datetime(2008, 1, 31): datetime(2008, 2, 1),
  2159. datetime(2006, 12, 14): datetime(2006, 12, 15),
  2160. datetime(2006, 12, 29): datetime(2007, 1, 1),
  2161. datetime(2006, 12, 31): datetime(2007, 1, 1),
  2162. datetime(2007, 1, 1): datetime(2007, 1, 15),
  2163. datetime(2006, 12, 1): datetime(2006, 12, 15),
  2164. datetime(2006, 12, 15): datetime(2007, 1, 1)}))
  2165. offset_cases.append((SemiMonthBegin(day_of_month=20), {
  2166. datetime(2008, 1, 1): datetime(2008, 1, 20),
  2167. datetime(2008, 1, 15): datetime(2008, 1, 20),
  2168. datetime(2008, 1, 21): datetime(2008, 2, 1),
  2169. datetime(2008, 1, 31): datetime(2008, 2, 1),
  2170. datetime(2006, 12, 14): datetime(2006, 12, 20),
  2171. datetime(2006, 12, 29): datetime(2007, 1, 1),
  2172. datetime(2006, 12, 31): datetime(2007, 1, 1),
  2173. datetime(2007, 1, 1): datetime(2007, 1, 20),
  2174. datetime(2006, 12, 1): datetime(2006, 12, 20),
  2175. datetime(2006, 12, 15): datetime(2006, 12, 20)}))
  2176. offset_cases.append((SemiMonthBegin(0), {
  2177. datetime(2008, 1, 1): datetime(2008, 1, 1),
  2178. datetime(2008, 1, 16): datetime(2008, 2, 1),
  2179. datetime(2008, 1, 15): datetime(2008, 1, 15),
  2180. datetime(2008, 1, 31): datetime(2008, 2, 1),
  2181. datetime(2006, 12, 29): datetime(2007, 1, 1),
  2182. datetime(2006, 12, 2): datetime(2006, 12, 15),
  2183. datetime(2007, 1, 1): datetime(2007, 1, 1)}))
  2184. offset_cases.append((SemiMonthBegin(0, day_of_month=16), {
  2185. datetime(2008, 1, 1): datetime(2008, 1, 1),
  2186. datetime(2008, 1, 16): datetime(2008, 1, 16),
  2187. datetime(2008, 1, 15): datetime(2008, 1, 16),
  2188. datetime(2008, 1, 31): datetime(2008, 2, 1),
  2189. datetime(2006, 12, 29): datetime(2007, 1, 1),
  2190. datetime(2006, 12, 31): datetime(2007, 1, 1),
  2191. datetime(2007, 1, 5): datetime(2007, 1, 16),
  2192. datetime(2007, 1, 1): datetime(2007, 1, 1)}))
  2193. offset_cases.append((SemiMonthBegin(2), {
  2194. datetime(2008, 1, 1): datetime(2008, 2, 1),
  2195. datetime(2008, 1, 31): datetime(2008, 2, 15),
  2196. datetime(2006, 12, 1): datetime(2007, 1, 1),
  2197. datetime(2006, 12, 29): datetime(2007, 1, 15),
  2198. datetime(2006, 12, 15): datetime(2007, 1, 15),
  2199. datetime(2007, 1, 1): datetime(2007, 2, 1),
  2200. datetime(2007, 1, 16): datetime(2007, 2, 15),
  2201. datetime(2006, 11, 1): datetime(2006, 12, 1)}))
  2202. offset_cases.append((SemiMonthBegin(-1), {
  2203. datetime(2007, 1, 1): datetime(2006, 12, 15),
  2204. datetime(2008, 6, 30): datetime(2008, 6, 15),
  2205. datetime(2008, 6, 14): datetime(2008, 6, 1),
  2206. datetime(2008, 12, 31): datetime(2008, 12, 15),
  2207. datetime(2006, 12, 29): datetime(2006, 12, 15),
  2208. datetime(2006, 12, 15): datetime(2006, 12, 1),
  2209. datetime(2007, 1, 1): datetime(2006, 12, 15)}))
  2210. offset_cases.append((SemiMonthBegin(-1, day_of_month=4), {
  2211. datetime(2007, 1, 1): datetime(2006, 12, 4),
  2212. datetime(2007, 1, 4): datetime(2007, 1, 1),
  2213. datetime(2008, 6, 30): datetime(2008, 6, 4),
  2214. datetime(2008, 12, 31): datetime(2008, 12, 4),
  2215. datetime(2006, 12, 5): datetime(2006, 12, 4),
  2216. datetime(2006, 12, 30): datetime(2006, 12, 4),
  2217. datetime(2006, 12, 2): datetime(2006, 12, 1),
  2218. datetime(2007, 1, 1): datetime(2006, 12, 4)}))
  2219. offset_cases.append((SemiMonthBegin(-2), {
  2220. datetime(2007, 1, 1): datetime(2006, 12, 1),
  2221. datetime(2008, 6, 30): datetime(2008, 6, 1),
  2222. datetime(2008, 6, 14): datetime(2008, 5, 15),
  2223. datetime(2008, 12, 31): datetime(2008, 12, 1),
  2224. datetime(2006, 12, 29): datetime(2006, 12, 1),
  2225. datetime(2006, 12, 15): datetime(2006, 11, 15),
  2226. datetime(2007, 1, 1): datetime(2006, 12, 1)}))
  2227. @pytest.mark.parametrize('case', offset_cases)
  2228. def test_offset(self, case):
  2229. offset, cases = case
  2230. for base, expected in compat.iteritems(cases):
  2231. assert_offset_equal(offset, base, expected)
  2232. @pytest.mark.parametrize('case', offset_cases)
  2233. def test_apply_index(self, case):
  2234. offset, cases = case
  2235. s = DatetimeIndex(cases.keys())
  2236. with tm.assert_produces_warning(None):
  2237. # GH#22535 check that we don't get a FutureWarning from adding
  2238. # an integer array to PeriodIndex
  2239. result = offset.apply_index(s)
  2240. exp = DatetimeIndex(cases.values())
  2241. tm.assert_index_equal(result, exp)
  2242. on_offset_cases = [(datetime(2007, 12, 1), True),
  2243. (datetime(2007, 12, 15), True),
  2244. (datetime(2007, 12, 14), False),
  2245. (datetime(2007, 12, 31), False),
  2246. (datetime(2008, 2, 15), True)]
  2247. @pytest.mark.parametrize('case', on_offset_cases)
  2248. def test_onOffset(self, case):
  2249. dt, expected = case
  2250. assert_onOffset(SemiMonthBegin(), dt, expected)
  2251. @pytest.mark.parametrize('klass', [Series, DatetimeIndex])
  2252. def test_vectorized_offset_addition(self, klass):
  2253. s = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
  2254. Timestamp('2000-02-15', tz='US/Central')], name='a')
  2255. with tm.assert_produces_warning(None):
  2256. # GH#22535 check that we don't get a FutureWarning from adding
  2257. # an integer array to PeriodIndex
  2258. result = s + SemiMonthBegin()
  2259. result2 = SemiMonthBegin() + s
  2260. exp = klass([Timestamp('2000-02-01 00:15:00', tz='US/Central'),
  2261. Timestamp('2000-03-01', tz='US/Central')], name='a')
  2262. tm.assert_equal(result, exp)
  2263. tm.assert_equal(result2, exp)
  2264. s = klass([Timestamp('2000-01-01 00:15:00', tz='US/Central'),
  2265. Timestamp('2000-02-01', tz='US/Central')], name='a')
  2266. with tm.assert_produces_warning(None):
  2267. # GH#22535 check that we don't get a FutureWarning from adding
  2268. # an integer array to PeriodIndex
  2269. result = s + SemiMonthBegin()
  2270. result2 = SemiMonthBegin() + s
  2271. exp = klass([Timestamp('2000-01-15 00:15:00', tz='US/Central'),
  2272. Timestamp('2000-02-15', tz='US/Central')], name='a')
  2273. tm.assert_equal(result, exp)
  2274. tm.assert_equal(result2, exp)
  2275. def test_Easter():
  2276. assert_offset_equal(Easter(), datetime(2010, 1, 1), datetime(2010, 4, 4))
  2277. assert_offset_equal(Easter(), datetime(2010, 4, 5), datetime(2011, 4, 24))
  2278. assert_offset_equal(Easter(2), datetime(2010, 1, 1), datetime(2011, 4, 24))
  2279. assert_offset_equal(Easter(), datetime(2010, 4, 4), datetime(2011, 4, 24))
  2280. assert_offset_equal(Easter(2), datetime(2010, 4, 4), datetime(2012, 4, 8))
  2281. assert_offset_equal(-Easter(), datetime(2011, 1, 1), datetime(2010, 4, 4))
  2282. assert_offset_equal(-Easter(), datetime(2010, 4, 5), datetime(2010, 4, 4))
  2283. assert_offset_equal(-Easter(2),
  2284. datetime(2011, 1, 1),
  2285. datetime(2009, 4, 12))
  2286. assert_offset_equal(-Easter(), datetime(2010, 4, 4), datetime(2009, 4, 12))
  2287. assert_offset_equal(-Easter(2),
  2288. datetime(2010, 4, 4),
  2289. datetime(2008, 3, 23))
  2290. class TestOffsetNames(object):
  2291. def test_get_offset_name(self):
  2292. assert BDay().freqstr == 'B'
  2293. assert BDay(2).freqstr == '2B'
  2294. assert BMonthEnd().freqstr == 'BM'
  2295. assert Week(weekday=0).freqstr == 'W-MON'
  2296. assert Week(weekday=1).freqstr == 'W-TUE'
  2297. assert Week(weekday=2).freqstr == 'W-WED'
  2298. assert Week(weekday=3).freqstr == 'W-THU'
  2299. assert Week(weekday=4).freqstr == 'W-FRI'
  2300. assert LastWeekOfMonth(weekday=WeekDay.SUN).freqstr == "LWOM-SUN"
  2301. def test_get_offset():
  2302. with pytest.raises(ValueError, match=INVALID_FREQ_ERR_MSG):
  2303. get_offset('gibberish')
  2304. with pytest.raises(ValueError, match=INVALID_FREQ_ERR_MSG):
  2305. get_offset('QS-JAN-B')
  2306. pairs = [
  2307. ('B', BDay()), ('b', BDay()), ('bm', BMonthEnd()),
  2308. ('Bm', BMonthEnd()), ('W-MON', Week(weekday=0)),
  2309. ('W-TUE', Week(weekday=1)), ('W-WED', Week(weekday=2)),
  2310. ('W-THU', Week(weekday=3)), ('W-FRI', Week(weekday=4))]
  2311. for name, expected in pairs:
  2312. offset = get_offset(name)
  2313. assert offset == expected, ("Expected %r to yield %r (actual: %r)" %
  2314. (name, expected, offset))
  2315. def test_get_offset_legacy():
  2316. pairs = [('w@Sat', Week(weekday=5))]
  2317. for name, expected in pairs:
  2318. with pytest.raises(ValueError, match=INVALID_FREQ_ERR_MSG):
  2319. get_offset(name)
  2320. class TestOffsetAliases(object):
  2321. def setup_method(self, method):
  2322. _offset_map.clear()
  2323. def test_alias_equality(self):
  2324. for k, v in compat.iteritems(_offset_map):
  2325. if v is None:
  2326. continue
  2327. assert k == v.copy()
  2328. def test_rule_code(self):
  2329. lst = ['M', 'MS', 'BM', 'BMS', 'D', 'B', 'H', 'T', 'S', 'L', 'U']
  2330. for k in lst:
  2331. assert k == get_offset(k).rule_code
  2332. # should be cached - this is kind of an internals test...
  2333. assert k in _offset_map
  2334. assert k == (get_offset(k) * 3).rule_code
  2335. suffix_lst = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN']
  2336. base = 'W'
  2337. for v in suffix_lst:
  2338. alias = '-'.join([base, v])
  2339. assert alias == get_offset(alias).rule_code
  2340. assert alias == (get_offset(alias) * 5).rule_code
  2341. suffix_lst = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG',
  2342. 'SEP', 'OCT', 'NOV', 'DEC']
  2343. base_lst = ['A', 'AS', 'BA', 'BAS', 'Q', 'QS', 'BQ', 'BQS']
  2344. for base in base_lst:
  2345. for v in suffix_lst:
  2346. alias = '-'.join([base, v])
  2347. assert alias == get_offset(alias).rule_code
  2348. assert alias == (get_offset(alias) * 5).rule_code
  2349. lst = ['M', 'D', 'B', 'H', 'T', 'S', 'L', 'U']
  2350. for k in lst:
  2351. code, stride = get_freq_code('3' + k)
  2352. assert isinstance(code, int)
  2353. assert stride == 3
  2354. assert k == get_freq_str(code)
  2355. def test_dateoffset_misc():
  2356. oset = offsets.DateOffset(months=2, days=4)
  2357. # it works
  2358. oset.freqstr
  2359. assert (not offsets.DateOffset(months=2) == 2)
  2360. def test_freq_offsets():
  2361. off = BDay(1, offset=timedelta(0, 1800))
  2362. assert (off.freqstr == 'B+30Min')
  2363. off = BDay(1, offset=timedelta(0, -1800))
  2364. assert (off.freqstr == 'B-30Min')
  2365. class TestReprNames(object):
  2366. def test_str_for_named_is_name(self):
  2367. # look at all the amazing combinations!
  2368. month_prefixes = ['A', 'AS', 'BA', 'BAS', 'Q', 'BQ', 'BQS', 'QS']
  2369. names = [prefix + '-' + month
  2370. for prefix in month_prefixes
  2371. for month in ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL',
  2372. 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']]
  2373. days = ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN']
  2374. names += ['W-' + day for day in days]
  2375. names += ['WOM-' + week + day
  2376. for week in ('1', '2', '3', '4') for day in days]
  2377. _offset_map.clear()
  2378. for name in names:
  2379. offset = get_offset(name)
  2380. assert offset.freqstr == name
  2381. def get_utc_offset_hours(ts):
  2382. # take a Timestamp and compute total hours of utc offset
  2383. o = ts.utcoffset()
  2384. return (o.days * 24 * 3600 + o.seconds) / 3600.0
  2385. class TestDST(object):
  2386. """
  2387. test DateOffset additions over Daylight Savings Time
  2388. """
  2389. # one microsecond before the DST transition
  2390. ts_pre_fallback = "2013-11-03 01:59:59.999999"
  2391. ts_pre_springfwd = "2013-03-10 01:59:59.999999"
  2392. # test both basic names and dateutil timezones
  2393. timezone_utc_offsets = {
  2394. 'US/Eastern': dict(utc_offset_daylight=-4,
  2395. utc_offset_standard=-5, ),
  2396. 'dateutil/US/Pacific': dict(utc_offset_daylight=-7,
  2397. utc_offset_standard=-8, )
  2398. }
  2399. valid_date_offsets_singular = [
  2400. 'weekday', 'day', 'hour', 'minute', 'second', 'microsecond'
  2401. ]
  2402. valid_date_offsets_plural = [
  2403. 'weeks', 'days',
  2404. 'hours', 'minutes', 'seconds',
  2405. 'milliseconds', 'microseconds'
  2406. ]
  2407. def _test_all_offsets(self, n, **kwds):
  2408. valid_offsets = self.valid_date_offsets_plural if n > 1 \
  2409. else self.valid_date_offsets_singular
  2410. for name in valid_offsets:
  2411. self._test_offset(offset_name=name, offset_n=n, **kwds)
  2412. def _test_offset(self, offset_name, offset_n, tstart, expected_utc_offset):
  2413. offset = DateOffset(**{offset_name: offset_n})
  2414. t = tstart + offset
  2415. if expected_utc_offset is not None:
  2416. assert get_utc_offset_hours(t) == expected_utc_offset
  2417. if offset_name == 'weeks':
  2418. # dates should match
  2419. assert t.date() == timedelta(days=7 * offset.kwds[
  2420. 'weeks']) + tstart.date()
  2421. # expect the same day of week, hour of day, minute, second, ...
  2422. assert (t.dayofweek == tstart.dayofweek and
  2423. t.hour == tstart.hour and
  2424. t.minute == tstart.minute and
  2425. t.second == tstart.second)
  2426. elif offset_name == 'days':
  2427. # dates should match
  2428. assert timedelta(offset.kwds['days']) + tstart.date() == t.date()
  2429. # expect the same hour of day, minute, second, ...
  2430. assert (t.hour == tstart.hour and
  2431. t.minute == tstart.minute and
  2432. t.second == tstart.second)
  2433. elif offset_name in self.valid_date_offsets_singular:
  2434. # expect the singular offset value to match between tstart and t
  2435. datepart_offset = getattr(t, offset_name
  2436. if offset_name != 'weekday' else
  2437. 'dayofweek')
  2438. assert datepart_offset == offset.kwds[offset_name]
  2439. else:
  2440. # the offset should be the same as if it was done in UTC
  2441. assert (t == (tstart.tz_convert('UTC') + offset)
  2442. .tz_convert('US/Pacific'))
  2443. def _make_timestamp(self, string, hrs_offset, tz):
  2444. if hrs_offset >= 0:
  2445. offset_string = '{hrs:02d}00'.format(hrs=hrs_offset)
  2446. else:
  2447. offset_string = '-{hrs:02d}00'.format(hrs=-1 * hrs_offset)
  2448. return Timestamp(string + offset_string).tz_convert(tz)
  2449. def test_fallback_plural(self):
  2450. # test moving from daylight savings to standard time
  2451. import dateutil
  2452. for tz, utc_offsets in self.timezone_utc_offsets.items():
  2453. hrs_pre = utc_offsets['utc_offset_daylight']
  2454. hrs_post = utc_offsets['utc_offset_standard']
  2455. if LooseVersion(dateutil.__version__) < LooseVersion('2.6.0'):
  2456. # buggy ambiguous behavior in 2.6.0
  2457. # GH 14621
  2458. # https://github.com/dateutil/dateutil/issues/321
  2459. self._test_all_offsets(
  2460. n=3, tstart=self._make_timestamp(self.ts_pre_fallback,
  2461. hrs_pre, tz),
  2462. expected_utc_offset=hrs_post)
  2463. elif LooseVersion(dateutil.__version__) > LooseVersion('2.6.0'):
  2464. # fixed, but skip the test
  2465. continue
  2466. def test_springforward_plural(self):
  2467. # test moving from standard to daylight savings
  2468. for tz, utc_offsets in self.timezone_utc_offsets.items():
  2469. hrs_pre = utc_offsets['utc_offset_standard']
  2470. hrs_post = utc_offsets['utc_offset_daylight']
  2471. self._test_all_offsets(
  2472. n=3, tstart=self._make_timestamp(self.ts_pre_springfwd,
  2473. hrs_pre, tz),
  2474. expected_utc_offset=hrs_post)
  2475. def test_fallback_singular(self):
  2476. # in the case of singular offsets, we don't necessarily know which utc
  2477. # offset the new Timestamp will wind up in (the tz for 1 month may be
  2478. # different from 1 second) so we don't specify an expected_utc_offset
  2479. for tz, utc_offsets in self.timezone_utc_offsets.items():
  2480. hrs_pre = utc_offsets['utc_offset_standard']
  2481. self._test_all_offsets(n=1, tstart=self._make_timestamp(
  2482. self.ts_pre_fallback, hrs_pre, tz), expected_utc_offset=None)
  2483. def test_springforward_singular(self):
  2484. for tz, utc_offsets in self.timezone_utc_offsets.items():
  2485. hrs_pre = utc_offsets['utc_offset_standard']
  2486. self._test_all_offsets(n=1, tstart=self._make_timestamp(
  2487. self.ts_pre_springfwd, hrs_pre, tz), expected_utc_offset=None)
  2488. offset_classes = {MonthBegin: ['11/2/2012', '12/1/2012'],
  2489. MonthEnd: ['11/2/2012', '11/30/2012'],
  2490. BMonthBegin: ['11/2/2012', '12/3/2012'],
  2491. BMonthEnd: ['11/2/2012', '11/30/2012'],
  2492. CBMonthBegin: ['11/2/2012', '12/3/2012'],
  2493. CBMonthEnd: ['11/2/2012', '11/30/2012'],
  2494. SemiMonthBegin: ['11/2/2012', '11/15/2012'],
  2495. SemiMonthEnd: ['11/2/2012', '11/15/2012'],
  2496. Week: ['11/2/2012', '11/9/2012'],
  2497. YearBegin: ['11/2/2012', '1/1/2013'],
  2498. YearEnd: ['11/2/2012', '12/31/2012'],
  2499. BYearBegin: ['11/2/2012', '1/1/2013'],
  2500. BYearEnd: ['11/2/2012', '12/31/2012'],
  2501. QuarterBegin: ['11/2/2012', '12/1/2012'],
  2502. QuarterEnd: ['11/2/2012', '12/31/2012'],
  2503. BQuarterBegin: ['11/2/2012', '12/3/2012'],
  2504. BQuarterEnd: ['11/2/2012', '12/31/2012'],
  2505. Day: ['11/4/2012', '11/4/2012 23:00']}.items()
  2506. @pytest.mark.parametrize('tup', offset_classes)
  2507. def test_all_offset_classes(self, tup):
  2508. offset, test_values = tup
  2509. first = Timestamp(test_values[0], tz='US/Eastern') + offset()
  2510. second = Timestamp(test_values[1], tz='US/Eastern')
  2511. assert first == second
  2512. # ---------------------------------------------------------------------
  2513. def test_get_offset_day_error():
  2514. # subclass of _BaseOffset must override _day_opt attribute, or we should
  2515. # get a NotImplementedError
  2516. with pytest.raises(NotImplementedError):
  2517. DateOffset()._get_offset_day(datetime.now())
  2518. def test_valid_default_arguments(offset_types):
  2519. # GH#19142 check that the calling the constructors without passing
  2520. # any keyword arguments produce valid offsets
  2521. cls = offset_types
  2522. cls()
  2523. @pytest.mark.parametrize('kwd', sorted(list(liboffsets.relativedelta_kwds)))
  2524. def test_valid_month_attributes(kwd, month_classes):
  2525. # GH#18226
  2526. cls = month_classes
  2527. # check that we cannot create e.g. MonthEnd(weeks=3)
  2528. with pytest.raises(TypeError):
  2529. cls(**{kwd: 3})
  2530. @pytest.mark.parametrize('kwd', sorted(list(liboffsets.relativedelta_kwds)))
  2531. def test_valid_relativedelta_kwargs(kwd):
  2532. # Check that all the arguments specified in liboffsets.relativedelta_kwds
  2533. # are in fact valid relativedelta keyword args
  2534. DateOffset(**{kwd: 1})
  2535. @pytest.mark.parametrize('kwd', sorted(list(liboffsets.relativedelta_kwds)))
  2536. def test_valid_tick_attributes(kwd, tick_classes):
  2537. # GH#18226
  2538. cls = tick_classes
  2539. # check that we cannot create e.g. Hour(weeks=3)
  2540. with pytest.raises(TypeError):
  2541. cls(**{kwd: 3})
  2542. def test_validate_n_error():
  2543. with pytest.raises(TypeError):
  2544. DateOffset(n='Doh!')
  2545. with pytest.raises(TypeError):
  2546. MonthBegin(n=timedelta(1))
  2547. with pytest.raises(TypeError):
  2548. BDay(n=np.array([1, 2], dtype=np.int64))
  2549. def test_require_integers(offset_types):
  2550. cls = offset_types
  2551. with pytest.raises(ValueError):
  2552. cls(n=1.5)
  2553. def test_tick_normalize_raises(tick_classes):
  2554. # check that trying to create a Tick object with normalize=True raises
  2555. # GH#21427
  2556. cls = tick_classes
  2557. with pytest.raises(ValueError):
  2558. cls(n=3, normalize=True)
  2559. def test_weeks_onoffset():
  2560. # GH#18510 Week with weekday = None, normalize = False should always
  2561. # be onOffset
  2562. offset = Week(n=2, weekday=None)
  2563. ts = Timestamp('1862-01-13 09:03:34.873477378+0210', tz='Africa/Lusaka')
  2564. fast = offset.onOffset(ts)
  2565. slow = (ts + offset) - offset == ts
  2566. assert fast == slow
  2567. # negative n
  2568. offset = Week(n=2, weekday=None)
  2569. ts = Timestamp('1856-10-24 16:18:36.556360110-0717', tz='Pacific/Easter')
  2570. fast = offset.onOffset(ts)
  2571. slow = (ts + offset) - offset == ts
  2572. assert fast == slow
  2573. def test_weekofmonth_onoffset():
  2574. # GH#18864
  2575. # Make sure that nanoseconds don't trip up onOffset (and with it apply)
  2576. offset = WeekOfMonth(n=2, week=2, weekday=0)
  2577. ts = Timestamp('1916-05-15 01:14:49.583410462+0422', tz='Asia/Qyzylorda')
  2578. fast = offset.onOffset(ts)
  2579. slow = (ts + offset) - offset == ts
  2580. assert fast == slow
  2581. # negative n
  2582. offset = WeekOfMonth(n=-3, week=1, weekday=0)
  2583. ts = Timestamp('1980-12-08 03:38:52.878321185+0500', tz='Asia/Oral')
  2584. fast = offset.onOffset(ts)
  2585. slow = (ts + offset) - offset == ts
  2586. assert fast == slow
  2587. def test_last_week_of_month_on_offset():
  2588. # GH#19036, GH#18977 _adjust_dst was incorrect for LastWeekOfMonth
  2589. offset = LastWeekOfMonth(n=4, weekday=6)
  2590. ts = Timestamp('1917-05-27 20:55:27.084284178+0200',
  2591. tz='Europe/Warsaw')
  2592. slow = (ts + offset) - offset == ts
  2593. fast = offset.onOffset(ts)
  2594. assert fast == slow
  2595. # negative n
  2596. offset = LastWeekOfMonth(n=-4, weekday=5)
  2597. ts = Timestamp('2005-08-27 05:01:42.799392561-0500',
  2598. tz='America/Rainy_River')
  2599. slow = (ts + offset) - offset == ts
  2600. fast = offset.onOffset(ts)
  2601. assert fast == slow