From 7b66182b934462ade870350adc95a62095f2243f Mon Sep 17 00:00:00 2001 From: Matt Roeschke Date: Tue, 8 May 2018 23:42:13 -0700 Subject: [PATCH 1/2] BUG: date_range linspace respects tz --- doc/source/whatsnew/v0.23.0.txt | 2 +- pandas/core/indexes/datetimes.py | 9 ++++++--- pandas/tests/indexes/datetimes/test_date_range.py | 12 ++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/doc/source/whatsnew/v0.23.0.txt b/doc/source/whatsnew/v0.23.0.txt index c6991bc016868..6f5c180c587bd 100644 --- a/doc/source/whatsnew/v0.23.0.txt +++ b/doc/source/whatsnew/v0.23.0.txt @@ -525,7 +525,7 @@ Other Enhancements library. (:issue:`20564`) - Added new writer for exporting Stata dta files in version 117, ``StataWriter117``. This format supports exporting strings with lengths up to 2,000,000 characters (:issue:`16450`) - :func:`to_hdf` and :func:`read_hdf` now accept an ``errors`` keyword argument to control encoding error handling (:issue:`20835`) -- :func:`date_range` now returns a linearly spaced ``DatetimeIndex`` if ``start``, ``stop``, and ``periods`` are specified, but ``freq`` is not. (:issue:`20808`) +- :func:`date_range` now returns a linearly spaced ``DatetimeIndex`` if ``start``, ``stop``, and ``periods`` are specified, but ``freq`` is not. (:issue:`20808`, :issue:`20983`) .. _whatsnew_0230.api_breaking: diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 1b5aa3b45f3b5..6b8b216dc35c2 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -587,10 +587,13 @@ def _generate(cls, start, end, periods, name, freq, if end is not None: end = end.tz_localize(tz).asm8 else: + # Create a linearly spaced date_range. + start = start.tz_localize(tz) + end = end.tz_localize(tz) index = tools.to_datetime(np.linspace(start.value, - end.value, periods)) - if tz is not None: - index = index.tz_localize('UTC').tz_convert(tz) + end.value, periods), + utc=True) + index = index.tz_convert(tz) if not left_closed and len(index) and index[0] == start: index = index[1:] diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index bbe9cb65eb1a9..e2565538d80f4 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -179,6 +179,18 @@ def test_date_range_convenience_periods(self): '2018-04-01 03:00:00+10:00', '2018-04-01 04:00:00+10:00'], freq=None) + @pytest.mark.parametrize('start,end', [ + ['20180101', '20180103'], + [datetime(2018, 1, 1), datetime(2018, 1, 3)], + [Timestamp('20180101'), Timestamp('20180103')], + [Timestamp('20180101', tz='US/Eastern'), + Timestamp('20180103', tz='US/Eastern')]]) + def test_date_range_linspacing_tz(self, start, end): + # GH 20983 + result = date_range(start, end, periods=3, tz='US/Eastern') + expected = date_range('20180101', periods=3, freq='D', tz='US/Eastern') + tm.assert_index_equal(result, expected) + def test_date_range_businesshour(self): idx = DatetimeIndex(['2014-07-04 09:00', '2014-07-04 10:00', '2014-07-04 11:00', From 66a9d76f7eaa89e5296d0c77ad55ae790c901938 Mon Sep 17 00:00:00 2001 From: Matt Roeschke Date: Wed, 9 May 2018 19:19:56 -0700 Subject: [PATCH 2/2] Address comments --- pandas/core/indexes/datetimes.py | 2 +- .../indexes/datetimes/test_date_range.py | 45 +++++++++++-------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py index 6b8b216dc35c2..1d5c2d9a098ed 100644 --- a/pandas/core/indexes/datetimes.py +++ b/pandas/core/indexes/datetimes.py @@ -587,7 +587,7 @@ def _generate(cls, start, end, periods, name, freq, if end is not None: end = end.tz_localize(tz).asm8 else: - # Create a linearly spaced date_range. + # Create a linearly spaced date_range in local time start = start.tz_localize(tz) end = end.tz_localize(tz) index = tools.to_datetime(np.linspace(start.value, diff --git a/pandas/tests/indexes/datetimes/test_date_range.py b/pandas/tests/indexes/datetimes/test_date_range.py index e2565538d80f4..3fb088329f225 100644 --- a/pandas/tests/indexes/datetimes/test_date_range.py +++ b/pandas/tests/indexes/datetimes/test_date_range.py @@ -164,30 +164,37 @@ def test_date_range_ambiguous_arguments(self): def test_date_range_convenience_periods(self): # GH 20808 - rng = date_range('2018-04-24', '2018-04-27', periods=3) - exp = DatetimeIndex(['2018-04-24 00:00:00', '2018-04-25 12:00:00', - '2018-04-27 00:00:00'], freq=None) + result = date_range('2018-04-24', '2018-04-27', periods=3) + expected = DatetimeIndex(['2018-04-24 00:00:00', + '2018-04-25 12:00:00', + '2018-04-27 00:00:00'], freq=None) - tm.assert_index_equal(rng, exp) + tm.assert_index_equal(result, expected) # Test if spacing remains linear if tz changes to dst in range - rng = date_range('2018-04-01 01:00:00', '2018-04-01 04:00:00', - tz='Australia/Sydney', periods=3) - exp = DatetimeIndex(['2018-04-01 01:00:00+11:00', - '2018-04-01 02:00:00+11:00', - '2018-04-01 02:00:00+10:00', - '2018-04-01 03:00:00+10:00', - '2018-04-01 04:00:00+10:00'], freq=None) - - @pytest.mark.parametrize('start,end', [ - ['20180101', '20180103'], - [datetime(2018, 1, 1), datetime(2018, 1, 3)], - [Timestamp('20180101'), Timestamp('20180103')], + result = date_range('2018-04-01 01:00:00', + '2018-04-01 04:00:00', + tz='Australia/Sydney', + periods=3) + expected = DatetimeIndex([Timestamp('2018-04-01 01:00:00+1100', + tz='Australia/Sydney'), + Timestamp('2018-04-01 02:00:00+1000', + tz='Australia/Sydney'), + Timestamp('2018-04-01 04:00:00+1000', + tz='Australia/Sydney')]) + tm.assert_index_equal(result, expected) + + @pytest.mark.parametrize('start,end,result_tz', [ + ['20180101', '20180103', 'US/Eastern'], + [datetime(2018, 1, 1), datetime(2018, 1, 3), 'US/Eastern'], + [Timestamp('20180101'), Timestamp('20180103'), 'US/Eastern'], + [Timestamp('20180101', tz='US/Eastern'), + Timestamp('20180103', tz='US/Eastern'), 'US/Eastern'], [Timestamp('20180101', tz='US/Eastern'), - Timestamp('20180103', tz='US/Eastern')]]) - def test_date_range_linspacing_tz(self, start, end): + Timestamp('20180103', tz='US/Eastern'), None]]) + def test_date_range_linspacing_tz(self, start, end, result_tz): # GH 20983 - result = date_range(start, end, periods=3, tz='US/Eastern') + result = date_range(start, end, periods=3, tz=result_tz) expected = date_range('20180101', periods=3, freq='D', tz='US/Eastern') tm.assert_index_equal(result, expected)