API deprecate date_parser, add date_format
State: closed

- [ ] closes #50601 (Replace xxxx with the GitHub issue number)
- [ ] [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.
- [ ] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.

To summarise:
- `date_parser` is only ever a performance hindrance, and never speeds anything up
- `date_format` would be more useful to users, and could actually offer a speedup

So, this PR deprecates `date_parser` in favour of `date_format`

Labels:
name: Deprecate
description: Functionality to remove in pandas

Comments:
Thinking aloud: We may want to keep `date_parser` around as it could provide a way to not depend on the dateutil parser internally in the future while allowing user to still get similar behavior by passing `date_parser=dateutil.parse`
It would still be faster to do that with `apply`:
```python
In [2]: timestamp_format = '%Y-%m-%d %H:%M:%S'
   ...: 
   ...: date_index = pd.date_range(start='1900', end='2000')
   ...: 
   ...: dates_df = date_index.strftime(timestamp_format).to_frame(name='ts_col')
   ...: data = dates_df.to_csv()

In [3]: %%timeit
   ...: df = pd.read_csv(io.StringIO(data), parse_dates=['ts_col'], date_parser=du_parse)
   ...: 
   ...: 
1.1 s ± 15.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [4]: %%timeit
   ...: df = pd.read_csv(io.StringIO(data))
   ...: df['ts_col'] = df['ts_col'].apply(du_parse)
   ...: 
   ...: 
1.06 s ± 9.87 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
```
thanks for reviewing - sure, will update the whatsnew, and can address the "accept dict" part in a separate PR

I don't think we want to encourage `.apply(du_parse)`, it was just an example to show that even if `to_datetime` got rid of `dateutil`, then users would still be able to use it without needing a `date_parser` argument
> Will tests be added for `date_format` in a follow up PR?

I'm embarrassed to have missed that 🤦 thanks, adding them now

EDIT: done - thanks @mroeschke for having caught this!
@MarcoGorelli: So you'll be using `date_format` after all instead of pushing users to do date parsing in two steps, [like you were saying in in the previous issue](https://github.com/pandas-dev/pandas/issues/50601#issuecomment-1376165670)? I do think that's the right call as far as ergonomics are concerned, especitally with regards to MultiIndex objects.

I'm also going to echo what phofl said and say that supporting a dict here (and possibly all the other configurations supported by  `parse_dates`, so they can mirror each other) to allow for per column formats would be a big plus. Per-column formats are already possible with date_parser, they're just [really *really* clunky to use](https://github.com/pandas-dev/pandas/issues/50601#issuecomment-1373912056).
hey @sm-Fifteen - yeah, a bit of both: support `date_format`, but also document that for anything for complex / unusual, people should just parse in two steps
Sure - got the deprecation showing up with `read_excel` and `read_fwf` now too

Got tests

Have opened https://github.com/pandas-dev/pandas/issues/51240 as a follow-up to let `date_format` take a mapping
Any objections to getting this in?
Thanks for your reviews! Merging then
> I'd like to have date_format accept a dictionary (not necessarily in this pr, but as a follow up).

not a blocker for 2.0, right? don't know if I can get that in fast enough
Yeah would be okay for 2.1 IMO but also fine to accept during the RC period
Cool thanks - I'll see how far I get tomorrow morning, maybe it won't be too bad