Repository: pandas-dev/pandas
Pull request: DEPR: 'epoch' date format in to_json
- [x] closes #57063 
- [x] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature
- [ ] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [ ] Added [type annotations](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#type-hints) to new arguments/methods/functions.
- [x] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.


Problem statement:
DEP: Deprecate date_format="epoch" in to_json
### Feature Type

- [ ] Adding new functionality to pandas

- [ ] Changing existing functionality in pandas

- [X] Removing existing functionality in pandas


### Problem Description

While `date_format="epoch"` made sense when we exclusively handled ns-precision timestamps since the epoch, it has started to lose its meaning as we support other precisions. 

If you try to roundtrip JSON produced with this argument you just have to guess what the precision is

### Feature Description

See above

### Alternative Solutions

See above

### Additional Context

_No response_

Hints from issue discussion:
So after this change, `to_json` will only support the iso date format? Not sure how I feel about this, but I'm open to working on it as long as nobody's opposed
@Aloqeely that is correct

Commit message: Remove examples using epoch

Patch to review:
diff --git a/doc/source/user_guide/io.rst b/doc/source/user_guide/io.rst
index db2326d5b9754..183283e37435c 100644
--- a/doc/source/user_guide/io.rst
+++ b/doc/source/user_guide/io.rst
@@ -1949,13 +1949,6 @@ Writing in ISO date format, with microseconds:
    json = dfd.to_json(date_format="iso", date_unit="us")
    json
 
-Epoch timestamps, in seconds:
-
-.. ipython:: python
-
-   json = dfd.to_json(date_format="epoch", date_unit="s")
-   json
-
 Writing to a file, with a date index and a date column:
 
 .. ipython:: python
diff --git a/doc/source/whatsnew/v2.2.2.rst b/doc/source/whatsnew/v2.2.2.rst
index 54084abab7817..5484b25f00377 100644
--- a/doc/source/whatsnew/v2.2.2.rst
+++ b/doc/source/whatsnew/v2.2.2.rst
@@ -30,7 +30,7 @@ Bug fixes
 
 Other
 ~~~~~
--
+- Deprecated using ``epoch`` date format in :meth:`DataFrame.to_json` and :meth:`Series.to_json`, use ``iso`` instead.
 
 .. ---------------------------------------------------------------------------
 .. _whatsnew_222.contributors:
diff --git a/pandas/core/generic.py b/pandas/core/generic.py
index c0eda7f022d8f..a06e96498b134 100644
--- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -2328,6 +2328,11 @@ def to_json(
             'iso' = ISO8601. The default depends on the `orient`. For
             ``orient='table'``, the default is 'iso'. For all other orients,
             the default is 'epoch'.
+
+            .. deprecated:: 2.2.2
+                'epoch' date format is deprecated and will be removed in a future
+                version, please use 'iso' instead.
+
         double_precision : int, default 10
             The number of decimal places to use when encoding
             floating point values. The possible maximal value is 15.
@@ -2530,6 +2535,14 @@ def to_json(
             date_format = "iso"
         elif date_format is None:
             date_format = "epoch"
+        elif date_format == "epoch":
+            # GH#57063
+            warnings.warn(
+                "'Epoch' date format is deprecated and will be removed in a future "
+                "version, please use 'iso' date format instead.",
+                FutureWarning,
+                stacklevel=find_stack_level(),
+            )
 
         config.is_nonnegative_int(indent)
         indent = indent or 0
diff --git a/pandas/tests/io/json/test_json_table_schema.py b/pandas/tests/io/json/test_json_table_schema.py
index afc9974c75e6a..e9ff96d7f182a 100644
--- a/pandas/tests/io/json/test_json_table_schema.py
+++ b/pandas/tests/io/json/test_json_table_schema.py
@@ -451,12 +451,17 @@ def test_to_json_categorical_index(self):
         assert result == expected
 
     def test_date_format_raises(self, df_table):
-        msg = (
+        error_msg = (
             "Trying to write with `orient='table'` and `date_format='epoch'`. Table "
             "Schema requires dates to be formatted with `date_format='iso'`"
         )
-        with pytest.raises(ValueError, match=msg):
-            df_table.to_json(orient="table", date_format="epoch")
+        warning_msg = (
+            "'Epoch' date format is deprecated and will be removed in a future "
+            "version, please use 'iso' date format instead."
+        )
+        with pytest.raises(ValueError, match=error_msg):
+            with tm.assert_produces_warning(FutureWarning, match=warning_msg):
+                df_table.to_json(orient="table", date_format="epoch")
 
         # others work
         df_table.to_json(orient="table", date_format="iso")
diff --git a/pandas/tests/io/json/test_pandas.py b/pandas/tests/io/json/test_pandas.py
index db120588b234c..070b569cef7a5 100644
--- a/pandas/tests/io/json/test_pandas.py
+++ b/pandas/tests/io/json/test_pandas.py
@@ -835,15 +835,23 @@ def test_date_index_and_values(self, date_format, as_object, date_typ):
             data.append("a")
 
         ser = Series(data, index=data)
-        result = ser.to_json(date_format=date_format)
 
+        expected_warning = None
         if date_format == "epoch":
             expected = '{"1577836800000":1577836800000,"null":null}'
+            expected_warning = FutureWarning
         else:
             expected = (
                 '{"2020-01-01T00:00:00.000":"2020-01-01T00:00:00.000","null":null}'
             )
 
+        msg = (
+            "'Epoch' date format is deprecated and will be removed in a future "
+            "version, please use 'iso' date format instead."
+        )
+        with tm.assert_produces_warning(expected_warning, match=msg):
+            result = ser.to_json(date_format=date_format)
+
         if as_object:
             expected = expected.replace("}", ',"a":"a"}')
 
@@ -940,7 +948,12 @@ def test_date_unit(self, unit, datetime_frame):
         df.iloc[2, dl] = Timestamp("21460101 20:43:42")
         df.iloc[4, dl] = pd.NaT
 
-        json = df.to_json(date_format="epoch", date_unit=unit)
+        msg = (
+            "'Epoch' date format is deprecated and will be removed in a future "
+            "version, please use 'iso' date format instead."
+        )
+        with tm.assert_produces_warning(FutureWarning, match=msg):
+            json = df.to_json(date_format="epoch", date_unit=unit)
 
         # force date unit
         result = read_json(StringIO(json), date_unit=unit)
@@ -1106,17 +1119,24 @@ def test_timedelta_to_json(self, as_object, date_format, timedelta_typ):
             data.append("a")
 
         ser = Series(data, index=data)
+        expected_warning = None
         if date_format == "iso":
             expected = (
                 '{"P1DT0H0M0S":"P1DT0H0M0S","P2DT0H0M0S":"P2DT0H0M0S","null":null}'
             )
         else:
+            expected_warning = FutureWarning
             expected = '{"86400000":86400000,"172800000":172800000,"null":null}'
 
         if as_object:
             expected = expected.replace("}", ',"a":"a"}')
 
-        result = ser.to_json(date_format=date_format)
+        msg = (
+            "'Epoch' date format is deprecated and will be removed in a future "
+            "version, please use 'iso' date format instead."
+        )
+        with tm.assert_produces_warning(expected_warning, match=msg):
+            result = ser.to_json(date_format=date_format)
         assert result == expected
 
     @pytest.mark.parametrize("as_object", [True, False])
@@ -1669,7 +1689,17 @@ def test_read_json_with_very_long_file_path(self, compression):
     def test_timedelta_as_label(self, date_format, key):
         df = DataFrame([[1]], columns=[pd.Timedelta("1D")])
         expected = f'{{"{key}":{{"0":1}}}}'
-        result = df.to_json(date_format=date_format)
+
+        expected_warning = None
+        if date_format == "epoch":
+            expected_warning = FutureWarning
+
+        msg = (
+            "'Epoch' date format is deprecated and will be removed in a future "
+            "version, please use 'iso' date format instead."
+        )
+        with tm.assert_produces_warning(expected_warning, match=msg):
+            result = df.to_json(date_format=date_format)
 
         assert result == expected
 


Code review comments:
diff_hunk: @@ -30,7 +30,7 @@ Bug fixes
 
 Other
 ~~~~~
--
+- Deprecated using ``epoch`` date format in :meth:`DataFrame.to_json` and :meth:`Series.to_json`, use ``iso`` instead.
path: doc/source/whatsnew/v2.2.2.rst
text: @user1:
I suppose when we get rid of `epoch` as an argument then `iso` is the only option; as such we should probably just get rid of this argument altogether instead of directing users to supply a different argument

@author:
 Should I go back and deprecate using the `date_format` parameter?
My first thought was that if I did that, then using the `iso` format will throw a deprecation warning, which I don't think is quite right because `epoch` will be deprecated and it's good for people to avoid using it, to avoid this issue, I would have to make the default date format = `iso`, but that might break existing code, right?

Or could I be misunderstanding and you are just suggesting to remove the `use ``iso`` instead.` message

@user1:
OK ignore what I said above. In a future world we can get rid of this argument altogther, but I suppose for the first deprecation we should make it so that the only valid arguments are None / iso