Repository: pandas-dev/pandas

Issue:
BUG: resample with tz-aware: Values falls after last bin
#### Code Sample, a copy-pastable example if possible

```python
import pandas as pd

index = pd.DatetimeIndex([1450137600000000000, 1474059600000000000], tz='UTC').tz_convert('America/Chicago')

print(index)

df = pd.DataFrame([1, 2], index=index)

print(df.resample('12h', closed='right', label='right').last().ffill())

```
#### Problem description

resampling is not handling non-UTC index properly due to daylight saving time change 

and the problem occurs in file https://github.com/pandas-dev/pandas/blob/master/pandas/tseries/resample.py
function: `_get_time_bins` 
code: `binner = labels = DatetimeIndex(freq=self.freq,...`

this problem can be solved by converting ax tz to UTC before the resampling and applying the original tz after DatetimeIndex is created

so the code will look like this



        tz = ax.tz
        ax = ax.tz_convert('UTC')
        if len(ax) == 0:
            binner = labels = DatetimeIndex(
                data=[], freq=self.freq, name=ax.name)
            return binner, [], labels

        first, last = ax.min(), ax.max()
        first, last = _get_range_edges(first, last, self.freq,
                                       closed=self.closed,
                                       base=self.base)
        # GH #12037
        # use first/last directly instead of call replace() on them
        # because replace() will swallow the nanosecond part
        # thus last bin maybe slightly before the end if the end contains
        # nanosecond part and lead to `Values falls after last bin` error
        binner = labels = DatetimeIndex(freq=self.freq,
                                        start=first,
                                        end=last,
                                        name=ax.name).tz_convert(tz)


this cause the bins to be always aligned by UTC times rather than original tz, but I think that it is adequate behaviour as well.



#### Expected Output
I expect the resampling to be successful regardless of the time range selected

#### Output of ``pd.show_versions()``

<details>
INSTALLED VERSIONS
------------------
commit: None
python: 3.6.0.final.0
python-bits: 64
OS: Windows
OS-release: 10
machine: AMD64
processor: Intel64 Family 6 Model 94 Stepping 3, GenuineIntel
byteorder: little
LC_ALL: None
LANG: None
LOCALE: None.None
pandas: 0.19.2
nose: 1.3.7
pip: 9.0.1
setuptools: 34.0.2
Cython: None
numpy: 1.12.0
scipy: 0.18.1
statsmodels: None
xarray: None
IPython: None
sphinx: None
patsy: None
dateutil: 2.6.0
pytz: 2016.10
blosc: None
bottleneck: None
tables: None
numexpr: None
matplotlib: 2.0.0
openpyxl: 2.4.1
xlrd: 1.0.0
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: None
httplib2: None
apiclient: None
sqlalchemy: None
pymysql: 0.7.9.None
psycopg2: None
jinja2: None
boto: None
pandas_datareader: None
</details>



Discussion:
I can create a pull request with the change I described if it looks ok
might be (tangentially) related to #12351  and #12037 .
though i suspect the tz is messing with this.

sure a PR to fix would be great.

Resolving patch:
diff --git a/pandas/core/resample.py b/pandas/core/resample.py
--- a/pandas/core/resample.py
+++ b/pandas/core/resample.py
@@ -1141,6 +1141,16 @@ def _get_time_bins(self, ax):
                                         tz=tz,
                                         name=ax.name)
 
+        # GH 15549
+        # In edge case of tz-aware resapmling binner last index can be
+        # less than the last variable in data object, this happens because of
+        # DST time change
+        if len(binner) > 1 and binner[-1] < last:
+            extra_date_range = pd.date_range(binner[-1], last + self.freq,
+                                             freq=self.freq, tz=tz,
+                                             name=ax.name)
+            binner = labels = binner.append(extra_date_range[1:])
+
         # a little hack
         trimmed = False
         if (len(binner) > 2 and binner[-2] == last and


Test patch:
diff --git a/pandas/tests/test_resample.py b/pandas/tests/test_resample.py
--- a/pandas/tests/test_resample.py
+++ b/pandas/tests/test_resample.py
@@ -2719,6 +2719,34 @@ def test_resample_weekly_bug_1726(self):
         # it works!
         df.resample('W-MON', closed='left', label='left').first()
 
+    def test_resample_with_dst_time_change(self):
+        # GH 15549
+        index = pd.DatetimeIndex([1457537600000000000, 1458059600000000000],
+                                 tz='UTC').tz_convert('America/Chicago')
+        df = pd.DataFrame([1, 2], index=index)
+        result = df.resample('12h', closed='right',
+                             label='right').last().ffill()
+
+        expected_index_values = ['2016-03-09 12:00:00-06:00',
+                                 '2016-03-10 00:00:00-06:00',
+                                 '2016-03-10 12:00:00-06:00',
+                                 '2016-03-11 00:00:00-06:00',
+                                 '2016-03-11 12:00:00-06:00',
+                                 '2016-03-12 00:00:00-06:00',
+                                 '2016-03-12 12:00:00-06:00',
+                                 '2016-03-13 00:00:00-06:00',
+                                 '2016-03-13 13:00:00-05:00',
+                                 '2016-03-14 01:00:00-05:00',
+                                 '2016-03-14 13:00:00-05:00',
+                                 '2016-03-15 01:00:00-05:00',
+                                 '2016-03-15 13:00:00-05:00']
+        index = pd.DatetimeIndex(expected_index_values,
+                                 tz='UTC').tz_convert('America/Chicago')
+        expected = pd.DataFrame([1.0, 1.0, 1.0, 1.0, 1.0,
+                                 1.0, 1.0, 1.0, 1.0, 1.0,
+                                 1.0, 1.0, 2.0], index=index)
+        assert_frame_equal(result, expected)
+
     def test_resample_bms_2752(self):
         # GH2753
         foo = Series(index=pd.bdate_range('20000101', '20000201'))