1from datetime import datetime
2
3import numpy as np
4import pytest
5from pytz import UTC
6
7from pandas._libs.tslibs import (
8    OutOfBoundsTimedelta,
9    conversion,
10    iNaT,
11    timezones,
12    tzconversion,
13)
14
15from pandas import Timestamp, date_range
16import pandas._testing as tm
17
18
19def _compare_utc_to_local(tz_didx):
20    def f(x):
21        return tzconversion.tz_convert_from_utc_single(x, tz_didx.tz)
22
23    result = tzconversion.tz_convert_from_utc(tz_didx.asi8, tz_didx.tz)
24    expected = np.vectorize(f)(tz_didx.asi8)
25
26    tm.assert_numpy_array_equal(result, expected)
27
28
29def _compare_local_to_utc(tz_didx, naive_didx):
30    # Check that tz_localize behaves the same vectorized and pointwise.
31    err1 = err2 = None
32    try:
33        result = tzconversion.tz_localize_to_utc(naive_didx.asi8, tz_didx.tz)
34        err1 = None
35    except Exception as err:
36        err1 = err
37
38    try:
39        expected = naive_didx.map(lambda x: x.tz_localize(tz_didx.tz)).asi8
40    except Exception as err:
41        err2 = err
42
43    if err1 is not None:
44        assert type(err1) == type(err2)
45    else:
46        assert err2 is None
47        tm.assert_numpy_array_equal(result, expected)
48
49
50def test_tz_convert_single_matches_tz_convert_hourly(tz_aware_fixture):
51    tz = tz_aware_fixture
52    tz_didx = date_range("2014-03-01", "2015-01-10", freq="H", tz=tz)
53    naive_didx = date_range("2014-03-01", "2015-01-10", freq="H")
54
55    _compare_utc_to_local(tz_didx)
56    _compare_local_to_utc(tz_didx, naive_didx)
57
58
59@pytest.mark.parametrize("freq", ["D", "A"])
60def test_tz_convert_single_matches_tz_convert(tz_aware_fixture, freq):
61    tz = tz_aware_fixture
62    tz_didx = date_range("2000-01-01", "2020-01-01", freq=freq, tz=tz)
63    naive_didx = date_range("2000-01-01", "2020-01-01", freq=freq)
64
65    _compare_utc_to_local(tz_didx)
66    _compare_local_to_utc(tz_didx, naive_didx)
67
68
69@pytest.mark.parametrize(
70    "arr",
71    [
72        pytest.param(np.array([], dtype=np.int64), id="empty"),
73        pytest.param(np.array([iNaT], dtype=np.int64), id="all_nat"),
74    ],
75)
76def test_tz_convert_corner(arr):
77    result = tzconversion.tz_convert_from_utc(arr, timezones.maybe_get_tz("Asia/Tokyo"))
78    tm.assert_numpy_array_equal(result, arr)
79
80
81def test_tz_convert_readonly():
82    # GH#35530
83    arr = np.array([0], dtype=np.int64)
84    arr.setflags(write=False)
85    result = tzconversion.tz_convert_from_utc(arr, UTC)
86    tm.assert_numpy_array_equal(result, arr)
87
88
89@pytest.mark.parametrize("copy", [True, False])
90@pytest.mark.parametrize("dtype", ["M8[ns]", "M8[s]"])
91def test_length_zero_copy(dtype, copy):
92    arr = np.array([], dtype=dtype)
93    result = conversion.ensure_datetime64ns(arr, copy=copy)
94    assert result.base is (None if copy else arr)
95
96
97def test_ensure_datetime64ns_bigendian():
98    # GH#29684
99    arr = np.array([np.datetime64(1, "ms")], dtype=">M8[ms]")
100    result = conversion.ensure_datetime64ns(arr)
101
102    expected = np.array([np.datetime64(1, "ms")], dtype="M8[ns]")
103    tm.assert_numpy_array_equal(result, expected)
104
105
106def test_ensure_timedelta64ns_overflows():
107    arr = np.arange(10).astype("m8[Y]") * 100
108    msg = r"Out of bounds for nanosecond timedelta64\[Y\] 900"
109    with pytest.raises(OutOfBoundsTimedelta, match=msg):
110        conversion.ensure_timedelta64ns(arr)
111
112
113class SubDatetime(datetime):
114    pass
115
116
117@pytest.mark.parametrize(
118    "dt, expected",
119    [
120        pytest.param(
121            Timestamp("2000-01-01"), Timestamp("2000-01-01", tz=UTC), id="timestamp"
122        ),
123        pytest.param(
124            datetime(2000, 1, 1), datetime(2000, 1, 1, tzinfo=UTC), id="datetime"
125        ),
126        pytest.param(
127            SubDatetime(2000, 1, 1),
128            SubDatetime(2000, 1, 1, tzinfo=UTC),
129            id="subclassed_datetime",
130        ),
131    ],
132)
133def test_localize_pydatetime_dt_types(dt, expected):
134    # GH 25851
135    # ensure that subclassed datetime works with
136    # localize_pydatetime
137    result = conversion.localize_pydatetime(dt, UTC)
138    assert result == expected
139