Repository: pandas-dev/pandas

Issue:
DEPR: deprecate keep_tz keyword in DatetimeIndex.to_series
xref https://github.com/pandas-dev/pandas/pull/17826

`DatetimeIndex.to_series` has a `keep_tz` keyword with a default of False (= dropping the timezone information). This stems from a time we could not store datetime tz data inside a series (with `keep_tz=True`, you would get an object dtyped Series of Timestamps). Nowadays this just gives a series with `datetime64[ns, tz]` dtype, so it makes sense to make this the default and deprecate the keyword.

Since it is an API change, ideally we first raise a warning that the behaviour will be changed (the default will change from False to True). You could then suppress this warning by passing `keep_tz=True`. 
However, that means that we cannot directly deprecate the keyword itself, as we have to keep it for suppressing the warning / getting the future behaviour.




Discussion:
Indeed, how we approach this will depend on when the release gets cut.  If we get this in before the cut, we could easily just change the default behavior + deprecate in one PR.  Otherwise, we would have to split it into two across multiple releases.
Why does this depend on when the rc gets cut? (when I mentioned it, I was referring to whether I would have time to do it before the rc gets cut) 

How can we change the behaviour and deprecate in one PR? You mean just changing the behaviour without warning? Which is actually certainly an option, as this prevents having to do the deprecation in two steps (first behaviour, then keyword), and I don't think this is that much used, so I am OK with just a back incompat breaking change.

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
@@ -478,7 +478,7 @@ def _get_time_micros(self):
             values = self._local_timestamps()
         return fields.get_time_micros(values)
 
-    def to_series(self, keep_tz=False, index=None, name=None):
+    def to_series(self, keep_tz=None, index=None, name=None):
         """
         Create a Series with both index and values equal to the index keys
         useful with map for returning an indexer based on an index
@@ -486,7 +486,7 @@ def to_series(self, keep_tz=False, index=None, name=None):
         Parameters
         ----------
         keep_tz : optional, defaults False
-            return the data keeping the timezone.
+            Return the data keeping the timezone.
 
             If keep_tz is True:
 
@@ -500,6 +500,12 @@ def to_series(self, keep_tz=False, index=None, name=None):
 
               Series will have a datetime64[ns] dtype. TZ aware
               objects will have the tz removed.
+
+            .. versionchanged:: 0.24
+                The default value will change to True in a future release.
+                You can set ``keep_tz=True`` to already obtain the future
+                behaviour and silence the warning.
+
         index : Index, optional
             index of resulting Series. If None, defaults to original index
         name : string, optional
@@ -517,6 +523,19 @@ def to_series(self, keep_tz=False, index=None, name=None):
         if name is None:
             name = self.name
 
+        if keep_tz is None and self.tz is not None:
+            warnings.warn("The default of the 'keep_tz' keyword will change "
+                          "to True in a future release. You can set "
+                          "'keep_tz=True' to obtain the future behaviour and "
+                          "silence this warning.", FutureWarning, stacklevel=2)
+            keep_tz = False
+        elif keep_tz is False:
+            warnings.warn("Specifying 'keep_tz=False' is deprecated and this "
+                          "option will be removed in a future release. If "
+                          "you want to remove the timezone information, you "
+                          "can do 'idx.tz_convert(None)' before calling "
+                          "'to_series'.", FutureWarning, stacklevel=2)
+
         if keep_tz and self.tz is not None:
             # preserve the tz & copy
             values = self.copy(deep=True)


Test patch:
diff --git a/pandas/tests/frame/test_alter_axes.py b/pandas/tests/frame/test_alter_axes.py
--- a/pandas/tests/frame/test_alter_axes.py
+++ b/pandas/tests/frame/test_alter_axes.py
@@ -317,12 +317,21 @@ def test_convert_dti_to_series(self):
         tm.assert_series_equal(result, expected)
 
         # convert to utc
-        df['B'] = idx.to_series(index=[0, 1])
+        with tm.assert_produces_warning(FutureWarning):
+            df['B'] = idx.to_series(keep_tz=False, index=[0, 1])
         result = df['B']
         comp = Series(DatetimeIndex(expected.values).tz_localize(None),
                       name='B')
         tm.assert_series_equal(result, comp)
 
+        with tm.assert_produces_warning(FutureWarning):
+            result = idx.to_series(index=[0, 1])
+        tm.assert_series_equal(result, expected.dt.tz_convert(None))
+
+        with tm.assert_produces_warning(FutureWarning):
+            result = idx.to_series(keep_tz=False, index=[0, 1])
+        tm.assert_series_equal(result, expected.dt.tz_convert(None))
+
         # list of datetimes with a tz
         df['B'] = idx.to_pydatetime()
         result = df['B']