Repository: pandas-dev/pandas

Issue:
BUG: df.plot() with FixedOffset timezones
#### Code Sample, a copy-pastable example if possible

The following example will trigger an exception:

```python
import pandas as pd
import datetime

data = []
now = datetime.datetime.now(tz=datetime.timezone.utc)
for i in range(10):
    data.append(dict(date=now + datetime.timedelta(days=i), data=i))

df = pd.DataFrame(data, index=[x['date'] for x in data])

df.plot()
```

This should fail with the error:

```python
pandas/_libs/tslib.pyx in pandas._libs.tslib.dates_normalized (pandas/_libs/tslib.c:88136)()

AttributeError: 'datetime.timezone' object has no attribute '_transition_info'
```

#### Problem description

The "else" branch in this code will fail on instances of datetime.timezone which don't have a `_transition_info` internal field, which is true with objects like `datetime.timezone.utc`.

The code on master looks like this:

```python
    else:
        trans, deltas, typ = _get_dst_info(tz)

        for i in range(n):
            # Adjust datetime64 timestamp, recompute datetimestruct
            pos = trans.searchsorted(stamps[i]) - 1
            inf = tz._transition_info[pos]

            pandas_datetime_to_datetimestruct(stamps[i] + deltas[pos],
                                              PANDAS_FR_ns, &dts)
            if (dts.hour + dts.min + dts.sec + dts.us) > 0:
                return False
```

The problem is the setting of `inf` and its reference to `_transition_info`, but if I'm reading this code correctly, this is dead code anyway.  With that line removed, this code should work with things like `datetime.timezone.utc` again.

#### Expected Output

I'd expect to be able to plot off timestamps which use datetime.timezone.utc as their timezone.

#### Output of ``pd.show_versions()``

<details>
>>> pd.show_versions()

INSTALLED VERSIONS
------------------
commit: None
python: 3.6.1.final.0
python-bits: 64
OS: Linux
OS-release: 4.10.0-27-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.20.2
pytest: None
pip: 9.0.1
setuptools: 27.2.0
Cython: None
numpy: 1.13.0
scipy: 0.19.1
xarray: None
IPython: 6.1.0
sphinx: None
patsy: None
dateutil: 2.6.0
pytz: 2017.2
blosc: None
bottleneck: None
tables: None
numexpr: None
feather: None
matplotlib: 2.0.2
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: 0.999
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.9.6
s3fs: None
pandas_gbq: None
pandas_datareader: None
</details>



Discussion:
can u show an example where this actually fails
Example added above
this is a pretty new API IIRC. We don't support this, only ``pytz`` and ``dateutil`` for timezones. xref to https://github.com/pandas-dev/pandas/issues/15986.

If someone wants to add support (and to be honest, its mostly testing), go for it.
This now raises:

```
ValueError: view limit minimum -36851.5436251915 is less than 1 and is an invalid Matplotlib date value. This often happens if you pass a non-datetime value to an axis that has datetime units
```
Actually this is more of an issue with FixedOffset timezones now.

```
In [10]: t = pytz.FixedOffset(30)

In [11]: t
Out[11]: pytz.FixedOffset(30)

In [12]: df.index = df.index.tz_convert(t)

In [13]: df['date'] = df['date'].dt.tz_convert(t)

In [14]: df
Out[14]:
                                  data                             date
2019-02-03 18:21:03.191561+00:30     0 2019-02-03 18:21:03.191561+00:30
2019-02-04 18:21:03.191561+00:30     1 2019-02-04 18:21:03.191561+00:30
2019-02-05 18:21:03.191561+00:30     2 2019-02-05 18:21:03.191561+00:30
2019-02-06 18:21:03.191561+00:30     3 2019-02-06 18:21:03.191561+00:30
2019-02-07 18:21:03.191561+00:30     4 2019-02-07 18:21:03.191561+00:30
2019-02-08 18:21:03.191561+00:30     5 2019-02-08 18:21:03.191561+00:30
2019-02-09 18:21:03.191561+00:30     6 2019-02-09 18:21:03.191561+00:30
2019-02-10 18:21:03.191561+00:30     7 2019-02-10 18:21:03.191561+00:30
2019-02-11 18:21:03.191561+00:30     8 2019-02-11 18:21:03.191561+00:30
2019-02-12 18:21:03.191561+00:30     9 2019-02-12 18:21:03.191561+00:30

In [17]: df.plot()
libc++abi.dylib: terminating with uncaught exception of type NSException
Abort trap: 6
```
I don't think the following lines are needed since we handle datetime.timezone instances now. 

