BUG/PERF: offsets.apply doesnt preserve nanosecond
State: closed

Main Fix is to preserve nanosecond info which can lost during `offset.apply`, but it also includes:
- Support dateutil timezone
- Little performance improvement. Even though v0.14.1 should take longer than v0.14.0 because perf test in v0.14 doesn't perform timestamp conversion which was fixed in #7502.
  NOTE: This caches `Tick.delta` because it was calculated 3 times repeatedly, but does it cause any side effect?
### Before

```
-------------------------------------------------------------------------------
Test name                                    | head[ms] | base[ms] |  ratio   |
-------------------------------------------------------------------------------
timeseries_year_incr                         |   0.0164 |   0.0103 |   1.5846 |
timeseries_year_apply                        |   0.0153 |   0.0094 |   1.6356 |
timeseries_day_incr                          |   0.0187 |   0.0053 |   3.5075 |
timeseries_day_apply                         |   0.0164 |   0.0033 |   4.9048 |

Target [d0076db] : PERF: Improve index.min and max perf
Base   [da0f7ae] : RLS: 0.14.0 final
```
### After the fix

```
-------------------------------------------------------------------------------
Test name                                    | head[ms] | base[ms] |  ratio   |
-------------------------------------------------------------------------------
timeseries_year_incr                         |   0.0150 |   0.0087 |   1.7339 |
timeseries_year_apply                        |   0.0126 |   0.0073 |   1.7283 |
timeseries_day_incr                          |   0.0130 |   0.0053 |   2.4478 |
timeseries_day_apply                         |   0.0107 |   0.0033 |   3.2143 |

Target [64dd021] : BUG: offsets.apply doesnt preserve nanosecond
Base   [da0f7ae] : RLS: 0.14.0 final
```


Labels:
name: Bug
name: Performance
description: Memory or execution speed performance
name: Frequency
description: DateOffsets

Comments:
@sinhrks revisit?

@jreback Rebased. Is any other thing required?

- can you show an example of where this fails (in current master)
- can you point to where `Tick` is calculated several times (currently)?

### Affected offset (reset nanosecond)

If the aply logic includes `datetime` conversion, `nanosecond` will be lost.
- CustomBusinessDay
- CustomBusinessMonthEnd
- CustomBusinessMonthBegin
- MonthBegin
- BusinessMonthBegin
- MonthEnd
- BusinessMonthEnd
- YearBegin
- BYearBegin
- YearEnd
- BYearEnd
- QuarterBegin
- BQuarterBegin

### Affected offset (dateutil support)

`tz.localize` raises `AttributeError: 'tzfile' object has no attribute 'localize'` if `tz` is `dateutil` timezone.
- BQuarterBegin
- QuarterEnd
- BQuarterEnd
- LastWeekOfMonth
- FY5253Quarter
- FY5253
- WeekOfMonth
- Easter

> can you point to where Tick is calculated several times (currently)?

Caused by these:
- Check `hasattr`

https://github.com/pydata/pandas/blob/master/pandas/tslib.pyx#784
- Twice again in nanosecond conversion (`hasattr` and actual addition)

https://github.com/pydata/pandas/blob/master/pandas/tslib.pyx#784

not sure what you mean (about Tick being calced more than once). The 2nd time is simply int_64 addition. AFAICT. `delta_to_nanoseconds` is necessary. What do you think this should change to?

Yeah all operations are necessary. What I meant is every time `delta` is being calculated, thus it may better to change `cache_readonly`
https://github.com/pydata/pandas/blob/master/pandas/tseries/offsets.py#L2016.

Actually this is not affects to performance so much, thus it is possible to leave it as normal property.

I _think_ that `delta` and `nanos` _could_ be `cache_readonly`. Once you create an offset they are not changed (I don't think). You can try that (but separate PR). I am not sure how to test that, maybe just trace the code and see.

@sinhrks otherwise this looks ok. It just changes a lot of code so trying to review.

OK, modified to normal `property`.

seems, `apply_wraps` is on every apply, except for in `DateOffset`. maybe add a note there why this is (or is it right?)

`DateOffset` needs `apply_wraps`.  I missed because of misunderstanding that `DateOffset` cannot be used by itself (#7375). Fixed and added tests.

ok, ping when green

@jreback now green.

@sinhrks thanks for this...cleans up a large amount of code.....