diff --git a/doc/source/whatsnew/v0.19.1.txt b/doc/source/whatsnew/v0.19.1.txt index 3edb8c1fa9071..0f7edc7a99abc 100644 --- a/doc/source/whatsnew/v0.19.1.txt +++ b/doc/source/whatsnew/v0.19.1.txt @@ -35,7 +35,7 @@ Bug Fixes - +- Bug in localizing an ambiguous timezone when a boolean is passed (:issue:`14402`) diff --git a/pandas/tseries/tests/test_timezones.py b/pandas/tseries/tests/test_timezones.py index a85a606075911..c7e4f03fcd792 100644 --- a/pandas/tseries/tests/test_timezones.py +++ b/pandas/tseries/tests/test_timezones.py @@ -17,7 +17,8 @@ from pytz import NonExistentTimeError import pandas.util.testing as tm -from pandas.util.testing import assert_frame_equal, set_timezone +from pandas.util.testing import (assert_frame_equal, assert_series_equal, + set_timezone) from pandas.compat import lrange, zip try: @@ -535,6 +536,44 @@ def test_ambiguous_nat(self): # right is datetime64[ns, tzfile('/usr/share/zoneinfo/US/Eastern')] self.assert_numpy_array_equal(di_test.values, localized.values) + def test_ambiguous_bool(self): + # make sure that we are correctly accepting bool values as ambiguous + + # gh-14402 + t = Timestamp('2015-11-01 01:00:03') + expected0 = Timestamp('2015-11-01 01:00:03-0500', tz='US/Central') + expected1 = Timestamp('2015-11-01 01:00:03-0600', tz='US/Central') + + def f(): + t.tz_localize('US/Central') + self.assertRaises(pytz.AmbiguousTimeError, f) + + result = t.tz_localize('US/Central', ambiguous=True) + self.assertEqual(result, expected0) + + result = t.tz_localize('US/Central', ambiguous=False) + self.assertEqual(result, expected1) + + s = Series([t]) + expected0 = Series([expected0]) + expected1 = Series([expected1]) + + def f(): + s.dt.tz_localize('US/Central') + self.assertRaises(pytz.AmbiguousTimeError, f) + + result = s.dt.tz_localize('US/Central', ambiguous=True) + assert_series_equal(result, expected0) + + result = s.dt.tz_localize('US/Central', ambiguous=[True]) + assert_series_equal(result, expected0) + + result = s.dt.tz_localize('US/Central', ambiguous=False) + assert_series_equal(result, expected1) + + result = s.dt.tz_localize('US/Central', ambiguous=[False]) + assert_series_equal(result, expected1) + def test_nonexistent_raise_coerce(self): # See issue 13057 from pytz.exceptions import NonExistentTimeError @@ -629,14 +668,14 @@ def test_localized_at_time_between_time(self): result = ts_local.at_time(time(10, 0)) expected = ts.at_time(time(10, 0)).tz_localize(self.tzstr( 'US/Eastern')) - tm.assert_series_equal(result, expected) + assert_series_equal(result, expected) self.assertTrue(self.cmptz(result.index.tz, self.tz('US/Eastern'))) t1, t2 = time(10, 0), time(11, 0) result = ts_local.between_time(t1, t2) expected = ts.between_time(t1, t2).tz_localize(self.tzstr('US/Eastern')) - tm.assert_series_equal(result, expected) + assert_series_equal(result, expected) self.assertTrue(self.cmptz(result.index.tz, self.tz('US/Eastern'))) def test_string_index_alias_tz_aware(self): @@ -723,7 +762,7 @@ def test_frame_no_datetime64_dtype(self): result = df.get_dtype_counts().sort_index() expected = Series({'datetime64[ns]': 2, str(tz_expected): 2}).sort_index() - tm.assert_series_equal(result, expected) + assert_series_equal(result, expected) def test_hongkong_tz_convert(self): # #1673 @@ -1324,7 +1363,7 @@ def test_append_aware(self): exp_index = DatetimeIndex(['2011-01-01 01:00', '2011-01-01 02:00'], tz='US/Eastern') exp = Series([1, 2], index=exp_index) - self.assert_series_equal(ts_result, exp) + assert_series_equal(ts_result, exp) self.assertEqual(ts_result.index.tz, rng1.tz) rng1 = date_range('1/1/2011 01:00', periods=1, freq='H', tz='UTC') @@ -1336,7 +1375,7 @@ def test_append_aware(self): exp_index = DatetimeIndex(['2011-01-01 01:00', '2011-01-01 02:00'], tz='UTC') exp = Series([1, 2], index=exp_index) - self.assert_series_equal(ts_result, exp) + assert_series_equal(ts_result, exp) utc = rng1.tz self.assertEqual(utc, ts_result.index.tz) @@ -1352,7 +1391,7 @@ def test_append_aware(self): exp_index = Index([Timestamp('1/1/2011 01:00', tz='US/Eastern'), Timestamp('1/1/2011 02:00', tz='US/Central')]) exp = Series([1, 2], index=exp_index) - self.assert_series_equal(ts_result, exp) + assert_series_equal(ts_result, exp) def test_append_dst(self): rng1 = date_range('1/1/2016 01:00', periods=3, freq='H', @@ -1368,7 +1407,7 @@ def test_append_dst(self): '2016-08-01 02:00', '2016-08-01 03:00'], tz='US/Eastern') exp = Series([1, 2, 3, 10, 11, 12], index=exp_index) - tm.assert_series_equal(ts_result, exp) + assert_series_equal(ts_result, exp) self.assertEqual(ts_result.index.tz, rng1.tz) def test_append_aware_naive(self): @@ -1429,7 +1468,7 @@ def test_arith_utc_convert(self): expected = uts1 + uts2 self.assertEqual(result.index.tz, pytz.UTC) - tm.assert_series_equal(result, expected) + assert_series_equal(result, expected) def test_intersection(self): rng = date_range('1/1/2011', periods=100, freq='H', tz='utc') diff --git a/pandas/tslib.pyx b/pandas/tslib.pyx index 9073ad0abd535..bab45595cd60f 100644 --- a/pandas/tslib.pyx +++ b/pandas/tslib.pyx @@ -4155,6 +4155,7 @@ def tz_localize_to_utc(ndarray[int64_t] vals, object tz, object ambiguous=None, """ cdef: ndarray[int64_t] trans, deltas, idx_shifted + ndarray ambiguous_array Py_ssize_t i, idx, pos, ntrans, n = len(vals) int64_t *tdata int64_t v, left, right @@ -4190,11 +4191,18 @@ def tz_localize_to_utc(ndarray[int64_t] vals, object tz, object ambiguous=None, infer_dst = True elif ambiguous == 'NaT': fill = True + elif isinstance(ambiguous, bool): + is_dst = True + if ambiguous: + ambiguous_array = np.ones(len(vals), dtype=bool) + else: + ambiguous_array = np.zeros(len(vals), dtype=bool) elif hasattr(ambiguous, '__iter__'): is_dst = True if len(ambiguous) != len(vals): raise ValueError( "Length of ambiguous bool-array must be the same size as vals") + ambiguous_array = np.asarray(ambiguous) trans, deltas, typ = _get_dst_info(tz) @@ -4286,7 +4294,7 @@ def tz_localize_to_utc(ndarray[int64_t] vals, object tz, object ambiguous=None, if infer_dst and dst_hours[i] != NPY_NAT: result[i] = dst_hours[i] elif is_dst: - if ambiguous[i]: + if ambiguous_array[i]: result[i] = left else: result[i] = right