# -*- coding: utf-8 -*-
"""
Tests for DatetimeIndex timezone-related methods
"""
from datetime import datetime, timedelta, tzinfo, date, time
from distutils.version import LooseVersion

import pytest
import pytz
import dateutil
from dateutil.tz import gettz, tzlocal
import numpy as np

import pandas.util.testing as tm
import pandas.util._test_decorators as td

import pandas as pd
from pandas._libs import tslib
from pandas._libs.tslibs import timezones
from pandas.compat import lrange, zip, PY3
from pandas import (DatetimeIndex, date_range, bdate_range,
                    Timestamp, isna, to_datetime, Index)


class FixedOffset(tzinfo):
    """Fixed offset in minutes east from UTC."""

    def __init__(self, offset, name):
        self.__offset = timedelta(minutes=offset)
        self.__name = name

    def utcoffset(self, dt):
        return self.__offset

    def tzname(self, dt):
        return self.__name

    def dst(self, dt):
        return timedelta(0)


fixed_off = FixedOffset(-420, '-07:00')
fixed_off_no_name = FixedOffset(-330, None)


class TestDatetimeIndexTimezones(object):
    # -------------------------------------------------------------
    # DatetimeIndex.tz_convert
    def test_tz_convert_nat(self):
        # GH#5546
        dates = [pd.NaT]
        idx = DatetimeIndex(dates)
        idx = idx.tz_localize('US/Pacific')
        tm.assert_index_equal(idx, DatetimeIndex(dates, tz='US/Pacific'))
        idx = idx.tz_convert('US/Eastern')
        tm.assert_index_equal(idx, DatetimeIndex(dates, tz='US/Eastern'))
        idx = idx.tz_convert('UTC')
        tm.assert_index_equal(idx, DatetimeIndex(dates, tz='UTC'))

        dates = ['2010-12-01 00:00', '2010-12-02 00:00', pd.NaT]
        idx = DatetimeIndex(dates)
        idx = idx.tz_localize('US/Pacific')
        tm.assert_index_equal(idx, DatetimeIndex(dates, tz='US/Pacific'))
        idx = idx.tz_convert('US/Eastern')
        expected = ['2010-12-01 03:00', '2010-12-02 03:00', pd.NaT]
        tm.assert_index_equal(idx, DatetimeIndex(expected, tz='US/Eastern'))

        idx = idx + pd.offsets.Hour(5)
        expected = ['2010-12-01 08:00', '2010-12-02 08:00', pd.NaT]
        tm.assert_index_equal(idx, DatetimeIndex(expected, tz='US/Eastern'))
        idx = idx.tz_convert('US/Pacific')
        expected = ['2010-12-01 05:00', '2010-12-02 05:00', pd.NaT]
        tm.assert_index_equal(idx, DatetimeIndex(expected, tz='US/Pacific'))

        idx = idx + np.timedelta64(3, 'h')
        expected = ['2010-12-01 08:00', '2010-12-02 08:00', pd.NaT]
        tm.assert_index_equal(idx, DatetimeIndex(expected, tz='US/Pacific'))

        idx = idx.tz_convert('US/Eastern')
        expected = ['2010-12-01 11:00', '2010-12-02 11:00', pd.NaT]
        tm.assert_index_equal(idx, DatetimeIndex(expected, tz='US/Eastern'))

    @pytest.mark.parametrize('prefix', ['', 'dateutil/'])
    def test_dti_tz_convert_compat_timestamp(self, prefix):
        strdates = ['1/1/2012', '3/1/2012', '4/1/2012']
        idx = DatetimeIndex(strdates, tz=prefix + 'US/Eastern')

        conv = idx[0].tz_convert(prefix + 'US/Pacific')
        expected = idx.tz_convert(prefix + 'US/Pacific')[0]

        assert conv == expected

    def test_dti_tz_convert_hour_overflow_dst(self):
        # Regression test for:
        # https://github.com/pandas-dev/pandas/issues/13306

        # sorted case US/Eastern -> UTC
        ts = ['2008-05-12 09:50:00',
              '2008-12-12 09:50:35',
              '2009-05-12 09:50:32']
        tt = DatetimeIndex(ts).tz_localize('US/Eastern')
        ut = tt.tz_convert('UTC')
        expected = Index([13, 14, 13])
        tm.assert_index_equal(ut.hour, expected)

        # sorted case UTC -> US/Eastern
        ts = ['2008-05-12 13:50:00',
              '2008-12-12 14:50:35',
              '2009-05-12 13:50:32']
        tt = DatetimeIndex(ts).tz_localize('UTC')
        ut = tt.tz_convert('US/Eastern')
        expected = Index([9, 9, 9])
        tm.assert_index_equal(ut.hour, expected)

        # unsorted case US/Eastern -> UTC
        ts = ['2008-05-12 09:50:00',
              '2008-12-12 09:50:35',
              '2008-05-12 09:50:32']
        tt = DatetimeIndex(ts).tz_localize('US/Eastern')
        ut = tt.tz_convert('UTC')
        expected = Index([13, 14, 13])
        tm.assert_index_equal(ut.hour, expected)

        # unsorted case UTC -> US/Eastern
        ts = ['2008-05-12 13:50:00',
              '2008-12-12 14:50:35',
              '2008-05-12 13:50:32']
        tt = DatetimeIndex(ts).tz_localize('UTC')
        ut = tt.tz_convert('US/Eastern')
        expected = Index([9, 9, 9])
        tm.assert_index_equal(ut.hour, expected)

    @pytest.mark.parametrize('tz', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_dti_tz_convert_hour_overflow_dst_timestamps(self, tz):
        # Regression test for GH#13306

        # sorted case US/Eastern -> UTC
        ts = [Timestamp('2008-05-12 09:50:00', tz=tz),
              Timestamp('2008-12-12 09:50:35', tz=tz),
              Timestamp('2009-05-12 09:50:32', tz=tz)]
        tt = DatetimeIndex(ts)
        ut = tt.tz_convert('UTC')
        expected = Index([13, 14, 13])
        tm.assert_index_equal(ut.hour, expected)

        # sorted case UTC -> US/Eastern
        ts = [Timestamp('2008-05-12 13:50:00', tz='UTC'),
              Timestamp('2008-12-12 14:50:35', tz='UTC'),
              Timestamp('2009-05-12 13:50:32', tz='UTC')]
        tt = DatetimeIndex(ts)
        ut = tt.tz_convert('US/Eastern')
        expected = Index([9, 9, 9])
        tm.assert_index_equal(ut.hour, expected)

        # unsorted case US/Eastern -> UTC
        ts = [Timestamp('2008-05-12 09:50:00', tz=tz),
              Timestamp('2008-12-12 09:50:35', tz=tz),
              Timestamp('2008-05-12 09:50:32', tz=tz)]
        tt = DatetimeIndex(ts)
        ut = tt.tz_convert('UTC')
        expected = Index([13, 14, 13])
        tm.assert_index_equal(ut.hour, expected)

        # unsorted case UTC -> US/Eastern
        ts = [Timestamp('2008-05-12 13:50:00', tz='UTC'),
              Timestamp('2008-12-12 14:50:35', tz='UTC'),
              Timestamp('2008-05-12 13:50:32', tz='UTC')]
        tt = DatetimeIndex(ts)
        ut = tt.tz_convert('US/Eastern')
        expected = Index([9, 9, 9])
        tm.assert_index_equal(ut.hour, expected)

    @pytest.mark.parametrize('freq, n', [('H', 1), ('T', 60), ('S', 3600)])
    def test_dti_tz_convert_trans_pos_plus_1__bug(self, freq, n):
        # Regression test for tslib.tz_convert(vals, tz1, tz2).
        # See https://github.com/pandas-dev/pandas/issues/4496 for details.
        idx = date_range(datetime(2011, 3, 26, 23),
                         datetime(2011, 3, 27, 1), freq=freq)
        idx = idx.tz_localize('UTC')
        idx = idx.tz_convert('Europe/Moscow')

        expected = np.repeat(np.array([3, 4, 5]), np.array([n, n, 1]))
        tm.assert_index_equal(idx.hour, Index(expected))

    def test_dti_tz_convert_dst(self):
        for freq, n in [('H', 1), ('T', 60), ('S', 3600)]:
            # Start DST
            idx = date_range('2014-03-08 23:00', '2014-03-09 09:00', freq=freq,
                             tz='UTC')
            idx = idx.tz_convert('US/Eastern')
            expected = np.repeat(np.array([18, 19, 20, 21, 22, 23,
                                           0, 1, 3, 4, 5]),
                                 np.array([n, n, n, n, n, n, n, n, n, n, 1]))
            tm.assert_index_equal(idx.hour, Index(expected))

            idx = date_range('2014-03-08 18:00', '2014-03-09 05:00', freq=freq,
                             tz='US/Eastern')
            idx = idx.tz_convert('UTC')
            expected = np.repeat(np.array([23, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
                                 np.array([n, n, n, n, n, n, n, n, n, n, 1]))
            tm.assert_index_equal(idx.hour, Index(expected))

            # End DST
            idx = date_range('2014-11-01 23:00', '2014-11-02 09:00', freq=freq,
                             tz='UTC')
            idx = idx.tz_convert('US/Eastern')
            expected = np.repeat(np.array([19, 20, 21, 22, 23,
                                           0, 1, 1, 2, 3, 4]),
                                 np.array([n, n, n, n, n, n, n, n, n, n, 1]))
            tm.assert_index_equal(idx.hour, Index(expected))

            idx = date_range('2014-11-01 18:00', '2014-11-02 05:00', freq=freq,
                             tz='US/Eastern')
            idx = idx.tz_convert('UTC')
            expected = np.repeat(np.array([22, 23, 0, 1, 2, 3, 4, 5, 6,
                                           7, 8, 9, 10]),
                                 np.array([n, n, n, n, n, n, n, n, n,
                                           n, n, n, 1]))
            tm.assert_index_equal(idx.hour, Index(expected))

        # daily
        # Start DST
        idx = date_range('2014-03-08 00:00', '2014-03-09 00:00', freq='D',
                         tz='UTC')
        idx = idx.tz_convert('US/Eastern')
        tm.assert_index_equal(idx.hour, Index([19, 19]))

        idx = date_range('2014-03-08 00:00', '2014-03-09 00:00', freq='D',
                         tz='US/Eastern')
        idx = idx.tz_convert('UTC')
        tm.assert_index_equal(idx.hour, Index([5, 5]))

        # End DST
        idx = date_range('2014-11-01 00:00', '2014-11-02 00:00', freq='D',
                         tz='UTC')
        idx = idx.tz_convert('US/Eastern')
        tm.assert_index_equal(idx.hour, Index([20, 20]))

        idx = date_range('2014-11-01 00:00', '2014-11-02 000:00', freq='D',
                         tz='US/Eastern')
        idx = idx.tz_convert('UTC')
        tm.assert_index_equal(idx.hour, Index([4, 4]))

    def test_tz_convert_roundtrip(self, tz_aware_fixture):
        tz = tz_aware_fixture
        idx1 = date_range(start='2014-01-01', end='2014-12-31', freq='M',
                          tz='UTC')
        exp1 = date_range(start='2014-01-01', end='2014-12-31', freq='M')

        idx2 = date_range(start='2014-01-01', end='2014-12-31', freq='D',
                          tz='UTC')
        exp2 = date_range(start='2014-01-01', end='2014-12-31', freq='D')

        idx3 = date_range(start='2014-01-01', end='2014-03-01', freq='H',
                          tz='UTC')
        exp3 = date_range(start='2014-01-01', end='2014-03-01', freq='H')

        idx4 = date_range(start='2014-08-01', end='2014-10-31', freq='T',
                          tz='UTC')
        exp4 = date_range(start='2014-08-01', end='2014-10-31', freq='T')

        for idx, expected in [(idx1, exp1), (idx2, exp2), (idx3, exp3),
                              (idx4, exp4)]:
            converted = idx.tz_convert(tz)
            reset = converted.tz_convert(None)
            tm.assert_index_equal(reset, expected)
            assert reset.tzinfo is None
            expected = converted.tz_convert('UTC').tz_localize(None)
            tm.assert_index_equal(reset, expected)

    def test_dti_tz_convert_tzlocal(self):
        # GH#13583
        # tz_convert doesn't affect to internal
        dti = date_range(start='2001-01-01', end='2001-03-01', tz='UTC')
        dti2 = dti.tz_convert(dateutil.tz.tzlocal())
        tm.assert_numpy_array_equal(dti2.asi8, dti.asi8)

        dti = date_range(start='2001-01-01', end='2001-03-01',
                         tz=dateutil.tz.tzlocal())
        dti2 = dti.tz_convert(None)
        tm.assert_numpy_array_equal(dti2.asi8, dti.asi8)

    @pytest.mark.parametrize('tz', ['US/Eastern', 'dateutil/US/Eastern',
                                    pytz.timezone('US/Eastern'),
                                    gettz('US/Eastern')])
    def test_dti_tz_convert_utc_to_local_no_modify(self, tz):
        rng = date_range('3/11/2012', '3/12/2012', freq='H', tz='utc')
        rng_eastern = rng.tz_convert(tz)

        # Values are unmodified
        tm.assert_numpy_array_equal(rng.asi8, rng_eastern.asi8)

        assert timezones.tz_compare(rng_eastern.tz, timezones.maybe_get_tz(tz))

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_tz_convert_unsorted(self, tzstr):
        dr = date_range('2012-03-09', freq='H', periods=100, tz='utc')
        dr = dr.tz_convert(tzstr)

        result = dr[::-1].hour
        exp = dr.hour[::-1]
        tm.assert_almost_equal(result, exp)

    # -------------------------------------------------------------
    # DatetimeIndex.tz_localize

    def test_dti_tz_localize_nonexistent_raise_coerce(self):
        # GH#13057
        times = ['2015-03-08 01:00', '2015-03-08 02:00', '2015-03-08 03:00']
        index = DatetimeIndex(times)
        tz = 'US/Eastern'
        with pytest.raises(pytz.NonExistentTimeError):
            index.tz_localize(tz=tz)

        with pytest.raises(pytz.NonExistentTimeError):
            index.tz_localize(tz=tz, errors='raise')

        result = index.tz_localize(tz=tz, errors='coerce')
        test_times = ['2015-03-08 01:00-05:00', 'NaT',
                      '2015-03-08 03:00-04:00']
        dti = DatetimeIndex(test_times)
        expected = dti.tz_localize('UTC').tz_convert('US/Eastern')
        tm.assert_index_equal(result, expected)

    @pytest.mark.parametrize('tz', [pytz.timezone('US/Eastern'),
                                    gettz('US/Eastern')])
    def test_dti_tz_localize_ambiguous_infer(self, tz):
        # November 6, 2011, fall back, repeat 2 AM hour
        # With no repeated hours, we cannot infer the transition
        dr = date_range(datetime(2011, 11, 6, 0), periods=5,
                        freq=pd.offsets.Hour())
        with pytest.raises(pytz.AmbiguousTimeError):
            dr.tz_localize(tz)

        # With repeated hours, we can infer the transition
        dr = date_range(datetime(2011, 11, 6, 0), periods=5,
                        freq=pd.offsets.Hour(), tz=tz)
        times = ['11/06/2011 00:00', '11/06/2011 01:00', '11/06/2011 01:00',
                 '11/06/2011 02:00', '11/06/2011 03:00']
        di = DatetimeIndex(times)
        localized = di.tz_localize(tz, ambiguous='infer')
        tm.assert_index_equal(dr, localized)
        tm.assert_index_equal(dr, DatetimeIndex(times, tz=tz,
                                                ambiguous='infer'))

        # When there is no dst transition, nothing special happens
        dr = date_range(datetime(2011, 6, 1, 0), periods=10,
                        freq=pd.offsets.Hour())
        localized = dr.tz_localize(tz)
        localized_infer = dr.tz_localize(tz, ambiguous='infer')
        tm.assert_index_equal(localized, localized_infer)

    @pytest.mark.parametrize('tz', [pytz.timezone('US/Eastern'),
                                    gettz('US/Eastern')])
    def test_dti_tz_localize_ambiguous_times(self, tz):
        # March 13, 2011, spring forward, skip from 2 AM to 3 AM
        dr = date_range(datetime(2011, 3, 13, 1, 30), periods=3,
                        freq=pd.offsets.Hour())
        with pytest.raises(pytz.NonExistentTimeError):
            dr.tz_localize(tz)

        # after dst transition, it works
        dr = date_range(datetime(2011, 3, 13, 3, 30), periods=3,
                        freq=pd.offsets.Hour(), tz=tz)

        # November 6, 2011, fall back, repeat 2 AM hour
        dr = date_range(datetime(2011, 11, 6, 1, 30), periods=3,
                        freq=pd.offsets.Hour())
        with pytest.raises(pytz.AmbiguousTimeError):
            dr.tz_localize(tz)

        # UTC is OK
        dr = date_range(datetime(2011, 3, 13), periods=48,
                        freq=pd.offsets.Minute(30), tz=pytz.utc)

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_dti_tz_localize_pass_dates_to_utc(self, tzstr):
        strdates = ['1/1/2012', '3/1/2012', '4/1/2012']

        idx = DatetimeIndex(strdates)
        conv = idx.tz_localize(tzstr)

        fromdates = DatetimeIndex(strdates, tz=tzstr)

        assert conv.tz == fromdates.tz
        tm.assert_numpy_array_equal(conv.values, fromdates.values)

    @pytest.mark.parametrize('prefix', ['', 'dateutil/'])
    def test_dti_tz_localize(self, prefix):
        tzstr = prefix + 'US/Eastern'
        dti = DatetimeIndex(start='1/1/2005', end='1/1/2005 0:00:30.256',
                            freq='L')
        dti2 = dti.tz_localize(tzstr)

        dti_utc = DatetimeIndex(start='1/1/2005 05:00',
                                end='1/1/2005 5:00:30.256', freq='L', tz='utc')

        tm.assert_numpy_array_equal(dti2.values, dti_utc.values)

        dti3 = dti2.tz_convert(prefix + 'US/Pacific')
        tm.assert_numpy_array_equal(dti3.values, dti_utc.values)

        dti = DatetimeIndex(start='11/6/2011 1:59', end='11/6/2011 2:00',
                            freq='L')
        with pytest.raises(pytz.AmbiguousTimeError):
            dti.tz_localize(tzstr)

        dti = DatetimeIndex(start='3/13/2011 1:59', end='3/13/2011 2:00',
                            freq='L')
        with pytest.raises(pytz.NonExistentTimeError):
            dti.tz_localize(tzstr)

    @pytest.mark.parametrize('tz', ['US/Eastern', 'dateutil/US/Eastern',
                                    pytz.timezone('US/Eastern'),
                                    gettz('US/Eastern')])
    def test_dti_tz_localize_utc_conversion(self, tz):
        # Localizing to time zone should:
        #  1) check for DST ambiguities
        #  2) convert to UTC

        rng = date_range('3/10/2012', '3/11/2012', freq='30T')

        converted = rng.tz_localize(tz)
        expected_naive = rng + pd.offsets.Hour(5)
        tm.assert_numpy_array_equal(converted.asi8, expected_naive.asi8)

        # DST ambiguity, this should fail
        rng = date_range('3/11/2012', '3/12/2012', freq='30T')
        # Is this really how it should fail??
        with pytest.raises(pytz.NonExistentTimeError):
            rng.tz_localize(tz)

    def test_dti_tz_localize_roundtrip(self, tz_aware_fixture):
        tz = tz_aware_fixture

        idx1 = date_range(start='2014-01-01', end='2014-12-31', freq='M')
        idx2 = date_range(start='2014-01-01', end='2014-12-31', freq='D')
        idx3 = date_range(start='2014-01-01', end='2014-03-01', freq='H')
        idx4 = date_range(start='2014-08-01', end='2014-10-31', freq='T')
        for idx in [idx1, idx2, idx3, idx4]:
            localized = idx.tz_localize(tz)
            expected = date_range(start=idx[0], end=idx[-1], freq=idx.freq,
                                  tz=tz)
            tm.assert_index_equal(localized, expected)
            with pytest.raises(TypeError):
                localized.tz_localize(tz)

            reset = localized.tz_localize(None)
            tm.assert_index_equal(reset, idx)
            assert reset.tzinfo is None

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

        conv = rng.tz_localize('US/Pacific')
        exp = date_range('1/1/2011', periods=100, freq='H', tz='US/Pacific')

        tm.assert_index_equal(conv, exp)

    def test_dti_tz_localize_tzlocal(self):
        # GH#13583
        offset = dateutil.tz.tzlocal().utcoffset(datetime(2011, 1, 1))
        offset = int(offset.total_seconds() * 1000000000)

        dti = date_range(start='2001-01-01', end='2001-03-01')
        dti2 = dti.tz_localize(dateutil.tz.tzlocal())
        tm.assert_numpy_array_equal(dti2.asi8 + offset, dti.asi8)

        dti = date_range(start='2001-01-01', end='2001-03-01',
                         tz=dateutil.tz.tzlocal())
        dti2 = dti.tz_localize(None)
        tm.assert_numpy_array_equal(dti2.asi8 - offset, dti.asi8)

    @pytest.mark.parametrize('tz', [pytz.timezone('US/Eastern'),
                                    gettz('US/Eastern')])
    def test_dti_tz_localize_ambiguous_nat(self, tz):
        times = ['11/06/2011 00:00', '11/06/2011 01:00', '11/06/2011 01:00',
                 '11/06/2011 02:00', '11/06/2011 03:00']
        di = DatetimeIndex(times)
        localized = di.tz_localize(tz, ambiguous='NaT')

        times = ['11/06/2011 00:00', np.NaN, np.NaN, '11/06/2011 02:00',
                 '11/06/2011 03:00']
        di_test = DatetimeIndex(times, tz='US/Eastern')

        # left dtype is datetime64[ns, US/Eastern]
        # right is datetime64[ns, tzfile('/usr/share/zoneinfo/US/Eastern')]
        tm.assert_numpy_array_equal(di_test.values, localized.values)

    @pytest.mark.parametrize('tz', [pytz.timezone('US/Eastern'),
                                    gettz('US/Eastern')])
    def test_dti_tz_localize_ambiguous_flags(self, tz):
        # November 6, 2011, fall back, repeat 2 AM hour

        # Pass in flags to determine right dst transition
        dr = date_range(datetime(2011, 11, 6, 0), periods=5,
                        freq=pd.offsets.Hour(), tz=tz)
        times = ['11/06/2011 00:00', '11/06/2011 01:00', '11/06/2011 01:00',
                 '11/06/2011 02:00', '11/06/2011 03:00']

        # Test tz_localize
        di = DatetimeIndex(times)
        is_dst = [1, 1, 0, 0, 0]
        localized = di.tz_localize(tz, ambiguous=is_dst)
        tm.assert_index_equal(dr, localized)
        tm.assert_index_equal(dr, DatetimeIndex(times, tz=tz,
                                                ambiguous=is_dst))

        localized = di.tz_localize(tz, ambiguous=np.array(is_dst))
        tm.assert_index_equal(dr, localized)

        localized = di.tz_localize(tz,
                                   ambiguous=np.array(is_dst).astype('bool'))
        tm.assert_index_equal(dr, localized)

        # Test constructor
        localized = DatetimeIndex(times, tz=tz, ambiguous=is_dst)
        tm.assert_index_equal(dr, localized)

        # Test duplicate times where inferring the dst fails
        times += times
        di = DatetimeIndex(times)

        # When the sizes are incompatible, make sure error is raised
        with pytest.raises(Exception):
            di.tz_localize(tz, ambiguous=is_dst)

        # When sizes are compatible and there are repeats ('infer' won't work)
        is_dst = np.hstack((is_dst, is_dst))
        localized = di.tz_localize(tz, ambiguous=is_dst)
        dr = dr.append(dr)
        tm.assert_index_equal(dr, localized)

        # When there is no dst transition, nothing special happens
        dr = date_range(datetime(2011, 6, 1, 0), periods=10,
                        freq=pd.offsets.Hour())
        is_dst = np.array([1] * 10)
        localized = dr.tz_localize(tz)
        localized_is_dst = dr.tz_localize(tz, ambiguous=is_dst)
        tm.assert_index_equal(localized, localized_is_dst)

    # TODO: belongs outside tz_localize tests?
    @pytest.mark.parametrize('tz', ['Europe/London', 'dateutil/Europe/London'])
    def test_dti_construction_ambiguous_endpoint(self, tz):
        # construction with an ambiguous end-point
        # GH#11626

        # FIXME: This next block fails to raise; it was taken from an older
        # version of this test that had an indention mistake that caused it
        # to not get executed.
        # with pytest.raises(pytz.AmbiguousTimeError):
        #    date_range("2013-10-26 23:00", "2013-10-27 01:00",
        #               tz="Europe/London", freq="H")

        times = date_range("2013-10-26 23:00", "2013-10-27 01:00", freq="H",
                           tz=tz, ambiguous='infer')
        assert times[0] == Timestamp('2013-10-26 23:00', tz=tz, freq="H")

        if str(tz).startswith('dateutil'):
            if LooseVersion(dateutil.__version__) < LooseVersion('2.6.0'):
                # see GH#14621
                assert times[-1] == Timestamp('2013-10-27 01:00:00+0000',
                                              tz=tz, freq="H")
            elif LooseVersion(dateutil.__version__) > LooseVersion('2.6.0'):
                # fixed ambiguous behavior
                assert times[-1] == Timestamp('2013-10-27 01:00:00+0100',
                                              tz=tz, freq="H")
        else:
            assert times[-1] == Timestamp('2013-10-27 01:00:00+0000',
                                          tz=tz, freq="H")

    def test_dti_tz_localize_bdate_range(self):
        dr = pd.bdate_range('1/1/2009', '1/1/2010')
        dr_utc = pd.bdate_range('1/1/2009', '1/1/2010', tz=pytz.utc)
        localized = dr.tz_localize(pytz.utc)
        tm.assert_index_equal(dr_utc, localized)

    # -------------------------------------------------------------
    # DatetimeIndex.normalize

    def test_normalize_tz(self):
        rng = date_range('1/1/2000 9:30', periods=10, freq='D',
                         tz='US/Eastern')

        result = rng.normalize()
        expected = date_range('1/1/2000', periods=10, freq='D',
                              tz='US/Eastern')
        tm.assert_index_equal(result, expected)

        assert result.is_normalized
        assert not rng.is_normalized

        rng = date_range('1/1/2000 9:30', periods=10, freq='D', tz='UTC')

        result = rng.normalize()
        expected = date_range('1/1/2000', periods=10, freq='D', tz='UTC')
        tm.assert_index_equal(result, expected)

        assert result.is_normalized
        assert not rng.is_normalized

        rng = date_range('1/1/2000 9:30', periods=10, freq='D', tz=tzlocal())
        result = rng.normalize()
        expected = date_range('1/1/2000', periods=10, freq='D', tz=tzlocal())
        tm.assert_index_equal(result, expected)

        assert result.is_normalized
        assert not rng.is_normalized

    @td.skip_if_windows
    @pytest.mark.parametrize('timezone', ['US/Pacific', 'US/Eastern', 'UTC',
                                          'Asia/Kolkata', 'Asia/Shanghai',
                                          'Australia/Canberra'])
    def test_normalize_tz_local(self, timezone):
        # GH#13459
        with tm.set_timezone(timezone):
            rng = date_range('1/1/2000 9:30', periods=10, freq='D',
                             tz=tzlocal())

            result = rng.normalize()
            expected = date_range('1/1/2000', periods=10, freq='D',
                                  tz=tzlocal())
            tm.assert_index_equal(result, expected)

            assert result.is_normalized
            assert not rng.is_normalized

    # ------------------------------------------------------------
    # DatetimeIndex.__new__

    @pytest.mark.parametrize('prefix', ['', 'dateutil/'])
    def test_dti_constructor_static_tzinfo(self, prefix):
        # it works!
        index = DatetimeIndex([datetime(2012, 1, 1)], tz=prefix + 'EST')
        index.hour
        index[0]

    def test_dti_constructor_with_fixed_tz(self):
        off = FixedOffset(420, '+07:00')
        start = datetime(2012, 3, 11, 5, 0, 0, tzinfo=off)
        end = datetime(2012, 6, 11, 5, 0, 0, tzinfo=off)
        rng = date_range(start=start, end=end)
        assert off == rng.tz

        rng2 = date_range(start, periods=len(rng), tz=off)
        tm.assert_index_equal(rng, rng2)

        rng3 = date_range('3/11/2012 05:00:00+07:00',
                          '6/11/2012 05:00:00+07:00')
        assert (rng.values == rng3.values).all()

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_dti_convert_datetime_list(self, tzstr):
        dr = date_range('2012-06-02', periods=10,
                        tz=tzstr, name='foo')
        dr2 = DatetimeIndex(list(dr), name='foo')
        tm.assert_index_equal(dr, dr2)
        assert dr.tz == dr2.tz
        assert dr2.name == 'foo'

    def test_dti_construction_univalent(self):
        rng = date_range('03/12/2012 00:00', periods=10, freq='W-FRI',
                         tz='US/Eastern')
        rng2 = DatetimeIndex(data=rng, tz='US/Eastern')
        tm.assert_index_equal(rng, rng2)

    @pytest.mark.parametrize('tz', [pytz.timezone('US/Eastern'),
                                    gettz('US/Eastern')])
    def test_dti_from_tzaware_datetime(self, tz):
        d = [datetime(2012, 8, 19, tzinfo=tz)]

        index = DatetimeIndex(d)
        assert timezones.tz_compare(index.tz, tz)

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_dti_tz_constructors(self, tzstr):
        """ Test different DatetimeIndex constructions with timezone
        Follow-up of GH#4229
        """

        arr = ['11/10/2005 08:00:00', '11/10/2005 09:00:00']

        idx1 = to_datetime(arr).tz_localize(tzstr)
        idx2 = DatetimeIndex(start="2005-11-10 08:00:00", freq='H', periods=2,
                             tz=tzstr)
        idx3 = DatetimeIndex(arr, tz=tzstr)
        idx4 = DatetimeIndex(np.array(arr), tz=tzstr)

        for other in [idx2, idx3, idx4]:
            tm.assert_index_equal(idx1, other)

    # -------------------------------------------------------------
    # Unsorted

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

        left = rng.tz_convert('US/Eastern')
        right = rng.tz_convert('Europe/Berlin')

        result = left.join(left[:-5], how=join_type)
        assert isinstance(result, DatetimeIndex)
        assert result.tz == left.tz

        result = left.join(right[:-5], how=join_type)
        assert isinstance(result, DatetimeIndex)
        assert result.tz.zone == 'UTC'

    @pytest.mark.parametrize("dtype", [
        None, 'datetime64[ns, CET]',
        'datetime64[ns, EST]', 'datetime64[ns, UTC]'
    ])
    def test_date_accessor(self, dtype):
        # Regression test for GH#21230
        expected = np.array([date(2018, 6, 4), pd.NaT])

        index = DatetimeIndex(['2018-06-04 10:00:00', pd.NaT], dtype=dtype)
        result = index.date

        tm.assert_numpy_array_equal(result, expected)

    @pytest.mark.parametrize("dtype", [
        None, 'datetime64[ns, CET]',
        'datetime64[ns, EST]', 'datetime64[ns, UTC]'
    ])
    def test_time_accessor(self, dtype):
        # Regression test for GH#21267
        expected = np.array([time(10, 20, 30), pd.NaT])

        index = DatetimeIndex(['2018-06-04 10:20:30', pd.NaT], dtype=dtype)
        result = index.time

        tm.assert_numpy_array_equal(result, expected)

    def test_dti_drop_dont_lose_tz(self):
        # GH#2621
        ind = date_range("2012-12-01", periods=10, tz="utc")
        ind = ind.drop(ind[-1])

        assert ind.tz is not None

    def test_date_range_localize(self):
        rng = date_range('3/11/2012 03:00', periods=15, freq='H',
                         tz='US/Eastern')
        rng2 = DatetimeIndex(['3/11/2012 03:00', '3/11/2012 04:00'],
                             tz='US/Eastern')
        rng3 = date_range('3/11/2012 03:00', periods=15, freq='H')
        rng3 = rng3.tz_localize('US/Eastern')

        tm.assert_index_equal(rng, rng3)

        # DST transition time
        val = rng[0]
        exp = Timestamp('3/11/2012 03:00', tz='US/Eastern')

        assert val.hour == 3
        assert exp.hour == 3
        assert val == exp  # same UTC value
        tm.assert_index_equal(rng[:2], rng2)

        # Right before the DST transition
        rng = date_range('3/11/2012 00:00', periods=2, freq='H',
                         tz='US/Eastern')
        rng2 = DatetimeIndex(['3/11/2012 00:00', '3/11/2012 01:00'],
                             tz='US/Eastern')
        tm.assert_index_equal(rng, rng2)
        exp = Timestamp('3/11/2012 00:00', tz='US/Eastern')
        assert exp.hour == 0
        assert rng[0] == exp
        exp = Timestamp('3/11/2012 01:00', tz='US/Eastern')
        assert exp.hour == 1
        assert rng[1] == exp

        rng = date_range('3/11/2012 00:00', periods=10, freq='H',
                         tz='US/Eastern')
        assert rng[2].hour == 3

    def test_timestamp_equality_different_timezones(self):
        utc_range = date_range('1/1/2000', periods=20, tz='UTC')
        eastern_range = utc_range.tz_convert('US/Eastern')
        berlin_range = utc_range.tz_convert('Europe/Berlin')

        for a, b, c in zip(utc_range, eastern_range, berlin_range):
            assert a == b
            assert b == c
            assert a == c

        assert (utc_range == eastern_range).all()
        assert (utc_range == berlin_range).all()
        assert (berlin_range == eastern_range).all()

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

        left = rng[10:90][::-1]
        right = rng[20:80][::-1]

        assert left.tz == rng.tz
        result = left.intersection(right)
        assert result.tz == left.tz

    def test_dti_equals_with_tz(self):
        left = date_range('1/1/2011', periods=100, freq='H', tz='utc')
        right = date_range('1/1/2011', periods=100, freq='H', tz='US/Eastern')

        assert not left.equals(right)

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_dti_tz_nat(self, tzstr):
        idx = DatetimeIndex([Timestamp("2013-1-1", tz=tzstr), pd.NaT])

        assert isna(idx[1])
        assert idx[0].tzinfo is not None

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_dti_astype_asobject_tzinfos(self, tzstr):
        # GH#1345

        # dates around a dst transition
        rng = date_range('2/13/2010', '5/6/2010', tz=tzstr)

        objs = rng.astype(object)
        for i, x in enumerate(objs):
            exval = rng[i]
            assert x == exval
            assert x.tzinfo == exval.tzinfo

        objs = rng.astype(object)
        for i, x in enumerate(objs):
            exval = rng[i]
            assert x == exval
            assert x.tzinfo == exval.tzinfo

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_dti_with_timezone_repr(self, tzstr):
        rng = date_range('4/13/2010', '5/6/2010')

        rng_eastern = rng.tz_localize(tzstr)

        rng_repr = repr(rng_eastern)
        assert '2010-04-13 00:00:00' in rng_repr

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_dti_take_dont_lose_meta(self, tzstr):
        rng = date_range('1/1/2000', periods=20, tz=tzstr)

        result = rng.take(lrange(5))
        assert result.tz == rng.tz
        assert result.freq == rng.freq

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_utc_box_timestamp_and_localize(self, tzstr):
        tz = timezones.maybe_get_tz(tzstr)

        rng = date_range('3/11/2012', '3/12/2012', freq='H', tz='utc')
        rng_eastern = rng.tz_convert(tzstr)

        expected = rng[-1].astimezone(tz)

        stamp = rng_eastern[-1]
        assert stamp == expected
        assert stamp.tzinfo == expected.tzinfo

        # right tzinfo
        rng = date_range('3/13/2012', '3/14/2012', freq='H', tz='utc')
        rng_eastern = rng.tz_convert(tzstr)
        # test not valid for dateutil timezones.
        # assert 'EDT' in repr(rng_eastern[0].tzinfo)
        assert ('EDT' in repr(rng_eastern[0].tzinfo) or
                'tzfile' in repr(rng_eastern[0].tzinfo))

    def test_dti_to_pydatetime(self):
        dt = dateutil.parser.parse('2012-06-13T01:39:00Z')
        dt = dt.replace(tzinfo=tzlocal())

        arr = np.array([dt], dtype=object)

        result = to_datetime(arr, utc=True)
        assert result.tz is pytz.utc

        rng = date_range('2012-11-03 03:00', '2012-11-05 03:00', tz=tzlocal())
        arr = rng.to_pydatetime()
        result = to_datetime(arr, utc=True)
        assert result.tz is pytz.utc

    def test_dti_to_pydatetime_fizedtz(self):
        dates = np.array([datetime(2000, 1, 1, tzinfo=fixed_off),
                          datetime(2000, 1, 2, tzinfo=fixed_off),
                          datetime(2000, 1, 3, tzinfo=fixed_off)])
        dti = DatetimeIndex(dates)

        result = dti.to_pydatetime()
        tm.assert_numpy_array_equal(dates, result)

        result = dti._mpl_repr()
        tm.assert_numpy_array_equal(dates, result)

    @pytest.mark.parametrize('tz', [pytz.timezone('US/Central'),
                                    gettz('US/Central')])
    def test_with_tz(self, tz):
        # just want it to work
        start = datetime(2011, 3, 12, tzinfo=pytz.utc)
        dr = bdate_range(start, periods=50, freq=pd.offsets.Hour())
        assert dr.tz is pytz.utc

        # DateRange with naive datetimes
        dr = bdate_range('1/1/2005', '1/1/2009', tz=pytz.utc)
        dr = bdate_range('1/1/2005', '1/1/2009', tz=tz)

        # normalized
        central = dr.tz_convert(tz)
        assert central.tz is tz
        naive = central[0].to_pydatetime().replace(tzinfo=None)
        comp = tslib._localize_pydatetime(naive, tz).tzinfo
        assert central[0].tz is comp

        # compare vs a localized tz
        naive = dr[0].to_pydatetime().replace(tzinfo=None)
        comp = tslib._localize_pydatetime(naive, tz).tzinfo
        assert central[0].tz is comp

        # datetimes with tzinfo set
        dr = bdate_range(datetime(2005, 1, 1, tzinfo=pytz.utc),
                         datetime(2009, 1, 1, tzinfo=pytz.utc))
        with pytest.raises(Exception):
            bdate_range(datetime(2005, 1, 1, tzinfo=pytz.utc), '1/1/2009',
                        tz=tz)

    @pytest.mark.parametrize('prefix', ['', 'dateutil/'])
    def test_field_access_localize(self, prefix):
        strdates = ['1/1/2012', '3/1/2012', '4/1/2012']
        rng = DatetimeIndex(strdates, tz=prefix + 'US/Eastern')
        assert (rng.hour == 0).all()

        # a more unusual time zone, #1946
        dr = date_range('2011-10-02 00:00', freq='h', periods=10,
                        tz=prefix + 'America/Atikokan')

        expected = Index(np.arange(10, dtype=np.int64))
        tm.assert_index_equal(dr.hour, expected)

    @pytest.mark.parametrize('tz', [pytz.timezone('US/Eastern'),
                                    gettz('US/Eastern')])
    def test_dti_convert_tz_aware_datetime_datetime(self, tz):
        # GH#1581
        dates = [datetime(2000, 1, 1), datetime(2000, 1, 2),
                 datetime(2000, 1, 3)]

        dates_aware = [tslib._localize_pydatetime(x, tz) for x in dates]
        result = DatetimeIndex(dates_aware)
        assert timezones.tz_compare(result.tz, tz)

        converted = to_datetime(dates_aware, utc=True)
        ex_vals = np.array([Timestamp(x).value for x in dates_aware])
        tm.assert_numpy_array_equal(converted.asi8, ex_vals)
        assert converted.tz is pytz.utc

    def test_dti_union_aware(self):
        # non-overlapping
        rng = date_range("2012-11-15 00:00:00", periods=6, freq="H",
                         tz="US/Central")

        rng2 = date_range("2012-11-15 12:00:00", periods=6, freq="H",
                          tz="US/Eastern")

        result = rng.union(rng2)
        assert result.tz.zone == 'UTC'

    @pytest.mark.parametrize('tz', [None, 'UTC', "US/Central",
                                    dateutil.tz.tzoffset(None, -28800)])
    @pytest.mark.usefixtures("datetime_tz_utc")
    @pytest.mark.skipif(not PY3, reason="datetime.timezone not in PY2")
    def test_iteration_preserves_nanoseconds(self, tz):
        # GH 19603
        index = DatetimeIndex(["2018-02-08 15:00:00.168456358",
                               "2018-02-08 15:00:00.168456359"], tz=tz)
        for i, ts in enumerate(index):
            assert ts == index[i]


class TestDateRange(object):
    """Tests for date_range with timezones"""
    def test_hongkong_tz_convert(self):
        # GH#1673 smoke test
        dr = date_range('2012-01-01', '2012-01-10', freq='D', tz='Hongkong')

        # it works!
        dr.hour

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_date_range_span_dst_transition(self, tzstr):
        # GH#1778

        # Standard -> Daylight Savings Time
        dr = date_range('03/06/2012 00:00', periods=200, freq='W-FRI',
                        tz='US/Eastern')

        assert (dr.hour == 0).all()

        dr = date_range('2012-11-02', periods=10, tz=tzstr)
        assert (dr.hour == 0).all()

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_date_range_timezone_str_argument(self, tzstr):
        tz = timezones.maybe_get_tz(tzstr)
        result = date_range('1/1/2000', periods=10, tz=tzstr)
        expected = date_range('1/1/2000', periods=10, tz=tz)

        tm.assert_index_equal(result, expected)

    def test_date_range_with_fixedoffset_noname(self):
        off = fixed_off_no_name
        start = datetime(2012, 3, 11, 5, 0, 0, tzinfo=off)
        end = datetime(2012, 6, 11, 5, 0, 0, tzinfo=off)
        rng = date_range(start=start, end=end)
        assert off == rng.tz

        idx = Index([start, end])
        assert off == idx.tz

    @pytest.mark.parametrize('tzstr', ['US/Eastern', 'dateutil/US/Eastern'])
    def test_date_range_with_tz(self, tzstr):
        stamp = Timestamp('3/11/2012 05:00', tz=tzstr)
        assert stamp.hour == 5

        rng = date_range('3/11/2012 04:00', periods=10, freq='H',
                         tz=tzstr)

        assert stamp == rng[1]


class TestToDatetime(object):
    """Tests for the to_datetime constructor with timezones"""
    def test_to_datetime_utc(self):
        arr = np.array([dateutil.parser.parse('2012-06-13T01:39:00Z')],
                       dtype=object)

        result = to_datetime(arr, utc=True)
        assert result.tz is pytz.utc

    def test_to_datetime_fixed_offset(self):
        dates = [datetime(2000, 1, 1, tzinfo=fixed_off),
                 datetime(2000, 1, 2, tzinfo=fixed_off),
                 datetime(2000, 1, 3, tzinfo=fixed_off)]
        result = to_datetime(dates)
        assert result.tz == fixed_off
