Implement BaseOffset in tslibs.offsets
State: closed

This moves a handful of methods of `DateOffset` up into `tslibs.offsets.BaseOffset`.  The focus for now is on arithmetic methods that do not get overridden by subclasses.  These use the `self.__class__(..., **self.kwds)` pattern that we eventually need to get rid of.  Isolating this pattern before suggesting alternatives.

The `_BaseOffset` class was intended to be a `cdef` class, but that leads to errors in `test_pickle_v0_15_2` that I haven't figured out yet.  Once that gets sorted out, we can make `DateOffset` immutable and see some real speedups via caching.

See other comments in-line.

Labels:
name: Internals
description: Related to non-user accessible pandas implementation
name: Frequency
description: DateOffsets

Comments:
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=h1) Report
> Merging [#18016](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/b2d0d1bfe578ce91d545bd2f806655d0bf497440?src=pr&el=desc) will **decrease** coverage by `0.02%`.
> The diff coverage is `93.75%`.

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

```diff
@@            Coverage Diff             @@
##           master   #18016      +/-   ##
==========================================
- Coverage   91.23%   91.21%   -0.03%     
==========================================
  Files         163      163              
  Lines       50091    50032      -59     
==========================================
- Hits        45703    45636      -67     
- Misses       4388     4396       +8
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.02% <93.75%> (-0.02%)` | :arrow_down: |
| #single | `40.22% <93.75%> (-0.08%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/tseries/offsets.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL3RzZXJpZXMvb2Zmc2V0cy5weQ==) | `97.11% <93.75%> (-0.05%)` | :arrow_down: |
| [pandas/io/gbq.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL2lvL2dicS5weQ==) | `25% <0%> (-58.34%)` | :arrow_down: |
| [pandas/core/frame.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZnJhbWUucHk=) | `97.75% <0%> (-0.1%)` | :arrow_down: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18016?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/18016?src=pr&el=footer). Last update [b2d0d1b...8eb131e](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

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

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

```diff
@@            Coverage Diff             @@
##           master   #18016      +/-   ##
==========================================
+ Coverage   91.28%   91.41%   +0.12%     
==========================================
  Files         163      163              
  Lines       50130    50073      -57     
==========================================
+ Hits        45761    45772      +11     
+ Misses       4369     4301      -68
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `89.21% <100%> (+0.14%)` | :arrow_up: |
| #single | `40.32% <100%> (-0.07%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/tseries/offsets.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL3RzZXJpZXMvb2Zmc2V0cy5weQ==) | `97.11% <100%> (-0.05%)` | :arrow_down: |
| [pandas/io/gbq.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL2lvL2dicS5weQ==) | `25% <0%> (-58.34%)` | :arrow_down: |
| [pandas/core/frame.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZnJhbWUucHk=) | `97.8% <0%> (-0.05%)` | :arrow_down: |
| [pandas/core/internals.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW50ZXJuYWxzLnB5) | `94.54% <0%> (+0.07%)` | :arrow_up: |
| [pandas/io/formats/format.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL2lvL2Zvcm1hdHMvZm9ybWF0LnB5) | `96.01% <0%> (+0.07%)` | :arrow_up: |
| [pandas/core/panel.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvcGFuZWwucHk=) | `97.28% <0%> (+0.28%)` | :arrow_up: |
| [pandas/core/common.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvY29tbW9uLnB5) | `93% <0%> (+1.82%)` | :arrow_up: |
| [pandas/core/generic.py](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZ2VuZXJpYy5weQ==) | `95.72% <0%> (+3.3%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/18016?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/18016?src=pr&el=footer). Last update [a2d0eed...61bf134](https://codecov.io/gh/pandas-dev/pandas/pull/18016?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

```
taskset 4 asv continuous -E virtualenv -f 1.1 master HEAD -b timeseries
[...]
        before           after         ratio
     [b2d0d1bf]       [a728d995]
-     56.4±0.07μs       50.7±0.3μs     0.90  timeseries.SemiMonthOffset.time_begin_decr
-       152±0.7ms        135±0.5ms     0.89  timeseries.ToDatetime.time_iso8601_tz_spaceformat
-     1.79±0.01ms      1.54±0.01ms     0.86  frame_methods.frame_assign_timeseries_index.time_frame_assign_timeseries_index

```
small comments, and rebase
@jreback For triaging purposes, this is the only one of my PRs that is blocking non-refactoring work.
lgtm ping on green 
TestClipboard.test_round_trip_valid_encodings, otherwise green.  Will push a dummy commit anyway.
Ping
thanks!