Repository: pandas-dev/pandas
Pull request: REGR: preserve freq in DTI/TDI outer join
- [x] closes #32157
- [x] tests added / passed
- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [ ] whatsnew entry

also just a nice cleanup, hoping to get this into ExtensionIndex before long

Problem statement:
API: joining DatetimeIndexes not preserving freq
```
dti = pd.date_range('2016-01-01', periods=10)
left, right = dti[:5], dti[5:]
joined = left.join(right, how="outer")
```

In master `joined.freq` comes back as None, while in 0.25.3 it was "D".  There is some code in DatetimeIndex._wrap_joined_index that suggests to me this was intentional.

Can someone confirm this suspicion cc @jorisvandenbossche ?  If so, I've got a branch that restores the old behavior, just need to add tests.

Hints from issue discussion:

Commit message: remove comment

Patch to review:
diff --git a/pandas/core/indexes/datetimelike.py b/pandas/core/indexes/datetimelike.py
index 1b3b6934aa53a..8e446f6d1d797 100644
--- a/pandas/core/indexes/datetimelike.py
+++ b/pandas/core/indexes/datetimelike.py
@@ -919,17 +919,14 @@ def _is_convertible_to_index_for_join(cls, other: Index) -> bool:
             return True
         return False
 
-    def _wrap_joined_index(self, joined, other):
+    def _wrap_joined_index(self, joined: np.ndarray, other):
+        assert other.dtype == self.dtype, (other.dtype, self.dtype)
         name = get_op_result_name(self, other)
-        if self._can_fast_union(other):
-            joined = self._shallow_copy(joined)
-            joined.name = name
-            return joined
-        else:
-            kwargs = {}
-            if hasattr(self, "tz"):
-                kwargs["tz"] = getattr(other, "tz", None)
-            return type(self)._simple_new(joined, name, **kwargs)
+
+        freq = self.freq if self._can_fast_union(other) else None
+        new_data = type(self._data)._simple_new(joined, dtype=self.dtype, freq=freq)
+
+        return type(self)._simple_new(new_data, name=name)
 
     # --------------------------------------------------------------------
     # List-Like Methods
diff --git a/pandas/tests/indexes/datetimes/test_setops.py b/pandas/tests/indexes/datetimes/test_setops.py
index 78188c54b1d85..7c80af07385a1 100644
--- a/pandas/tests/indexes/datetimes/test_setops.py
+++ b/pandas/tests/indexes/datetimes/test_setops.py
@@ -367,6 +367,19 @@ def test_join_nonunique(self):
         rs = idx1.join(idx2, how="outer")
         assert rs.is_monotonic
 
+    @pytest.mark.parametrize("tz", [None, "US/Pacific"])
+    def test_join_preserves_freq(self, tz):
+        # GH#32157
+        dti = pd.date_range("2016-01-01", periods=10, tz=tz)
+        result = dti[:5].join(dti[5:], how="outer")
+        assert result.freq == dti.freq
+        tm.assert_index_equal(result, dti)
+
+        result = dti[:5].join(dti[6:], how="outer")
+        assert result.freq is None
+        expected = dti.delete(5)
+        tm.assert_index_equal(result, expected)
+
 
 class TestBusinessDatetimeIndex:
     def setup_method(self, method):
diff --git a/pandas/tests/indexes/timedeltas/test_timedelta.py b/pandas/tests/indexes/timedeltas/test_timedelta.py
index 8a91c9d5e09c8..572d5b1810e63 100644
--- a/pandas/tests/indexes/timedeltas/test_timedelta.py
+++ b/pandas/tests/indexes/timedeltas/test_timedelta.py
@@ -91,6 +91,18 @@ def test_factorize(self):
         tm.assert_numpy_array_equal(arr, exp_arr)
         tm.assert_index_equal(idx, idx3)
 
+    def test_join_preserves_freq(self):
+        # GH#32157
+        tdi = timedelta_range("1 day", periods=10)
+        result = tdi[:5].join(tdi[5:], how="outer")
+        assert result.freq == tdi.freq
+        tm.assert_index_equal(result, tdi)
+
+        result = tdi[:5].join(tdi[6:], how="outer")
+        assert result.freq is None
+        expected = tdi.delete(5)
+        tm.assert_index_equal(result, expected)
+
     def test_join_self(self, join_type):
         index = timedelta_range("1 day", periods=10)
         joined = index.join(index, how=join_type)


Code review comments:
diff_hunk: @@ -367,6 +367,19 @@ def test_join_nonunique(self):
         rs = idx1.join(idx2, how="outer")
         assert rs.is_monotonic
 
+    @pytest.mark.parametrize("tz", [None, "US/Pacific"])
path: pandas/tests/indexes/datetimes/test_setops.py
text: @user1:
Could we use the `tz_aware_fixture` here?

@author:
sure