Repository: pandas-dev/pandas

Issue:
Snap drops name from datetimeindex
#### Code Sample, a copy-pastable example if possible

```python

import pandas as pd

dti = pd.DatetimeIndex([], name='my_dti', freq='H')
print(dti.round('D'))  # Name is preserved
print(dti.snap('D'))  # Name is lost

```
#### Problem description

The DatetimeIndex.snap method loses the name of the DatetimeIndex which is a different behaviour than DatetimeIndex.round (or floor, ceil, etc.) that preserves the name of the DatetimeIndex.

#### Expected Output

DatetimeIndex([], dtype='datetime64[ns]', name='my_dti', freq='D')

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.6.7.final.0
python-bits: 64
OS: Linux
OS-release: 4.15.0-45-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_CA.UTF-8
LOCALE: en_CA.UTF-8

pandas: 0.24.1
pytest: 4.0.0
pip: 19.0.3
setuptools: 40.4.3
Cython: 0.29.6
numpy: 1.16.2
scipy: 1.2.1
pyarrow: None
xarray: None
IPython: 7.3.0
sphinx: None
patsy: 0.5.0
dateutil: 2.7.3
pytz: 2018.3
blosc: None
bottleneck: None
tables: None
numexpr: None
feather: None
matplotlib: 3.0.3
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml.etree: 4.2.1
bs4: None
html5lib: 1.0.1
sqlalchemy: 1.2.18
pymysql: None
psycopg2: 2.7.7 (dt dec pq3 ext lo64)
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: 0.9.0
pandas_datareader: None
gcsfs: None


</details>

Snap drops name from datetimeindex
#### Code Sample, a copy-pastable example if possible

```python

import pandas as pd

dti = pd.DatetimeIndex([], name='my_dti', freq='H')
print(dti.round('D'))  # Name is preserved
print(dti.snap('D'))  # Name is lost

```
#### Problem description

The DatetimeIndex.snap method loses the name of the DatetimeIndex which is a different behaviour than DatetimeIndex.round (or floor, ceil, etc.) that preserves the name of the DatetimeIndex.

#### Expected Output

DatetimeIndex([], dtype='datetime64[ns]', name='my_dti', freq='D')

#### Output of ``pd.show_versions()``

<details>

INSTALLED VERSIONS
------------------
commit: None
python: 3.6.7.final.0
python-bits: 64
OS: Linux
OS-release: 4.15.0-45-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_CA.UTF-8
LOCALE: en_CA.UTF-8

pandas: 0.24.1
pytest: 4.0.0
pip: 19.0.3
setuptools: 40.4.3
Cython: 0.29.6
numpy: 1.16.2
scipy: 1.2.1
pyarrow: None
xarray: None
IPython: 7.3.0
sphinx: None
patsy: 0.5.0
dateutil: 2.7.3
pytz: 2018.3
blosc: None
bottleneck: None
tables: None
numexpr: None
feather: None
matplotlib: 3.0.3
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml.etree: 4.2.1
bs4: None
html5lib: 1.0.1
sqlalchemy: 1.2.18
pymysql: None
psycopg2: 2.7.7 (dt dec pq3 ext lo64)
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: 0.9.0
pandas_datareader: None
gcsfs: None


</details>



Discussion:
Thanks for the report. There's actually a comment in the code that notes that this is missing (as well as tz). PR's welcome!

https://github.com/pandas-dev/pandas/blob/3e652ac565e054b763ccbe23006c02704f6cd1b1/pandas/core/indexes/datetimes.py#L790-L791
How does snap actually work?

pd.date_range(start='2019-1-1', end='2019-1-2', freq='10T').snap('H')

Doesn't return what I expect. Is this a bigger bug?
Thanks for the report. There's actually a comment in the code that notes that this is missing (as well as tz). PR's welcome!

https://github.com/pandas-dev/pandas/blob/3e652ac565e054b763ccbe23006c02704f6cd1b1/pandas/core/indexes/datetimes.py#L790-L791
How does snap actually work?

pd.date_range(start='2019-1-1', end='2019-1-2', freq='10T').snap('H')

Doesn't return what I expect. Is this a bigger bug?

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
@@ -787,8 +787,8 @@ def snap(self, freq='S'):
             snapped[i] = s
 
         # we know it conforms; skip check
-        return DatetimeIndex._simple_new(snapped, freq=freq)
-        # TODO: what about self.name?  tz? if so, use shallow_copy?
+        return DatetimeIndex._simple_new(snapped, name=self.name, tz=self.tz,
+                                         freq=freq)
 
     def join(self, other, how='left', level=None, return_indexers=False,
              sort=False):


Test patch:
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
@@ -52,20 +52,28 @@ def test_fancy_setitem():
     assert (s[48:54] == -3).all()
 
 
-def test_dti_snap():
+@pytest.mark.filterwarnings("ignore::DeprecationWarning")
+@pytest.mark.parametrize('tz', [None, 'Asia/Shanghai', 'Europe/Berlin'])
+@pytest.mark.parametrize('name', [None, 'my_dti'])
+def test_dti_snap(name, tz):
     dti = DatetimeIndex(['1/1/2002', '1/2/2002', '1/3/2002', '1/4/2002',
-                         '1/5/2002', '1/6/2002', '1/7/2002'], freq='D')
+                         '1/5/2002', '1/6/2002', '1/7/2002'],
+                        name=name, tz=tz, freq='D')
 
-    res = dti.snap(freq='W-MON')
-    exp = date_range('12/31/2001', '1/7/2002', freq='w-mon')
-    exp = exp.repeat([3, 4])
-    assert (res == exp).all()
+    result = dti.snap(freq='W-MON')
+    expected = date_range('12/31/2001', '1/7/2002',
+                          name=name, tz=tz, freq='w-mon')
+    expected = expected.repeat([3, 4])
+    tm.assert_index_equal(result, expected)
+    assert result.tz == expected.tz
 
-    res = dti.snap(freq='B')
+    result = dti.snap(freq='B')
 
-    exp = date_range('1/1/2002', '1/7/2002', freq='b')
-    exp = exp.repeat([1, 1, 1, 2, 2])
-    assert (res == exp).all()
+    expected = date_range('1/1/2002', '1/7/2002',
+                          name=name, tz=tz, freq='b')
+    expected = expected.repeat([1, 1, 1, 2, 2])
+    tm.assert_index_equal(result, expected)
+    assert result.tz == expected.tz
 
 
 def test_dti_reset_index_round_trip():