BUG/ENH: Handle NonexistentTimeError in date rounding
State: closed

- [x] closes #23324
- [x] tests added / passed
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`
- [x] whatsnew entry

Similar strategy as https://github.com/pandas-dev/pandas/pull/22647, added a `nonexistent` keyword argument to `round`, `ceil`, and `floor` to control rounding when encountering a `NonexistentTimeError`

This is also fixes a bug in the `nonexistent='shift'` implementation in https://github.com/pandas-dev/pandas/pull/22644 where dates with timezones with negative UTC offsets got shifted by an additional hour. This bug is naturally tested by these rounding tests

Labels:
name: Bug
name: Datetime
description: Datetime data dtype
name: Timezones
description: Timezone data dtype

Comments:
Hello @mroeschke! Thanks for submitting the PR.

 - There are no PEP8 issues in the file [`pandas/core/indexes/datetimelike.py`](https://github.com/mroeschke/pandas/blob/35711b08e1b4caddc6639fd96a37ad1ddd6b023d/pandas/core/indexes/datetimelike.py) !

 - There are no PEP8 issues in the file [`pandas/tests/scalar/timestamp/test_unary_ops.py`](https://github.com/mroeschke/pandas/blob/35711b08e1b4caddc6639fd96a37ad1ddd6b023d/pandas/tests/scalar/timestamp/test_unary_ops.py) !

 - There are no PEP8 issues in the file [`pandas/tests/series/test_datetime_values.py`](https://github.com/mroeschke/pandas/blob/35711b08e1b4caddc6639fd96a37ad1ddd6b023d/pandas/tests/series/test_datetime_values.py) !


# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/23406?src=pr&el=h1) Report
> Merging [#23406](https://codecov.io/gh/pandas-dev/pandas/pull/23406?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/a2e599499667b256bc5b8b13a75f0601eccfd432?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/23406/graphs/tree.svg?width=650&token=eZ4WkYLtcO&height=150&src=pr)](https://codecov.io/gh/pandas-dev/pandas/pull/23406?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master   #23406      +/-   ##
==========================================
+ Coverage   92.18%   92.19%   +<.01%     
==========================================
  Files         161      161              
  Lines       51184    51192       +8     
==========================================
+ Hits        47185    47194       +9     
+ Misses       3999     3998       -1
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `90.62% <100%> (ø)` | :arrow_up: |
| #single | `42.22% <55.55%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/23406?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/indexes/datetimelike.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9kYXRldGltZWxpa2UucHk=) | `98.01% <100%> (ø)` | :arrow_up: |
| [pandas/util/\_decorators.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvX2RlY29yYXRvcnMucHk=) | `91.34% <0%> (ø)` | :arrow_up: |
| [pandas/core/indexes/multi.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9tdWx0aS5weQ==) | `95.46% <0%> (ø)` | :arrow_up: |
| [pandas/util/testing.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvdGVzdGluZy5weQ==) | `86.84% <0%> (ø)` | :arrow_up: |
| [pandas/util/\_test\_decorators.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvX3Rlc3RfZGVjb3JhdG9ycy5weQ==) | `93.24% <0%> (ø)` | :arrow_up: |
| [pandas/util/\_doctools.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvX2RvY3Rvb2xzLnB5) | `12.87% <0%> (ø)` | :arrow_up: |
| [pandas/compat/pickle\_compat.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL2NvbXBhdC9waWNrbGVfY29tcGF0LnB5) | `75.6% <0%> (ø)` | :arrow_up: |
| [pandas/core/indexes/base.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9iYXNlLnB5) | `96.62% <0%> (ø)` | :arrow_up: |
| [pandas/core/ops.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvb3BzLnB5) | `94.25% <0%> (+0.01%)` | :arrow_up: |
| [pandas/core/indexes/range.py](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9yYW5nZS5weQ==) | `95.79% <0%> (+0.02%)` | :arrow_up: |
| ... and [2 more](https://codecov.io/gh/pandas-dev/pandas/pull/23406/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/23406?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/23406?src=pr&el=footer). Last update [a2e5994...b31f034](https://codecov.io/gh/pandas-dev/pandas/pull/23406?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Addressed your comments @jreback. I can make another issue between docstring templating between `Timestamp` and `NaT` as many methods share the same docstring (not just `tz_localize`) 
thanks @mroeschke 

yes would like to template the doc-strings to avoid duplication.