# -*- coding: utf-8 -*-
"""
Tests for Series timezone-related methods
"""
from datetime import datetime

from dateutil.tz import tzoffset
import numpy as np
import pytest
import pytz

from pandas._libs.tslibs import conversion, timezones
from pandas.compat import lrange

from pandas import DatetimeIndex, Index, NaT, Series, Timestamp
from pandas.core.indexes.datetimes import date_range
import pandas.util.testing as tm


class TestSeriesTimezones(object):
    # -----------------------------------------------------------------
    # Series.tz_localize
    def test_series_tz_localize(self):

        rng = date_range('1/1/2011', periods=100, freq='H')
        ts = Series(1, index=rng)

        result = ts.tz_localize('utc')
        assert result.index.tz.zone == 'UTC'

        # Can't localize if already tz-aware
        rng = date_range('1/1/2011', periods=100, freq='H', tz='utc')
        ts = Series(1, index=rng)

        with pytest.raises(TypeError, match='Already tz-aware'):
            ts.tz_localize('US/Eastern')

    @pytest.mark.filterwarnings('ignore::FutureWarning')
    def test_tz_localize_errors_deprecation(self):
        # GH 22644
        tz = 'Europe/Warsaw'
        n = 60
        rng = date_range(start='2015-03-29 02:00:00', periods=n, freq='min')
        ts = Series(rng)
        with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
            with pytest.raises(ValueError):
                ts.dt.tz_localize(tz, errors='foo')
            # make sure errors='coerce' gets mapped correctly to nonexistent
            result = ts.dt.tz_localize(tz, errors='coerce')
            expected = ts.dt.tz_localize(tz, nonexistent='NaT')
            tm.assert_series_equal(result, expected)

    def test_series_tz_localize_ambiguous_bool(self):
        # make sure that we are correctly accepting bool values as ambiguous

        # GH#14402
        ts = 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')

        ser = Series([ts])
        expected0 = Series([expected0])
        expected1 = Series([expected1])

        with pytest.raises(pytz.AmbiguousTimeError):
            ser.dt.tz_localize('US/Central')

        result = ser.dt.tz_localize('US/Central', ambiguous=True)
        tm.assert_series_equal(result, expected0)

        result = ser.dt.tz_localize('US/Central', ambiguous=[True])
        tm.assert_series_equal(result, expected0)

        result = ser.dt.tz_localize('US/Central', ambiguous=False)
        tm.assert_series_equal(result, expected1)

        result = ser.dt.tz_localize('US/Central', ambiguous=[False])
        tm.assert_series_equal(result, expected1)

    @pytest.mark.parametrize('tz', ['Europe/Warsaw', 'dateutil/Europe/Warsaw'])
    @pytest.mark.parametrize('method, exp', [
        ['shift_forward', '2015-03-29 03:00:00'],
        ['NaT', NaT],
        ['raise', None],
        ['foo', 'invalid']
    ])
    def test_series_tz_localize_nonexistent(self, tz, method, exp):
        # GH 8917
        n = 60
        dti = date_range(start='2015-03-29 02:00:00', periods=n, freq='min')
        s = Series(1, dti)
        if method == 'raise':
            with pytest.raises(pytz.NonExistentTimeError):
                s.tz_localize(tz, nonexistent=method)
        elif exp == 'invalid':
            with pytest.raises(ValueError):
                dti.tz_localize(tz, nonexistent=method)
        else:
            result = s.tz_localize(tz, nonexistent=method)
            expected = Series(1, index=DatetimeIndex([exp] * n, tz=tz))
            tm.assert_series_equal(result, expected)

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_series_tz_localize_empty(self, tzstr):
        # GH#2248
        ser = Series()

        ser2 = ser.tz_localize('utc')
        assert ser2.index.tz == pytz.utc

        ser2 = ser.tz_localize(tzstr)
        timezones.tz_compare(ser2.index.tz, timezones.maybe_get_tz(tzstr))

    # -----------------------------------------------------------------
    # Series.tz_convert

    def test_series_tz_convert(self):
        rng = date_range('1/1/2011', periods=200, freq='D', tz='US/Eastern')
        ts = Series(1, index=rng)

        result = ts.tz_convert('Europe/Berlin')
        assert result.index.tz.zone == 'Europe/Berlin'

        # can't convert tz-naive
        rng = date_range('1/1/2011', periods=200, freq='D')
        ts = Series(1, index=rng)

        with pytest.raises(TypeError, match="Cannot convert tz-naive"):
            ts.tz_convert('US/Eastern')

    def test_series_tz_convert_to_utc(self):
        base = DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03'],
                             tz='UTC')
        idx1 = base.tz_convert('Asia/Tokyo')[:2]
        idx2 = base.tz_convert('US/Eastern')[1:]

        res = Series([1, 2], index=idx1) + Series([1, 1], index=idx2)
        tm.assert_series_equal(res, Series([np.nan, 3, np.nan], index=base))

    # -----------------------------------------------------------------
    # Series.append

    def test_series_append_aware(self):
        rng1 = date_range('1/1/2011 01:00', periods=1, freq='H',
                          tz='US/Eastern')
        rng2 = date_range('1/1/2011 02:00', periods=1, freq='H',
                          tz='US/Eastern')
        ser1 = Series([1], index=rng1)
        ser2 = Series([2], index=rng2)
        ts_result = ser1.append(ser2)

        exp_index = DatetimeIndex(['2011-01-01 01:00', '2011-01-01 02:00'],
                                  tz='US/Eastern')
        exp = Series([1, 2], index=exp_index)
        tm.assert_series_equal(ts_result, exp)
        assert ts_result.index.tz == rng1.tz

        rng1 = date_range('1/1/2011 01:00', periods=1, freq='H', tz='UTC')
        rng2 = date_range('1/1/2011 02:00', periods=1, freq='H', tz='UTC')
        ser1 = Series([1], index=rng1)
        ser2 = Series([2], index=rng2)
        ts_result = ser1.append(ser2)

        exp_index = DatetimeIndex(['2011-01-01 01:00', '2011-01-01 02:00'],
                                  tz='UTC')
        exp = Series([1, 2], index=exp_index)
        tm.assert_series_equal(ts_result, exp)
        utc = rng1.tz
        assert utc == ts_result.index.tz

        # GH#7795
        # different tz coerces to object dtype, not UTC
        rng1 = date_range('1/1/2011 01:00', periods=1, freq='H',
                          tz='US/Eastern')
        rng2 = date_range('1/1/2011 02:00', periods=1, freq='H',
                          tz='US/Central')
        ser1 = Series([1], index=rng1)
        ser2 = Series([2], index=rng2)
        ts_result = ser1.append(ser2)
        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)
        tm.assert_series_equal(ts_result, exp)

    def test_series_append_aware_naive(self):
        rng1 = date_range('1/1/2011 01:00', periods=1, freq='H')
        rng2 = date_range('1/1/2011 02:00', periods=1, freq='H',
                          tz='US/Eastern')
        ser1 = Series(np.random.randn(len(rng1)), index=rng1)
        ser2 = Series(np.random.randn(len(rng2)), index=rng2)
        ts_result = ser1.append(ser2)

        expected = ser1.index.astype(object).append(ser2.index.astype(object))
        assert ts_result.index.equals(expected)

        # mixed
        rng1 = date_range('1/1/2011 01:00', periods=1, freq='H')
        rng2 = lrange(100)
        ser1 = Series(np.random.randn(len(rng1)), index=rng1)
        ser2 = Series(np.random.randn(len(rng2)), index=rng2)
        ts_result = ser1.append(ser2)

        expected = ser1.index.astype(object).append(ser2.index)
        assert ts_result.index.equals(expected)

    def test_series_append_dst(self):
        rng1 = date_range('1/1/2016 01:00', periods=3, freq='H',
                          tz='US/Eastern')
        rng2 = date_range('8/1/2016 01:00', periods=3, freq='H',
                          tz='US/Eastern')
        ser1 = Series([1, 2, 3], index=rng1)
        ser2 = Series([10, 11, 12], index=rng2)
        ts_result = ser1.append(ser2)

        exp_index = DatetimeIndex(['2016-01-01 01:00', '2016-01-01 02:00',
                                   '2016-01-01 03:00', '2016-08-01 01:00',
                                   '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 ts_result.index.tz == rng1.tz

    # -----------------------------------------------------------------

    def test_dateutil_tzoffset_support(self):
        values = [188.5, 328.25]
        tzinfo = tzoffset(None, 7200)
        index = [datetime(2012, 5, 11, 11, tzinfo=tzinfo),
                 datetime(2012, 5, 11, 12, tzinfo=tzinfo)]
        series = Series(data=values, index=index)

        assert series.index.tz == tzinfo

        # it works! #2443
        repr(series.index[0])

    @pytest.mark.parametrize('tz', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_tz_aware_asfreq(self, tz):
        dr = date_range('2011-12-01', '2012-07-20', freq='D', tz=tz)

        ser = Series(np.random.randn(len(dr)), index=dr)

        # it works!
        ser.asfreq('T')

    @pytest.mark.parametrize('tz', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_string_index_alias_tz_aware(self, tz):
        rng = date_range('1/1/2000', periods=10, tz=tz)
        ser = Series(np.random.randn(len(rng)), index=rng)

        result = ser['1/3/2000']
        tm.assert_almost_equal(result, ser[2])

    # TODO: De-duplicate with test below
    def test_series_add_tz_mismatch_converts_to_utc_duplicate(self):
        rng = date_range('1/1/2011', periods=10, freq='H', tz='US/Eastern')
        ser = Series(np.random.randn(len(rng)), index=rng)

        ts_moscow = ser.tz_convert('Europe/Moscow')

        result = ser + ts_moscow
        assert result.index.tz is pytz.utc

        result = ts_moscow + ser
        assert result.index.tz is pytz.utc

    def test_series_add_tz_mismatch_converts_to_utc(self):
        rng = date_range('1/1/2011', periods=100, freq='H', tz='utc')

        perm = np.random.permutation(100)[:90]
        ser1 = Series(np.random.randn(90),
                      index=rng.take(perm).tz_convert('US/Eastern'))

        perm = np.random.permutation(100)[:90]
        ser2 = Series(np.random.randn(90),
                      index=rng.take(perm).tz_convert('Europe/Berlin'))

        result = ser1 + ser2

        uts1 = ser1.tz_convert('utc')
        uts2 = ser2.tz_convert('utc')
        expected = uts1 + uts2

        assert result.index.tz == pytz.UTC
        tm.assert_series_equal(result, expected)

    def test_series_add_aware_naive_raises(self):
        rng = date_range('1/1/2011', periods=10, freq='H')
        ser = Series(np.random.randn(len(rng)), index=rng)

        ser_utc = ser.tz_localize('utc')

        with pytest.raises(Exception):
            ser + ser_utc

        with pytest.raises(Exception):
            ser_utc + ser

    def test_series_align_aware(self):
        idx1 = date_range('2001', periods=5, freq='H', tz='US/Eastern')
        ser = Series(np.random.randn(len(idx1)), index=idx1)
        ser_central = ser.tz_convert('US/Central')
        # # different timezones convert to UTC

        new1, new2 = ser.align(ser_central)
        assert new1.index.tz == pytz.UTC
        assert new2.index.tz == pytz.UTC

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_localized_at_time_between_time(self, tzstr):
        from datetime import time
        tz = timezones.maybe_get_tz(tzstr)

        rng = date_range('4/16/2012', '5/1/2012', freq='H')
        ts = Series(np.random.randn(len(rng)), index=rng)

        ts_local = ts.tz_localize(tzstr)

        result = ts_local.at_time(time(10, 0))
        expected = ts.at_time(time(10, 0)).tz_localize(tzstr)
        tm.assert_series_equal(result, expected)
        assert timezones.tz_compare(result.index.tz, tz)

        t1, t2 = time(10, 0), time(11, 0)
        result = ts_local.between_time(t1, t2)
        expected = ts.between_time(t1, t2).tz_localize(tzstr)
        tm.assert_series_equal(result, expected)
        assert timezones.tz_compare(result.index.tz, tz)

    @pytest.mark.parametrize('tzstr', ['Europe/Berlin',
                                       'dateutil/Europe/Berlin'])
    def test_getitem_pydatetime_tz(self, tzstr):
        tz = timezones.maybe_get_tz(tzstr)

        index = date_range(start='2012-12-24 16:00', end='2012-12-24 18:00',
                           freq='H', tz=tzstr)
        ts = Series(index=index, data=index.hour)
        time_pandas = Timestamp('2012-12-24 17:00', tz=tzstr)

        dt = datetime(2012, 12, 24, 17, 0)
        time_datetime = conversion.localize_pydatetime(dt, tz)
        assert ts[time_pandas] == ts[time_datetime]

    def test_series_truncate_datetimeindex_tz(self):
        # GH 9243
        idx = date_range('4/1/2005', '4/30/2005', freq='D', tz='US/Pacific')
        s = Series(range(len(idx)), index=idx)
        result = s.truncate(datetime(2005, 4, 2), datetime(2005, 4, 4))
        expected = Series([1, 2, 3], index=idx[1:4])
        tm.assert_series_equal(result, expected)

    @pytest.mark.parametrize('copy', [True, False])
    @pytest.mark.parametrize('method, tz', [
        ['tz_localize', None],
        ['tz_convert', 'Europe/Berlin']
    ])
    def test_tz_localize_convert_copy_inplace_mutate(self, copy, method, tz):
        # GH 6326
        result = Series(np.arange(0, 5),
                        index=date_range('20131027', periods=5, freq='1H',
                                         tz=tz))
        getattr(result, method)('UTC', copy=copy)
        expected = Series(np.arange(0, 5),
                          index=date_range('20131027', periods=5, freq='1H',
                                           tz=tz))
        tm.assert_series_equal(result, expected)
