Repository: pandas-dev/pandas

Issue:
Datetime.index.date returns incorrect date post upgrade to version 0.23
#### Code Sample, a copy-pastable example if possible

```python
df1 = pd.DataFrame(data=[24, 25],
                   index=pd.DatetimeIndex(['2013-01-24 15:01:00+01:00',
                                           '2013-01-25 15:01:00+01:00'],
                                          dtype='datetime64[ns, CET]',
                                          name='Date', freq=None))
print(df1.index.date)
```
the above code prints:
[datetime.date(2013, 1, 23) datetime.date(2013, 1, 24)]

#### Problem description
Datetime.index.date does return incorrect dates. The behavious worked until version 0.22, and seems to be incorrect post upgrade to version 0.23

#### Expected Output
`[datetime.date(2013, 1, 24) datetime.date(2013, 1, 25)]`

#### Output of ``pd.show_versions()``

<details>

[paste the output of ``pd.show_versions()`` here below this line]
INSTALLED VERSIONS
------------------
commit: None
python: 3.6.5.final.0
python-bits: 64
OS: Windows
OS-release: 10
machine: AMD64
processor: Intel64 Family 6 Model 60 Stepping 3, GenuineIntel
byteorder: little
LC_ALL: None
LANG: None
LOCALE: None.None
pandas: 0.23.0
pytest: 3.5.1
pip: 10.0.1
setuptools: 39.1.0
Cython: None
numpy: 1.12.1
scipy: 1.1.0
pyarrow: None
xarray: None
IPython: 6.4.0
sphinx: 1.7.4
patsy: 0.5.0
dateutil: 2.7.3
pytz: 2018.4
blosc: None
bottleneck: None
tables: None
numexpr: None
feather: None
matplotlib: 2.2.2
openpyxl: None
xlrd: 1.1.0
xlwt: None
xlsxwriter: None
lxml: None
bs4: 4.6.0
html5lib: 1.0.1
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None
</details>



Discussion:
Looks like a timezone issue since this works correctly without a timezone dtype. Investigation and PR's welcome!

```
In [7]: d = pd.DatetimeIndex(['2013-01-24 15:01:00+01:00','2013-01-25 15:01:00+01:00'])

In [8]: d.date
Out[8]:
array([datetime.date(2013, 1, 24), datetime.date(2013, 1, 25)],
      dtype=object)

In [9]: d.tz_localize('CET').date
Out[9]:
array([datetime.date(2013, 1, 23), datetime.date(2013, 1, 24)],
      dtype=object)
```
Hmm is this possibly on purpose?
```
import pandas as pd

df1 = pd.DataFrame(data=[24, 25],
                   index=pd.DatetimeIndex(['2013-01-24 15:01:00+01:00',
                                           '2013-01-25 15:01:00+01:00'],
                                          dtype='datetime64[ns, CET]',
                                          name='Date', freq=None))
print(type(df1.index))
```
Prints 

<class 'pandas.core.indexes.datetimes.DatetimeIndex'>

Looking at the index method of DatetimeIndex : 
https://github.com/pandas-dev/pandas/blob/master/pandas/core/indexes/datetimes.py#L2037
```
    @property
    def date(self):
        """
        Returns numpy array of python datetime.date objects (namely, the date
        part of Timestamps without timezone information).
        """
        return libts.ints_to_pydatetime(self.normalize().asi8, box="date")
```
So here the datetime.date obj is intentionally being returned without the timezone information.



I suppose this is due to https://github.com/pandas-dev/pandas/pull/18163, which improved the performance of the `.date` accessor, but probably forgot to deal with timezones.

Before it relied on the `date()` method of the individual timestamp objects, which is still correct: 

```
In [19]: df1.index[0]
Out[19]: Timestamp('2013-01-24 14:01:00+0100', tz='CET')

In [20]: df1.index[0].date()
Out[20]: datetime.date(2013, 1, 24)
```
cc @tmnhat2001 welcome to take a look if you would have time.

Resolving patch:
diff --git a/pandas/core/indexes/datetimes.py b/pandas/core/indexes/datetimes.py
--- a/pandas/core/indexes/datetimes.py
+++ b/pandas/core/indexes/datetimes.py
@@ -2032,7 +2032,16 @@ def time(self):
         """
         Returns numpy array of datetime.time. The time part of the Timestamps.
         """
-        return libts.ints_to_pydatetime(self.asi8, self.tz, box="time")
+
+        # If the Timestamps have a timezone that is not UTC,
+        # convert them into their i8 representation while
+        # keeping their timezone and not using UTC
+        if (self.tz is not None and self.tz is not utc):
+            timestamps = self._local_timestamps()
+        else:
+            timestamps = self.asi8
+
+        return libts.ints_to_pydatetime(timestamps, box="time")
 
     @property
     def date(self):
@@ -2040,7 +2049,16 @@ def date(self):
         Returns numpy array of python datetime.date objects (namely, the date
         part of Timestamps without timezone information).
         """
-        return libts.ints_to_pydatetime(self.normalize().asi8, box="date")
+
+        # If the Timestamps have a timezone that is not UTC,
+        # convert them into their i8 representation while
+        # keeping their timezone and not using UTC
+        if (self.tz is not None and self.tz is not utc):
+            timestamps = self._local_timestamps()
+        else:
+            timestamps = self.asi8
+
+        return libts.ints_to_pydatetime(timestamps, box="date")
 
     def normalize(self):
         """


Test patch:
diff --git a/pandas/tests/indexes/datetimes/test_timezones.py b/pandas/tests/indexes/datetimes/test_timezones.py
--- a/pandas/tests/indexes/datetimes/test_timezones.py
+++ b/pandas/tests/indexes/datetimes/test_timezones.py
@@ -2,7 +2,7 @@
 """
 Tests for DatetimeIndex timezone-related methods
 """
-from datetime import datetime, timedelta, tzinfo
+from datetime import datetime, timedelta, tzinfo, date, time
 from distutils.version import LooseVersion
 
 import pytest
@@ -706,6 +706,32 @@ def test_join_utc_convert(self, 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")