```
diff --git a/pandas/plotting/_matplotlib/converter.py b/pandas/plotting/_matplotlib/converter.py
index b20dd3212..15648d59c 100644
--- a/pandas/plotting/_matplotlib/converter.py
+++ b/pandas/plotting/_matplotlib/converter.py
@@ -324,9 +324,6 @@ class DatetimeConverter(dates.DateConverter):
 class PandasAutoDateFormatter(dates.AutoDateFormatter):
     def __init__(self, locator, tz=None, defaultfmt="%Y-%m-%d"):
         dates.AutoDateFormatter.__init__(self, locator, tz, defaultfmt)
-        # matplotlib.dates._UTC has no _utcoffset called by pandas
-        if self._tz is dates.UTC:
-            self._tz._utcoffset = self._tz.utcoffset(None)
```

Resolving patch:
diff --git a/pandas/core/series.py b/pandas/core/series.py
--- a/pandas/core/series.py
+++ b/pandas/core/series.py
@@ -1236,6 +1236,10 @@ def setitem(key, value):
 
     def _set_with_engine(self, key, value):
         values = self._values
+        if is_extension_array_dtype(values.dtype):
+            # The cython indexing engine does not support ExtensionArrays.
+            values[self.index.get_loc(key)] = value
+            return
         try:
             self.index._engine.set_value(values, key, value)
             return
diff --git a/pandas/plotting/_matplotlib/converter.py b/pandas/plotting/_matplotlib/converter.py
--- a/pandas/plotting/_matplotlib/converter.py
+++ b/pandas/plotting/_matplotlib/converter.py
@@ -324,9 +324,6 @@ def axisinfo(unit, axis):
 class PandasAutoDateFormatter(dates.AutoDateFormatter):
     def __init__(self, locator, tz=None, defaultfmt="%Y-%m-%d"):
         dates.AutoDateFormatter.__init__(self, locator, tz, defaultfmt)
-        # matplotlib.dates._UTC has no _utcoffset called by pandas
-        if self._tz is dates.UTC:
-            self._tz._utcoffset = self._tz.utcoffset(None)
 
 
 class PandasAutoDateLocator(dates.AutoDateLocator):


Test patch:
diff --git a/pandas/tests/plotting/test_datetimelike.py b/pandas/tests/plotting/test_datetimelike.py
--- a/pandas/tests/plotting/test_datetimelike.py
+++ b/pandas/tests/plotting/test_datetimelike.py
@@ -45,9 +45,10 @@ def teardown_method(self, method):
         tm.close()
 
     @pytest.mark.slow
-    def test_ts_plot_with_tz(self):
-        # GH2877
-        index = date_range("1/1/2011", periods=2, freq="H", tz="Europe/Brussels")
+    def test_ts_plot_with_tz(self, tz_aware_fixture):
+        # GH2877, GH17173
+        tz = tz_aware_fixture
+        index = date_range("1/1/2011", periods=2, freq="H", tz=tz)
         ts = Series([188.5, 328.25], index=index)
         _check_plot_works(ts.plot)
 
diff --git a/pandas/tests/reductions/test_reductions.py b/pandas/tests/reductions/test_reductions.py
--- a/pandas/tests/reductions/test_reductions.py
+++ b/pandas/tests/reductions/test_reductions.py
@@ -159,6 +159,15 @@ def test_same_tz_min_max_axis_1(self, op, expected_col):
         expected = df[expected_col].rename(None)
         tm.assert_series_equal(result, expected)
 
+    @pytest.mark.parametrize("func", ["maximum", "minimum"])
+    def test_numpy_reduction_with_tz_aware_dtype(self, tz_aware_fixture, func):
+        # GH 15552
+        tz = tz_aware_fixture
+        arg = pd.to_datetime(["2019"]).tz_localize(tz)
+        expected = Series(arg)
+        result = getattr(np, func)(expected, expected)
+        tm.assert_series_equal(result, expected)
+
 
 class TestIndexReductions:
     # Note: the name TestIndexReductions indicates these tests
diff --git a/pandas/tests/series/indexing/test_datetime.py b/pandas/tests/series/indexing/test_datetime.py
--- a/pandas/tests/series/indexing/test_datetime.py
+++ b/pandas/tests/series/indexing/test_datetime.py
@@ -765,3 +765,14 @@ def test_round_nat(method, freq):
     expected = Series(pd.NaT)
     round_method = getattr(s.dt, method)
     assert_series_equal(round_method(freq), expected)
+
+
+def test_setitem_tuple_with_datetimetz():
+    # GH 20441
+    arr = date_range("2017", periods=4, tz="US/Eastern")
+    index = [(0, 1), (0, 2), (0, 3), (0, 4)]
+    result = Series(arr, index=index)
+    expected = result.copy()
+    result[(0, 1)] = np.nan
+    expected.iloc[0] = np.nan
+    assert_series_equal(result, expected)