test_timezones.py 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213
  1. """
  2. Tests for DatetimeIndex timezone-related methods
  3. """
  4. from datetime import (
  5. date,
  6. datetime,
  7. time,
  8. timedelta,
  9. timezone,
  10. tzinfo,
  11. )
  12. import dateutil
  13. from dateutil.tz import (
  14. gettz,
  15. tzlocal,
  16. )
  17. import numpy as np
  18. import pytest
  19. import pytz
  20. try:
  21. from zoneinfo import ZoneInfo
  22. except ImportError:
  23. ZoneInfo = None
  24. from pandas._libs.tslibs import (
  25. conversion,
  26. timezones,
  27. )
  28. import pandas.util._test_decorators as td
  29. import pandas as pd
  30. from pandas import (
  31. DatetimeIndex,
  32. Index,
  33. Timestamp,
  34. bdate_range,
  35. date_range,
  36. isna,
  37. to_datetime,
  38. )
  39. import pandas._testing as tm
  40. class FixedOffset(tzinfo):
  41. """Fixed offset in minutes east from UTC."""
  42. def __init__(self, offset, name) -> None:
  43. self.__offset = timedelta(minutes=offset)
  44. self.__name = name
  45. def utcoffset(self, dt):
  46. return self.__offset
  47. def tzname(self, dt):
  48. return self.__name
  49. def dst(self, dt):
  50. return timedelta(0)
  51. fixed_off = FixedOffset(-420, "-07:00")
  52. fixed_off_no_name = FixedOffset(-330, None)
  53. class TestDatetimeIndexTimezones:
  54. # -------------------------------------------------------------
  55. # DatetimeIndex.tz_convert
  56. def test_tz_convert_nat(self):
  57. # GH#5546
  58. dates = [pd.NaT]
  59. idx = DatetimeIndex(dates)
  60. idx = idx.tz_localize("US/Pacific")
  61. tm.assert_index_equal(idx, DatetimeIndex(dates, tz="US/Pacific"))
  62. idx = idx.tz_convert("US/Eastern")
  63. tm.assert_index_equal(idx, DatetimeIndex(dates, tz="US/Eastern"))
  64. idx = idx.tz_convert("UTC")
  65. tm.assert_index_equal(idx, DatetimeIndex(dates, tz="UTC"))
  66. dates = ["2010-12-01 00:00", "2010-12-02 00:00", pd.NaT]
  67. idx = DatetimeIndex(dates)
  68. idx = idx.tz_localize("US/Pacific")
  69. tm.assert_index_equal(idx, DatetimeIndex(dates, tz="US/Pacific"))
  70. idx = idx.tz_convert("US/Eastern")
  71. expected = ["2010-12-01 03:00", "2010-12-02 03:00", pd.NaT]
  72. tm.assert_index_equal(idx, DatetimeIndex(expected, tz="US/Eastern"))
  73. idx = idx + pd.offsets.Hour(5)
  74. expected = ["2010-12-01 08:00", "2010-12-02 08:00", pd.NaT]
  75. tm.assert_index_equal(idx, DatetimeIndex(expected, tz="US/Eastern"))
  76. idx = idx.tz_convert("US/Pacific")
  77. expected = ["2010-12-01 05:00", "2010-12-02 05:00", pd.NaT]
  78. tm.assert_index_equal(idx, DatetimeIndex(expected, tz="US/Pacific"))
  79. idx = idx + np.timedelta64(3, "h")
  80. expected = ["2010-12-01 08:00", "2010-12-02 08:00", pd.NaT]
  81. tm.assert_index_equal(idx, DatetimeIndex(expected, tz="US/Pacific"))
  82. idx = idx.tz_convert("US/Eastern")
  83. expected = ["2010-12-01 11:00", "2010-12-02 11:00", pd.NaT]
  84. tm.assert_index_equal(idx, DatetimeIndex(expected, tz="US/Eastern"))
  85. @pytest.mark.parametrize("prefix", ["", "dateutil/"])
  86. def test_dti_tz_convert_compat_timestamp(self, prefix):
  87. strdates = ["1/1/2012", "3/1/2012", "4/1/2012"]
  88. idx = DatetimeIndex(strdates, tz=prefix + "US/Eastern")
  89. conv = idx[0].tz_convert(prefix + "US/Pacific")
  90. expected = idx.tz_convert(prefix + "US/Pacific")[0]
  91. assert conv == expected
  92. def test_dti_tz_convert_hour_overflow_dst(self):
  93. # Regression test for:
  94. # https://github.com/pandas-dev/pandas/issues/13306
  95. # sorted case US/Eastern -> UTC
  96. ts = ["2008-05-12 09:50:00", "2008-12-12 09:50:35", "2009-05-12 09:50:32"]
  97. tt = DatetimeIndex(ts).tz_localize("US/Eastern")
  98. ut = tt.tz_convert("UTC")
  99. expected = Index([13, 14, 13], dtype=np.int32)
  100. tm.assert_index_equal(ut.hour, expected)
  101. # sorted case UTC -> US/Eastern
  102. ts = ["2008-05-12 13:50:00", "2008-12-12 14:50:35", "2009-05-12 13:50:32"]
  103. tt = DatetimeIndex(ts).tz_localize("UTC")
  104. ut = tt.tz_convert("US/Eastern")
  105. expected = Index([9, 9, 9], dtype=np.int32)
  106. tm.assert_index_equal(ut.hour, expected)
  107. # unsorted case US/Eastern -> UTC
  108. ts = ["2008-05-12 09:50:00", "2008-12-12 09:50:35", "2008-05-12 09:50:32"]
  109. tt = DatetimeIndex(ts).tz_localize("US/Eastern")
  110. ut = tt.tz_convert("UTC")
  111. expected = Index([13, 14, 13], dtype=np.int32)
  112. tm.assert_index_equal(ut.hour, expected)
  113. # unsorted case UTC -> US/Eastern
  114. ts = ["2008-05-12 13:50:00", "2008-12-12 14:50:35", "2008-05-12 13:50:32"]
  115. tt = DatetimeIndex(ts).tz_localize("UTC")
  116. ut = tt.tz_convert("US/Eastern")
  117. expected = Index([9, 9, 9], dtype=np.int32)
  118. tm.assert_index_equal(ut.hour, expected)
  119. @pytest.mark.parametrize("tz", ["US/Eastern", "dateutil/US/Eastern"])
  120. def test_dti_tz_convert_hour_overflow_dst_timestamps(self, tz):
  121. # Regression test for GH#13306
  122. # sorted case US/Eastern -> UTC
  123. ts = [
  124. Timestamp("2008-05-12 09:50:00", tz=tz),
  125. Timestamp("2008-12-12 09:50:35", tz=tz),
  126. Timestamp("2009-05-12 09:50:32", tz=tz),
  127. ]
  128. tt = DatetimeIndex(ts)
  129. ut = tt.tz_convert("UTC")
  130. expected = Index([13, 14, 13], dtype=np.int32)
  131. tm.assert_index_equal(ut.hour, expected)
  132. # sorted case UTC -> US/Eastern
  133. ts = [
  134. Timestamp("2008-05-12 13:50:00", tz="UTC"),
  135. Timestamp("2008-12-12 14:50:35", tz="UTC"),
  136. Timestamp("2009-05-12 13:50:32", tz="UTC"),
  137. ]
  138. tt = DatetimeIndex(ts)
  139. ut = tt.tz_convert("US/Eastern")
  140. expected = Index([9, 9, 9], dtype=np.int32)
  141. tm.assert_index_equal(ut.hour, expected)
  142. # unsorted case US/Eastern -> UTC
  143. ts = [
  144. Timestamp("2008-05-12 09:50:00", tz=tz),
  145. Timestamp("2008-12-12 09:50:35", tz=tz),
  146. Timestamp("2008-05-12 09:50:32", tz=tz),
  147. ]
  148. tt = DatetimeIndex(ts)
  149. ut = tt.tz_convert("UTC")
  150. expected = Index([13, 14, 13], dtype=np.int32)
  151. tm.assert_index_equal(ut.hour, expected)
  152. # unsorted case UTC -> US/Eastern
  153. ts = [
  154. Timestamp("2008-05-12 13:50:00", tz="UTC"),
  155. Timestamp("2008-12-12 14:50:35", tz="UTC"),
  156. Timestamp("2008-05-12 13:50:32", tz="UTC"),
  157. ]
  158. tt = DatetimeIndex(ts)
  159. ut = tt.tz_convert("US/Eastern")
  160. expected = Index([9, 9, 9], dtype=np.int32)
  161. tm.assert_index_equal(ut.hour, expected)
  162. @pytest.mark.parametrize("freq, n", [("H", 1), ("T", 60), ("S", 3600)])
  163. def test_dti_tz_convert_trans_pos_plus_1__bug(self, freq, n):
  164. # Regression test for tslib.tz_convert(vals, tz1, tz2).
  165. # See https://github.com/pandas-dev/pandas/issues/4496 for details.
  166. idx = date_range(datetime(2011, 3, 26, 23), datetime(2011, 3, 27, 1), freq=freq)
  167. idx = idx.tz_localize("UTC")
  168. idx = idx.tz_convert("Europe/Moscow")
  169. expected = np.repeat(np.array([3, 4, 5]), np.array([n, n, 1]))
  170. tm.assert_index_equal(idx.hour, Index(expected, dtype=np.int32))
  171. def test_dti_tz_convert_dst(self):
  172. for freq, n in [("H", 1), ("T", 60), ("S", 3600)]:
  173. # Start DST
  174. idx = date_range(
  175. "2014-03-08 23:00", "2014-03-09 09:00", freq=freq, tz="UTC"
  176. )
  177. idx = idx.tz_convert("US/Eastern")
  178. expected = np.repeat(
  179. np.array([18, 19, 20, 21, 22, 23, 0, 1, 3, 4, 5]),
  180. np.array([n, n, n, n, n, n, n, n, n, n, 1]),
  181. )
  182. tm.assert_index_equal(idx.hour, Index(expected, dtype=np.int32))
  183. idx = date_range(
  184. "2014-03-08 18:00", "2014-03-09 05:00", freq=freq, tz="US/Eastern"
  185. )
  186. idx = idx.tz_convert("UTC")
  187. expected = np.repeat(
  188. np.array([23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
  189. np.array([n, n, n, n, n, n, n, n, n, n, 1]),
  190. )
  191. tm.assert_index_equal(idx.hour, Index(expected, dtype=np.int32))
  192. # End DST
  193. idx = date_range(
  194. "2014-11-01 23:00", "2014-11-02 09:00", freq=freq, tz="UTC"
  195. )
  196. idx = idx.tz_convert("US/Eastern")
  197. expected = np.repeat(
  198. np.array([19, 20, 21, 22, 23, 0, 1, 1, 2, 3, 4]),
  199. np.array([n, n, n, n, n, n, n, n, n, n, 1]),
  200. )
  201. tm.assert_index_equal(idx.hour, Index(expected, dtype=np.int32))
  202. idx = date_range(
  203. "2014-11-01 18:00", "2014-11-02 05:00", freq=freq, tz="US/Eastern"
  204. )
  205. idx = idx.tz_convert("UTC")
  206. expected = np.repeat(
  207. np.array([22, 23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
  208. np.array([n, n, n, n, n, n, n, n, n, n, n, n, 1]),
  209. )
  210. tm.assert_index_equal(idx.hour, Index(expected, dtype=np.int32))
  211. # daily
  212. # Start DST
  213. idx = date_range("2014-03-08 00:00", "2014-03-09 00:00", freq="D", tz="UTC")
  214. idx = idx.tz_convert("US/Eastern")
  215. tm.assert_index_equal(idx.hour, Index([19, 19], dtype=np.int32))
  216. idx = date_range(
  217. "2014-03-08 00:00", "2014-03-09 00:00", freq="D", tz="US/Eastern"
  218. )
  219. idx = idx.tz_convert("UTC")
  220. tm.assert_index_equal(idx.hour, Index([5, 5], dtype=np.int32))
  221. # End DST
  222. idx = date_range("2014-11-01 00:00", "2014-11-02 00:00", freq="D", tz="UTC")
  223. idx = idx.tz_convert("US/Eastern")
  224. tm.assert_index_equal(idx.hour, Index([20, 20], dtype=np.int32))
  225. idx = date_range(
  226. "2014-11-01 00:00", "2014-11-02 000:00", freq="D", tz="US/Eastern"
  227. )
  228. idx = idx.tz_convert("UTC")
  229. tm.assert_index_equal(idx.hour, Index([4, 4], dtype=np.int32))
  230. def test_tz_convert_roundtrip(self, tz_aware_fixture):
  231. tz = tz_aware_fixture
  232. idx1 = date_range(start="2014-01-01", end="2014-12-31", freq="M", tz="UTC")
  233. exp1 = date_range(start="2014-01-01", end="2014-12-31", freq="M")
  234. idx2 = date_range(start="2014-01-01", end="2014-12-31", freq="D", tz="UTC")
  235. exp2 = date_range(start="2014-01-01", end="2014-12-31", freq="D")
  236. idx3 = date_range(start="2014-01-01", end="2014-03-01", freq="H", tz="UTC")
  237. exp3 = date_range(start="2014-01-01", end="2014-03-01", freq="H")
  238. idx4 = date_range(start="2014-08-01", end="2014-10-31", freq="T", tz="UTC")
  239. exp4 = date_range(start="2014-08-01", end="2014-10-31", freq="T")
  240. for idx, expected in [(idx1, exp1), (idx2, exp2), (idx3, exp3), (idx4, exp4)]:
  241. converted = idx.tz_convert(tz)
  242. reset = converted.tz_convert(None)
  243. tm.assert_index_equal(reset, expected)
  244. assert reset.tzinfo is None
  245. expected = converted.tz_convert("UTC").tz_localize(None)
  246. expected = expected._with_freq("infer")
  247. tm.assert_index_equal(reset, expected)
  248. def test_dti_tz_convert_tzlocal(self):
  249. # GH#13583
  250. # tz_convert doesn't affect to internal
  251. dti = date_range(start="2001-01-01", end="2001-03-01", tz="UTC")
  252. dti2 = dti.tz_convert(dateutil.tz.tzlocal())
  253. tm.assert_numpy_array_equal(dti2.asi8, dti.asi8)
  254. dti = date_range(start="2001-01-01", end="2001-03-01", tz=dateutil.tz.tzlocal())
  255. dti2 = dti.tz_convert(None)
  256. tm.assert_numpy_array_equal(dti2.asi8, dti.asi8)
  257. @pytest.mark.parametrize(
  258. "tz",
  259. [
  260. "US/Eastern",
  261. "dateutil/US/Eastern",
  262. pytz.timezone("US/Eastern"),
  263. gettz("US/Eastern"),
  264. ],
  265. )
  266. def test_dti_tz_convert_utc_to_local_no_modify(self, tz):
  267. rng = date_range("3/11/2012", "3/12/2012", freq="H", tz="utc")
  268. rng_eastern = rng.tz_convert(tz)
  269. # Values are unmodified
  270. tm.assert_numpy_array_equal(rng.asi8, rng_eastern.asi8)
  271. assert timezones.tz_compare(rng_eastern.tz, timezones.maybe_get_tz(tz))
  272. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  273. def test_tz_convert_unsorted(self, tzstr):
  274. dr = date_range("2012-03-09", freq="H", periods=100, tz="utc")
  275. dr = dr.tz_convert(tzstr)
  276. result = dr[::-1].hour
  277. exp = dr.hour[::-1]
  278. tm.assert_almost_equal(result, exp)
  279. # -------------------------------------------------------------
  280. # DatetimeIndex.tz_localize
  281. def test_tz_localize_utc_copies(self, utc_fixture):
  282. # GH#46460
  283. times = ["2015-03-08 01:00", "2015-03-08 02:00", "2015-03-08 03:00"]
  284. index = DatetimeIndex(times)
  285. res = index.tz_localize(utc_fixture)
  286. assert not tm.shares_memory(res, index)
  287. res2 = index._data.tz_localize(utc_fixture)
  288. assert not tm.shares_memory(index._data, res2)
  289. def test_dti_tz_localize_nonexistent_raise_coerce(self):
  290. # GH#13057
  291. times = ["2015-03-08 01:00", "2015-03-08 02:00", "2015-03-08 03:00"]
  292. index = DatetimeIndex(times)
  293. tz = "US/Eastern"
  294. with pytest.raises(pytz.NonExistentTimeError, match="|".join(times)):
  295. index.tz_localize(tz=tz)
  296. with pytest.raises(pytz.NonExistentTimeError, match="|".join(times)):
  297. index.tz_localize(tz=tz, nonexistent="raise")
  298. result = index.tz_localize(tz=tz, nonexistent="NaT")
  299. test_times = ["2015-03-08 01:00-05:00", "NaT", "2015-03-08 03:00-04:00"]
  300. dti = to_datetime(test_times, utc=True)
  301. expected = dti.tz_convert("US/Eastern")
  302. tm.assert_index_equal(result, expected)
  303. easts = [pytz.timezone("US/Eastern"), gettz("US/Eastern")]
  304. if ZoneInfo is not None:
  305. try:
  306. tz = ZoneInfo("US/Eastern")
  307. except KeyError:
  308. # no tzdata
  309. pass
  310. else:
  311. easts.append(tz)
  312. @pytest.mark.parametrize("tz", easts)
  313. def test_dti_tz_localize_ambiguous_infer(self, tz):
  314. # November 6, 2011, fall back, repeat 2 AM hour
  315. # With no repeated hours, we cannot infer the transition
  316. dr = date_range(datetime(2011, 11, 6, 0), periods=5, freq=pd.offsets.Hour())
  317. with pytest.raises(pytz.AmbiguousTimeError, match="Cannot infer dst time"):
  318. dr.tz_localize(tz)
  319. # With repeated hours, we can infer the transition
  320. dr = date_range(
  321. datetime(2011, 11, 6, 0), periods=5, freq=pd.offsets.Hour(), tz=tz
  322. )
  323. times = [
  324. "11/06/2011 00:00",
  325. "11/06/2011 01:00",
  326. "11/06/2011 01:00",
  327. "11/06/2011 02:00",
  328. "11/06/2011 03:00",
  329. ]
  330. di = DatetimeIndex(times)
  331. localized = di.tz_localize(tz, ambiguous="infer")
  332. expected = dr._with_freq(None)
  333. tm.assert_index_equal(expected, localized)
  334. tm.assert_index_equal(expected, DatetimeIndex(times, tz=tz, ambiguous="infer"))
  335. # When there is no dst transition, nothing special happens
  336. dr = date_range(datetime(2011, 6, 1, 0), periods=10, freq=pd.offsets.Hour())
  337. localized = dr.tz_localize(tz)
  338. localized_infer = dr.tz_localize(tz, ambiguous="infer")
  339. tm.assert_index_equal(localized, localized_infer)
  340. @pytest.mark.parametrize("tz", [pytz.timezone("US/Eastern"), gettz("US/Eastern")])
  341. def test_dti_tz_localize_ambiguous_times(self, tz):
  342. # March 13, 2011, spring forward, skip from 2 AM to 3 AM
  343. dr = date_range(datetime(2011, 3, 13, 1, 30), periods=3, freq=pd.offsets.Hour())
  344. with pytest.raises(pytz.NonExistentTimeError, match="2011-03-13 02:30:00"):
  345. dr.tz_localize(tz)
  346. # after dst transition, it works
  347. dr = date_range(
  348. datetime(2011, 3, 13, 3, 30), periods=3, freq=pd.offsets.Hour(), tz=tz
  349. )
  350. # November 6, 2011, fall back, repeat 2 AM hour
  351. dr = date_range(datetime(2011, 11, 6, 1, 30), periods=3, freq=pd.offsets.Hour())
  352. with pytest.raises(pytz.AmbiguousTimeError, match="Cannot infer dst time"):
  353. dr.tz_localize(tz)
  354. # UTC is OK
  355. dr = date_range(
  356. datetime(2011, 3, 13), periods=48, freq=pd.offsets.Minute(30), tz=pytz.utc
  357. )
  358. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  359. def test_dti_tz_localize_pass_dates_to_utc(self, tzstr):
  360. strdates = ["1/1/2012", "3/1/2012", "4/1/2012"]
  361. idx = DatetimeIndex(strdates)
  362. conv = idx.tz_localize(tzstr)
  363. fromdates = DatetimeIndex(strdates, tz=tzstr)
  364. assert conv.tz == fromdates.tz
  365. tm.assert_numpy_array_equal(conv.values, fromdates.values)
  366. @pytest.mark.parametrize("prefix", ["", "dateutil/"])
  367. def test_dti_tz_localize(self, prefix):
  368. tzstr = prefix + "US/Eastern"
  369. dti = date_range(start="1/1/2005", end="1/1/2005 0:00:30.256", freq="L")
  370. dti2 = dti.tz_localize(tzstr)
  371. dti_utc = date_range(
  372. start="1/1/2005 05:00", end="1/1/2005 5:00:30.256", freq="L", tz="utc"
  373. )
  374. tm.assert_numpy_array_equal(dti2.values, dti_utc.values)
  375. dti3 = dti2.tz_convert(prefix + "US/Pacific")
  376. tm.assert_numpy_array_equal(dti3.values, dti_utc.values)
  377. dti = date_range(start="11/6/2011 1:59", end="11/6/2011 2:00", freq="L")
  378. with pytest.raises(pytz.AmbiguousTimeError, match="Cannot infer dst time"):
  379. dti.tz_localize(tzstr)
  380. dti = date_range(start="3/13/2011 1:59", end="3/13/2011 2:00", freq="L")
  381. with pytest.raises(pytz.NonExistentTimeError, match="2011-03-13 02:00:00"):
  382. dti.tz_localize(tzstr)
  383. @pytest.mark.parametrize(
  384. "tz",
  385. [
  386. "US/Eastern",
  387. "dateutil/US/Eastern",
  388. pytz.timezone("US/Eastern"),
  389. gettz("US/Eastern"),
  390. ],
  391. )
  392. def test_dti_tz_localize_utc_conversion(self, tz):
  393. # Localizing to time zone should:
  394. # 1) check for DST ambiguities
  395. # 2) convert to UTC
  396. rng = date_range("3/10/2012", "3/11/2012", freq="30T")
  397. converted = rng.tz_localize(tz)
  398. expected_naive = rng + pd.offsets.Hour(5)
  399. tm.assert_numpy_array_equal(converted.asi8, expected_naive.asi8)
  400. # DST ambiguity, this should fail
  401. rng = date_range("3/11/2012", "3/12/2012", freq="30T")
  402. # Is this really how it should fail??
  403. with pytest.raises(pytz.NonExistentTimeError, match="2012-03-11 02:00:00"):
  404. rng.tz_localize(tz)
  405. def test_dti_tz_localize_roundtrip(self, tz_aware_fixture):
  406. # note: this tz tests that a tz-naive index can be localized
  407. # and de-localized successfully, when there are no DST transitions
  408. # in the range.
  409. idx = date_range(start="2014-06-01", end="2014-08-30", freq="15T")
  410. tz = tz_aware_fixture
  411. localized = idx.tz_localize(tz)
  412. # can't localize a tz-aware object
  413. with pytest.raises(
  414. TypeError, match="Already tz-aware, use tz_convert to convert"
  415. ):
  416. localized.tz_localize(tz)
  417. reset = localized.tz_localize(None)
  418. assert reset.tzinfo is None
  419. expected = idx._with_freq(None)
  420. tm.assert_index_equal(reset, expected)
  421. def test_dti_tz_localize_naive(self):
  422. rng = date_range("1/1/2011", periods=100, freq="H")
  423. conv = rng.tz_localize("US/Pacific")
  424. exp = date_range("1/1/2011", periods=100, freq="H", tz="US/Pacific")
  425. tm.assert_index_equal(conv, exp._with_freq(None))
  426. def test_dti_tz_localize_tzlocal(self):
  427. # GH#13583
  428. offset = dateutil.tz.tzlocal().utcoffset(datetime(2011, 1, 1))
  429. offset = int(offset.total_seconds() * 1000000000)
  430. dti = date_range(start="2001-01-01", end="2001-03-01")
  431. dti2 = dti.tz_localize(dateutil.tz.tzlocal())
  432. tm.assert_numpy_array_equal(dti2.asi8 + offset, dti.asi8)
  433. dti = date_range(start="2001-01-01", end="2001-03-01", tz=dateutil.tz.tzlocal())
  434. dti2 = dti.tz_localize(None)
  435. tm.assert_numpy_array_equal(dti2.asi8 - offset, dti.asi8)
  436. @pytest.mark.parametrize("tz", [pytz.timezone("US/Eastern"), gettz("US/Eastern")])
  437. def test_dti_tz_localize_ambiguous_nat(self, tz):
  438. times = [
  439. "11/06/2011 00:00",
  440. "11/06/2011 01:00",
  441. "11/06/2011 01:00",
  442. "11/06/2011 02:00",
  443. "11/06/2011 03:00",
  444. ]
  445. di = DatetimeIndex(times)
  446. localized = di.tz_localize(tz, ambiguous="NaT")
  447. times = [
  448. "11/06/2011 00:00",
  449. np.NaN,
  450. np.NaN,
  451. "11/06/2011 02:00",
  452. "11/06/2011 03:00",
  453. ]
  454. di_test = DatetimeIndex(times, tz="US/Eastern")
  455. # left dtype is datetime64[ns, US/Eastern]
  456. # right is datetime64[ns, tzfile('/usr/share/zoneinfo/US/Eastern')]
  457. tm.assert_numpy_array_equal(di_test.values, localized.values)
  458. @pytest.mark.parametrize("tz", [pytz.timezone("US/Eastern"), gettz("US/Eastern")])
  459. def test_dti_tz_localize_ambiguous_flags(self, tz):
  460. # November 6, 2011, fall back, repeat 2 AM hour
  461. # Pass in flags to determine right dst transition
  462. dr = date_range(
  463. datetime(2011, 11, 6, 0), periods=5, freq=pd.offsets.Hour(), tz=tz
  464. )
  465. times = [
  466. "11/06/2011 00:00",
  467. "11/06/2011 01:00",
  468. "11/06/2011 01:00",
  469. "11/06/2011 02:00",
  470. "11/06/2011 03:00",
  471. ]
  472. # Test tz_localize
  473. di = DatetimeIndex(times)
  474. is_dst = [1, 1, 0, 0, 0]
  475. localized = di.tz_localize(tz, ambiguous=is_dst)
  476. expected = dr._with_freq(None)
  477. tm.assert_index_equal(expected, localized)
  478. tm.assert_index_equal(expected, DatetimeIndex(times, tz=tz, ambiguous=is_dst))
  479. localized = di.tz_localize(tz, ambiguous=np.array(is_dst))
  480. tm.assert_index_equal(dr, localized)
  481. localized = di.tz_localize(tz, ambiguous=np.array(is_dst).astype("bool"))
  482. tm.assert_index_equal(dr, localized)
  483. # Test constructor
  484. localized = DatetimeIndex(times, tz=tz, ambiguous=is_dst)
  485. tm.assert_index_equal(dr, localized)
  486. # Test duplicate times where inferring the dst fails
  487. times += times
  488. di = DatetimeIndex(times)
  489. # When the sizes are incompatible, make sure error is raised
  490. msg = "Length of ambiguous bool-array must be the same size as vals"
  491. with pytest.raises(Exception, match=msg):
  492. di.tz_localize(tz, ambiguous=is_dst)
  493. # When sizes are compatible and there are repeats ('infer' won't work)
  494. is_dst = np.hstack((is_dst, is_dst))
  495. localized = di.tz_localize(tz, ambiguous=is_dst)
  496. dr = dr.append(dr)
  497. tm.assert_index_equal(dr, localized)
  498. # When there is no dst transition, nothing special happens
  499. dr = date_range(datetime(2011, 6, 1, 0), periods=10, freq=pd.offsets.Hour())
  500. is_dst = np.array([1] * 10)
  501. localized = dr.tz_localize(tz)
  502. localized_is_dst = dr.tz_localize(tz, ambiguous=is_dst)
  503. tm.assert_index_equal(localized, localized_is_dst)
  504. # TODO: belongs outside tz_localize tests?
  505. @pytest.mark.parametrize("tz", ["Europe/London", "dateutil/Europe/London"])
  506. def test_dti_construction_ambiguous_endpoint(self, tz):
  507. # construction with an ambiguous end-point
  508. # GH#11626
  509. with pytest.raises(pytz.AmbiguousTimeError, match="Cannot infer dst time"):
  510. date_range(
  511. "2013-10-26 23:00", "2013-10-27 01:00", tz="Europe/London", freq="H"
  512. )
  513. times = date_range(
  514. "2013-10-26 23:00", "2013-10-27 01:00", freq="H", tz=tz, ambiguous="infer"
  515. )
  516. assert times[0] == Timestamp("2013-10-26 23:00", tz=tz)
  517. assert times[-1] == Timestamp("2013-10-27 01:00:00+0000", tz=tz)
  518. @pytest.mark.parametrize(
  519. "tz, option, expected",
  520. [
  521. ["US/Pacific", "shift_forward", "2019-03-10 03:00"],
  522. ["dateutil/US/Pacific", "shift_forward", "2019-03-10 03:00"],
  523. ["US/Pacific", "shift_backward", "2019-03-10 01:00"],
  524. ["dateutil/US/Pacific", "shift_backward", "2019-03-10 01:00"],
  525. ["US/Pacific", timedelta(hours=1), "2019-03-10 03:00"],
  526. ],
  527. )
  528. def test_dti_construction_nonexistent_endpoint(self, tz, option, expected):
  529. # construction with an nonexistent end-point
  530. with pytest.raises(pytz.NonExistentTimeError, match="2019-03-10 02:00:00"):
  531. date_range(
  532. "2019-03-10 00:00", "2019-03-10 02:00", tz="US/Pacific", freq="H"
  533. )
  534. times = date_range(
  535. "2019-03-10 00:00", "2019-03-10 02:00", freq="H", tz=tz, nonexistent=option
  536. )
  537. assert times[-1] == Timestamp(expected, tz=tz)
  538. def test_dti_tz_localize_bdate_range(self):
  539. dr = bdate_range("1/1/2009", "1/1/2010")
  540. dr_utc = bdate_range("1/1/2009", "1/1/2010", tz=pytz.utc)
  541. localized = dr.tz_localize(pytz.utc)
  542. tm.assert_index_equal(dr_utc, localized)
  543. @pytest.mark.parametrize(
  544. "start_ts, tz, end_ts, shift",
  545. [
  546. ["2015-03-29 02:20:00", "Europe/Warsaw", "2015-03-29 03:00:00", "forward"],
  547. [
  548. "2015-03-29 02:20:00",
  549. "Europe/Warsaw",
  550. "2015-03-29 01:59:59.999999999",
  551. "backward",
  552. ],
  553. [
  554. "2015-03-29 02:20:00",
  555. "Europe/Warsaw",
  556. "2015-03-29 03:20:00",
  557. timedelta(hours=1),
  558. ],
  559. [
  560. "2015-03-29 02:20:00",
  561. "Europe/Warsaw",
  562. "2015-03-29 01:20:00",
  563. timedelta(hours=-1),
  564. ],
  565. ["2018-03-11 02:33:00", "US/Pacific", "2018-03-11 03:00:00", "forward"],
  566. [
  567. "2018-03-11 02:33:00",
  568. "US/Pacific",
  569. "2018-03-11 01:59:59.999999999",
  570. "backward",
  571. ],
  572. [
  573. "2018-03-11 02:33:00",
  574. "US/Pacific",
  575. "2018-03-11 03:33:00",
  576. timedelta(hours=1),
  577. ],
  578. [
  579. "2018-03-11 02:33:00",
  580. "US/Pacific",
  581. "2018-03-11 01:33:00",
  582. timedelta(hours=-1),
  583. ],
  584. ],
  585. )
  586. @pytest.mark.parametrize("tz_type", ["", "dateutil/"])
  587. def test_dti_tz_localize_nonexistent_shift(
  588. self, start_ts, tz, end_ts, shift, tz_type
  589. ):
  590. # GH 8917
  591. tz = tz_type + tz
  592. if isinstance(shift, str):
  593. shift = "shift_" + shift
  594. dti = DatetimeIndex([Timestamp(start_ts)])
  595. result = dti.tz_localize(tz, nonexistent=shift)
  596. expected = DatetimeIndex([Timestamp(end_ts)]).tz_localize(tz)
  597. tm.assert_index_equal(result, expected)
  598. @pytest.mark.parametrize("offset", [-1, 1])
  599. def test_dti_tz_localize_nonexistent_shift_invalid(self, offset, warsaw):
  600. # GH 8917
  601. tz = warsaw
  602. dti = DatetimeIndex([Timestamp("2015-03-29 02:20:00")])
  603. msg = "The provided timedelta will relocalize on a nonexistent time"
  604. with pytest.raises(ValueError, match=msg):
  605. dti.tz_localize(tz, nonexistent=timedelta(seconds=offset))
  606. # -------------------------------------------------------------
  607. # DatetimeIndex.normalize
  608. def test_normalize_tz(self):
  609. rng = date_range("1/1/2000 9:30", periods=10, freq="D", tz="US/Eastern")
  610. result = rng.normalize() # does not preserve freq
  611. expected = date_range("1/1/2000", periods=10, freq="D", tz="US/Eastern")
  612. tm.assert_index_equal(result, expected._with_freq(None))
  613. assert result.is_normalized
  614. assert not rng.is_normalized
  615. rng = date_range("1/1/2000 9:30", periods=10, freq="D", tz="UTC")
  616. result = rng.normalize()
  617. expected = date_range("1/1/2000", periods=10, freq="D", tz="UTC")
  618. tm.assert_index_equal(result, expected)
  619. assert result.is_normalized
  620. assert not rng.is_normalized
  621. rng = date_range("1/1/2000 9:30", periods=10, freq="D", tz=tzlocal())
  622. result = rng.normalize() # does not preserve freq
  623. expected = date_range("1/1/2000", periods=10, freq="D", tz=tzlocal())
  624. tm.assert_index_equal(result, expected._with_freq(None))
  625. assert result.is_normalized
  626. assert not rng.is_normalized
  627. @td.skip_if_windows
  628. @pytest.mark.parametrize(
  629. "timezone",
  630. [
  631. "US/Pacific",
  632. "US/Eastern",
  633. "UTC",
  634. "Asia/Kolkata",
  635. "Asia/Shanghai",
  636. "Australia/Canberra",
  637. ],
  638. )
  639. def test_normalize_tz_local(self, timezone):
  640. # GH#13459
  641. with tm.set_timezone(timezone):
  642. rng = date_range("1/1/2000 9:30", periods=10, freq="D", tz=tzlocal())
  643. result = rng.normalize()
  644. expected = date_range("1/1/2000", periods=10, freq="D", tz=tzlocal())
  645. expected = expected._with_freq(None)
  646. tm.assert_index_equal(result, expected)
  647. assert result.is_normalized
  648. assert not rng.is_normalized
  649. # ------------------------------------------------------------
  650. # DatetimeIndex.__new__
  651. @pytest.mark.parametrize("prefix", ["", "dateutil/"])
  652. def test_dti_constructor_static_tzinfo(self, prefix):
  653. # it works!
  654. index = DatetimeIndex([datetime(2012, 1, 1)], tz=prefix + "EST")
  655. index.hour
  656. index[0]
  657. def test_dti_constructor_with_fixed_tz(self):
  658. off = FixedOffset(420, "+07:00")
  659. start = datetime(2012, 3, 11, 5, 0, 0, tzinfo=off)
  660. end = datetime(2012, 6, 11, 5, 0, 0, tzinfo=off)
  661. rng = date_range(start=start, end=end)
  662. assert off == rng.tz
  663. rng2 = date_range(start, periods=len(rng), tz=off)
  664. tm.assert_index_equal(rng, rng2)
  665. rng3 = date_range("3/11/2012 05:00:00+07:00", "6/11/2012 05:00:00+07:00")
  666. assert (rng.values == rng3.values).all()
  667. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  668. def test_dti_convert_datetime_list(self, tzstr):
  669. dr = date_range("2012-06-02", periods=10, tz=tzstr, name="foo")
  670. dr2 = DatetimeIndex(list(dr), name="foo", freq="D")
  671. tm.assert_index_equal(dr, dr2)
  672. def test_dti_construction_univalent(self):
  673. rng = date_range("03/12/2012 00:00", periods=10, freq="W-FRI", tz="US/Eastern")
  674. rng2 = DatetimeIndex(data=rng, tz="US/Eastern")
  675. tm.assert_index_equal(rng, rng2)
  676. @pytest.mark.parametrize("tz", [pytz.timezone("US/Eastern"), gettz("US/Eastern")])
  677. def test_dti_from_tzaware_datetime(self, tz):
  678. d = [datetime(2012, 8, 19, tzinfo=tz)]
  679. index = DatetimeIndex(d)
  680. assert timezones.tz_compare(index.tz, tz)
  681. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  682. def test_dti_tz_constructors(self, tzstr):
  683. """Test different DatetimeIndex constructions with timezone
  684. Follow-up of GH#4229
  685. """
  686. arr = ["11/10/2005 08:00:00", "11/10/2005 09:00:00"]
  687. idx1 = to_datetime(arr).tz_localize(tzstr)
  688. idx2 = date_range(start="2005-11-10 08:00:00", freq="H", periods=2, tz=tzstr)
  689. idx2 = idx2._with_freq(None) # the others all have freq=None
  690. idx3 = DatetimeIndex(arr, tz=tzstr)
  691. idx4 = DatetimeIndex(np.array(arr), tz=tzstr)
  692. for other in [idx2, idx3, idx4]:
  693. tm.assert_index_equal(idx1, other)
  694. # -------------------------------------------------------------
  695. # Unsorted
  696. @pytest.mark.parametrize(
  697. "dtype",
  698. [None, "datetime64[ns, CET]", "datetime64[ns, EST]", "datetime64[ns, UTC]"],
  699. )
  700. def test_date_accessor(self, dtype):
  701. # Regression test for GH#21230
  702. expected = np.array([date(2018, 6, 4), pd.NaT])
  703. index = DatetimeIndex(["2018-06-04 10:00:00", pd.NaT], dtype=dtype)
  704. result = index.date
  705. tm.assert_numpy_array_equal(result, expected)
  706. @pytest.mark.parametrize(
  707. "dtype",
  708. [None, "datetime64[ns, CET]", "datetime64[ns, EST]", "datetime64[ns, UTC]"],
  709. )
  710. def test_time_accessor(self, dtype):
  711. # Regression test for GH#21267
  712. expected = np.array([time(10, 20, 30), pd.NaT])
  713. index = DatetimeIndex(["2018-06-04 10:20:30", pd.NaT], dtype=dtype)
  714. result = index.time
  715. tm.assert_numpy_array_equal(result, expected)
  716. def test_timetz_accessor(self, tz_naive_fixture):
  717. # GH21358
  718. tz = timezones.maybe_get_tz(tz_naive_fixture)
  719. expected = np.array([time(10, 20, 30, tzinfo=tz), pd.NaT])
  720. index = DatetimeIndex(["2018-06-04 10:20:30", pd.NaT], tz=tz)
  721. result = index.timetz
  722. tm.assert_numpy_array_equal(result, expected)
  723. def test_dti_drop_dont_lose_tz(self):
  724. # GH#2621
  725. ind = date_range("2012-12-01", periods=10, tz="utc")
  726. ind = ind.drop(ind[-1])
  727. assert ind.tz is not None
  728. def test_dti_tz_conversion_freq(self, tz_naive_fixture):
  729. # GH25241
  730. t3 = DatetimeIndex(["2019-01-01 10:00"], freq="H")
  731. assert t3.tz_localize(tz=tz_naive_fixture).freq == t3.freq
  732. t4 = DatetimeIndex(["2019-01-02 12:00"], tz="UTC", freq="T")
  733. assert t4.tz_convert(tz="UTC").freq == t4.freq
  734. def test_drop_dst_boundary(self):
  735. # see gh-18031
  736. tz = "Europe/Brussels"
  737. freq = "15min"
  738. start = Timestamp("201710290100", tz=tz)
  739. end = Timestamp("201710290300", tz=tz)
  740. index = date_range(start=start, end=end, freq=freq)
  741. expected = DatetimeIndex(
  742. [
  743. "201710290115",
  744. "201710290130",
  745. "201710290145",
  746. "201710290200",
  747. "201710290215",
  748. "201710290230",
  749. "201710290245",
  750. "201710290200",
  751. "201710290215",
  752. "201710290230",
  753. "201710290245",
  754. "201710290300",
  755. ],
  756. tz=tz,
  757. freq=freq,
  758. ambiguous=[
  759. True,
  760. True,
  761. True,
  762. True,
  763. True,
  764. True,
  765. True,
  766. False,
  767. False,
  768. False,
  769. False,
  770. False,
  771. ],
  772. )
  773. result = index.drop(index[0])
  774. tm.assert_index_equal(result, expected)
  775. def test_date_range_localize(self):
  776. rng = date_range("3/11/2012 03:00", periods=15, freq="H", tz="US/Eastern")
  777. rng2 = DatetimeIndex(["3/11/2012 03:00", "3/11/2012 04:00"], tz="US/Eastern")
  778. rng3 = date_range("3/11/2012 03:00", periods=15, freq="H")
  779. rng3 = rng3.tz_localize("US/Eastern")
  780. tm.assert_index_equal(rng._with_freq(None), rng3)
  781. # DST transition time
  782. val = rng[0]
  783. exp = Timestamp("3/11/2012 03:00", tz="US/Eastern")
  784. assert val.hour == 3
  785. assert exp.hour == 3
  786. assert val == exp # same UTC value
  787. tm.assert_index_equal(rng[:2], rng2)
  788. # Right before the DST transition
  789. rng = date_range("3/11/2012 00:00", periods=2, freq="H", tz="US/Eastern")
  790. rng2 = DatetimeIndex(
  791. ["3/11/2012 00:00", "3/11/2012 01:00"], tz="US/Eastern", freq="H"
  792. )
  793. tm.assert_index_equal(rng, rng2)
  794. exp = Timestamp("3/11/2012 00:00", tz="US/Eastern")
  795. assert exp.hour == 0
  796. assert rng[0] == exp
  797. exp = Timestamp("3/11/2012 01:00", tz="US/Eastern")
  798. assert exp.hour == 1
  799. assert rng[1] == exp
  800. rng = date_range("3/11/2012 00:00", periods=10, freq="H", tz="US/Eastern")
  801. assert rng[2].hour == 3
  802. def test_timestamp_equality_different_timezones(self):
  803. utc_range = date_range("1/1/2000", periods=20, tz="UTC")
  804. eastern_range = utc_range.tz_convert("US/Eastern")
  805. berlin_range = utc_range.tz_convert("Europe/Berlin")
  806. for a, b, c in zip(utc_range, eastern_range, berlin_range):
  807. assert a == b
  808. assert b == c
  809. assert a == c
  810. assert (utc_range == eastern_range).all()
  811. assert (utc_range == berlin_range).all()
  812. assert (berlin_range == eastern_range).all()
  813. def test_dti_intersection(self):
  814. rng = date_range("1/1/2011", periods=100, freq="H", tz="utc")
  815. left = rng[10:90][::-1]
  816. right = rng[20:80][::-1]
  817. assert left.tz == rng.tz
  818. result = left.intersection(right)
  819. assert result.tz == left.tz
  820. def test_dti_equals_with_tz(self):
  821. left = date_range("1/1/2011", periods=100, freq="H", tz="utc")
  822. right = date_range("1/1/2011", periods=100, freq="H", tz="US/Eastern")
  823. assert not left.equals(right)
  824. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  825. def test_dti_tz_nat(self, tzstr):
  826. idx = DatetimeIndex([Timestamp("2013-1-1", tz=tzstr), pd.NaT])
  827. assert isna(idx[1])
  828. assert idx[0].tzinfo is not None
  829. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  830. def test_dti_astype_asobject_tzinfos(self, tzstr):
  831. # GH#1345
  832. # dates around a dst transition
  833. rng = date_range("2/13/2010", "5/6/2010", tz=tzstr)
  834. objs = rng.astype(object)
  835. for i, x in enumerate(objs):
  836. exval = rng[i]
  837. assert x == exval
  838. assert x.tzinfo == exval.tzinfo
  839. objs = rng.astype(object)
  840. for i, x in enumerate(objs):
  841. exval = rng[i]
  842. assert x == exval
  843. assert x.tzinfo == exval.tzinfo
  844. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  845. def test_dti_with_timezone_repr(self, tzstr):
  846. rng = date_range("4/13/2010", "5/6/2010")
  847. rng_eastern = rng.tz_localize(tzstr)
  848. rng_repr = repr(rng_eastern)
  849. assert "2010-04-13 00:00:00" in rng_repr
  850. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  851. def test_dti_take_dont_lose_meta(self, tzstr):
  852. rng = date_range("1/1/2000", periods=20, tz=tzstr)
  853. result = rng.take(range(5))
  854. assert result.tz == rng.tz
  855. assert result.freq == rng.freq
  856. @pytest.mark.parametrize("tzstr", ["US/Eastern", "dateutil/US/Eastern"])
  857. def test_utc_box_timestamp_and_localize(self, tzstr):
  858. tz = timezones.maybe_get_tz(tzstr)
  859. rng = date_range("3/11/2012", "3/12/2012", freq="H", tz="utc")
  860. rng_eastern = rng.tz_convert(tzstr)
  861. expected = rng[-1].astimezone(tz)
  862. stamp = rng_eastern[-1]
  863. assert stamp == expected
  864. assert stamp.tzinfo == expected.tzinfo
  865. # right tzinfo
  866. rng = date_range("3/13/2012", "3/14/2012", freq="H", tz="utc")
  867. rng_eastern = rng.tz_convert(tzstr)
  868. # test not valid for dateutil timezones.
  869. # assert 'EDT' in repr(rng_eastern[0].tzinfo)
  870. assert "EDT" in repr(rng_eastern[0].tzinfo) or "tzfile" in repr(
  871. rng_eastern[0].tzinfo
  872. )
  873. def test_dti_to_pydatetime(self):
  874. dt = dateutil.parser.parse("2012-06-13T01:39:00Z")
  875. dt = dt.replace(tzinfo=tzlocal())
  876. arr = np.array([dt], dtype=object)
  877. result = to_datetime(arr, utc=True)
  878. assert result.tz is timezone.utc
  879. rng = date_range("2012-11-03 03:00", "2012-11-05 03:00", tz=tzlocal())
  880. arr = rng.to_pydatetime()
  881. result = to_datetime(arr, utc=True)
  882. assert result.tz is timezone.utc
  883. def test_dti_to_pydatetime_fizedtz(self):
  884. dates = np.array(
  885. [
  886. datetime(2000, 1, 1, tzinfo=fixed_off),
  887. datetime(2000, 1, 2, tzinfo=fixed_off),
  888. datetime(2000, 1, 3, tzinfo=fixed_off),
  889. ]
  890. )
  891. dti = DatetimeIndex(dates)
  892. result = dti.to_pydatetime()
  893. tm.assert_numpy_array_equal(dates, result)
  894. result = dti._mpl_repr()
  895. tm.assert_numpy_array_equal(dates, result)
  896. @pytest.mark.parametrize("tz", [pytz.timezone("US/Central"), gettz("US/Central")])
  897. def test_with_tz(self, tz):
  898. # just want it to work
  899. start = datetime(2011, 3, 12, tzinfo=pytz.utc)
  900. dr = bdate_range(start, periods=50, freq=pd.offsets.Hour())
  901. assert dr.tz is pytz.utc
  902. # DateRange with naive datetimes
  903. dr = bdate_range("1/1/2005", "1/1/2009", tz=pytz.utc)
  904. dr = bdate_range("1/1/2005", "1/1/2009", tz=tz)
  905. # normalized
  906. central = dr.tz_convert(tz)
  907. assert central.tz is tz
  908. naive = central[0].to_pydatetime().replace(tzinfo=None)
  909. comp = conversion.localize_pydatetime(naive, tz).tzinfo
  910. assert central[0].tz is comp
  911. # compare vs a localized tz
  912. naive = dr[0].to_pydatetime().replace(tzinfo=None)
  913. comp = conversion.localize_pydatetime(naive, tz).tzinfo
  914. assert central[0].tz is comp
  915. # datetimes with tzinfo set
  916. dr = bdate_range(
  917. datetime(2005, 1, 1, tzinfo=pytz.utc), datetime(2009, 1, 1, tzinfo=pytz.utc)
  918. )
  919. msg = "Start and end cannot both be tz-aware with different timezones"
  920. with pytest.raises(Exception, match=msg):
  921. bdate_range(datetime(2005, 1, 1, tzinfo=pytz.utc), "1/1/2009", tz=tz)
  922. @pytest.mark.parametrize("prefix", ["", "dateutil/"])
  923. def test_field_access_localize(self, prefix):
  924. strdates = ["1/1/2012", "3/1/2012", "4/1/2012"]
  925. rng = DatetimeIndex(strdates, tz=prefix + "US/Eastern")
  926. assert (rng.hour == 0).all()
  927. # a more unusual time zone, #1946
  928. dr = date_range(
  929. "2011-10-02 00:00", freq="h", periods=10, tz=prefix + "America/Atikokan"
  930. )
  931. expected = Index(np.arange(10, dtype=np.int32))
  932. tm.assert_index_equal(dr.hour, expected)
  933. @pytest.mark.parametrize("tz", [pytz.timezone("US/Eastern"), gettz("US/Eastern")])
  934. def test_dti_convert_tz_aware_datetime_datetime(self, tz):
  935. # GH#1581
  936. dates = [datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)]
  937. dates_aware = [conversion.localize_pydatetime(x, tz) for x in dates]
  938. result = DatetimeIndex(dates_aware)
  939. assert timezones.tz_compare(result.tz, tz)
  940. converted = to_datetime(dates_aware, utc=True)
  941. ex_vals = np.array([Timestamp(x).as_unit("ns")._value for x in dates_aware])
  942. tm.assert_numpy_array_equal(converted.asi8, ex_vals)
  943. assert converted.tz is timezone.utc
  944. # Note: not difference, as there is no symmetry requirement there
  945. @pytest.mark.parametrize("setop", ["union", "intersection", "symmetric_difference"])
  946. def test_dti_setop_aware(self, setop):
  947. # non-overlapping
  948. # GH#39328 as of 2.0 we cast these to UTC instead of object
  949. rng = date_range("2012-11-15 00:00:00", periods=6, freq="H", tz="US/Central")
  950. rng2 = date_range("2012-11-15 12:00:00", periods=6, freq="H", tz="US/Eastern")
  951. result = getattr(rng, setop)(rng2)
  952. left = rng.tz_convert("UTC")
  953. right = rng2.tz_convert("UTC")
  954. expected = getattr(left, setop)(right)
  955. tm.assert_index_equal(result, expected)
  956. assert result.tz == left.tz
  957. if len(result):
  958. assert result[0].tz is timezone.utc
  959. assert result[-1].tz is timezone.utc
  960. def test_dti_union_mixed(self):
  961. # GH 21671
  962. rng = DatetimeIndex([Timestamp("2011-01-01"), pd.NaT])
  963. rng2 = DatetimeIndex(["2012-01-01", "2012-01-02"], tz="Asia/Tokyo")
  964. result = rng.union(rng2)
  965. expected = Index(
  966. [
  967. Timestamp("2011-01-01"),
  968. pd.NaT,
  969. Timestamp("2012-01-01", tz="Asia/Tokyo"),
  970. Timestamp("2012-01-02", tz="Asia/Tokyo"),
  971. ],
  972. dtype=object,
  973. )
  974. tm.assert_index_equal(result, expected)
  975. @pytest.mark.parametrize(
  976. "tz", [None, "UTC", "US/Central", dateutil.tz.tzoffset(None, -28800)]
  977. )
  978. def test_iteration_preserves_nanoseconds(self, tz):
  979. # GH 19603
  980. index = DatetimeIndex(
  981. ["2018-02-08 15:00:00.168456358", "2018-02-08 15:00:00.168456359"], tz=tz
  982. )
  983. for i, ts in enumerate(index):
  984. assert ts == index[i] # pylint: disable=unnecessary-list-index-lookup
  985. def test_tz_localize_invalidates_freq():
  986. # we only preserve freq in unambiguous cases
  987. # if localized to US/Eastern, this crosses a DST transition
  988. dti = date_range("2014-03-08 23:00", "2014-03-09 09:00", freq="H")
  989. assert dti.freq == "H"
  990. result = dti.tz_localize(None) # no-op
  991. assert result.freq == "H"
  992. result = dti.tz_localize("UTC") # unambiguous freq preservation
  993. assert result.freq == "H"
  994. result = dti.tz_localize("US/Eastern", nonexistent="shift_forward")
  995. assert result.freq is None
  996. assert result.inferred_freq is None # i.e. we are not _too_ strict here
  997. # Case where we _can_ keep freq because we're length==1
  998. dti2 = dti[:1]
  999. result = dti2.tz_localize("US/Eastern")
  1000. assert result.freq == "H"