Repository: pandas-dev/pandas

Issue:
AssertionError in FY5253.rollback()
Hi,

I'm trying to do North American (Year,WeekOfYear) <-> Period (W-SAT) conversions, for calculations involving fiscal year week numbers, etc. From what I understand pandas.offsets.FY5253 can be used for this, but I'm getting a weird assertion error than should probably not happen.

#### Code

```python
import pandas as pd
fy = pd.offsets.FY5253(n=1, weekday=5, startingMonth=12, variation='nearest')
fy.rollback(pd.Timestamp('2004-01-01'))
```

#### Expected Output

I think the output should be equal to the one you get with
```python
fy.rollback(pd.Timestamp('2003-12-31'))
```
which is
```python
Timestamp('2002-12-28 00:00:00')
```

#### Output of ``pd.show_versions()``

<details>

commit: None
python: 2.7.12.final.0
python-bits: 64
OS: Linux
OS-release: 4.2.0-35-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.19.1
nose: 1.3.7
pip: 8.1.2
setuptools: 28.7.1
Cython: 0.23.4
numpy: 1.11.2
scipy: 0.17.1
statsmodels: 0.6.1
xarray: None
IPython: 4.1.2
sphinx: 1.3.5
patsy: 0.4.0
dateutil: 2.6.0
pytz: 2016.7
blosc: None
bottleneck: 1.0.0
tables: 3.2.2
numexpr: 2.5
matplotlib: 1.5.1
openpyxl: 2.3.2
xlrd: 0.9.4
xlwt: 1.0.0
xlsxwriter: 0.8.4
lxml: 3.6.0
bs4: 4.4.1
html5lib: None
httplib2: 0.9.2
apiclient: 1.4.2
sqlalchemy: 1.0.12
pymysql: None
psycopg2: 2.6.1 (dt dec pq3 ext)
jinja2: 2.7.3
boto: 2.38.0
pandas_datareader: None

</details>

AssertionError in FY5253.rollback()
Hi,

I'm trying to do North American (Year,WeekOfYear) <-> Period (W-SAT) conversions, for calculations involving fiscal year week numbers, etc. From what I understand pandas.offsets.FY5253 can be used for this, but I'm getting a weird assertion error than should probably not happen.

#### Code

```python
import pandas as pd
fy = pd.offsets.FY5253(n=1, weekday=5, startingMonth=12, variation='nearest')
fy.rollback(pd.Timestamp('2004-01-01'))
```

#### Expected Output

I think the output should be equal to the one you get with
```python
fy.rollback(pd.Timestamp('2003-12-31'))
```
which is
```python
Timestamp('2002-12-28 00:00:00')
```

#### Output of ``pd.show_versions()``

<details>

commit: None
python: 2.7.12.final.0
python-bits: 64
OS: Linux
OS-release: 4.2.0-35-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None
LANG: en_US.UTF-8
LOCALE: en_US.UTF-8

pandas: 0.19.1
nose: 1.3.7
pip: 8.1.2
setuptools: 28.7.1
Cython: 0.23.4
numpy: 1.11.2
scipy: 0.17.1
statsmodels: 0.6.1
xarray: None
IPython: 4.1.2
sphinx: 1.3.5
patsy: 0.4.0
dateutil: 2.6.0
pytz: 2016.7
blosc: None
bottleneck: 1.0.0
tables: 3.2.2
numexpr: 2.5
matplotlib: 1.5.1
openpyxl: 2.3.2
xlrd: 0.9.4
xlwt: 1.0.0
xlsxwriter: 0.8.4
lxml: 3.6.0
bs4: 4.4.1
html5lib: None
httplib2: 0.9.2
apiclient: 1.4.2
sqlalchemy: 1.0.12
pymysql: None
psycopg2: 2.6.1 (dt dec pq3 ext)
jinja2: 2.7.3
boto: 2.38.0
pandas_datareader: None

</details>



Discussion:
@ronif I've got a bugfix that handles the example you gave.  Do you happen to have an example that fails on `rollforward`?  I think there may be a symmetric bug.
@ronif I've got a bugfix that handles the example you gave.  Do you happen to have an example that fails on `rollforward`?  I think there may be a symmetric bug.

Resolving patch:
diff --git a/pandas/tseries/offsets.py b/pandas/tseries/offsets.py
--- a/pandas/tseries/offsets.py
+++ b/pandas/tseries/offsets.py
@@ -1831,21 +1831,26 @@ def apply(self, other):
         elif n > 0:
             if other < prev_year:
                 n -= 2
-                # TODO: Not hit in tests
-            elif other < cur_year:
+            elif prev_year < other < cur_year:
                 n -= 1
-            elif other < next_year:
+            elif cur_year < other < next_year:
                 pass
             else:
                 assert False
         else:
-            if other > next_year:
+            if next_year < other:
                 n += 2
-                # TODO: Not hit in tests
-            elif other > cur_year:
+                # TODO: Not hit in tests; UPDATE: looks impossible
+            elif cur_year < other < next_year:
                 n += 1
-            elif other > prev_year:
+            elif prev_year < other < cur_year:
                 pass
+            elif (other.year == prev_year.year and other < prev_year and
+                  prev_year - other <= timedelta(6)):
+                # GH#14774, error when next_year.year == cur_year.year
+                # e.g. prev_year == datetime(2004, 1, 3),
+                # other == datetime(2004, 1, 1)
+                n -= 1
             else:
                 assert False
 


Test patch:
diff --git a/pandas/tests/tseries/offsets/test_fiscal.py b/pandas/tests/tseries/offsets/test_fiscal.py
--- a/pandas/tests/tseries/offsets/test_fiscal.py
+++ b/pandas/tests/tseries/offsets/test_fiscal.py
@@ -9,6 +9,7 @@
 
 import pandas.util.testing as tm
 
+from pandas import Timestamp
 from pandas.tseries.frequencies import get_offset, _INVALID_FREQ_ERROR
 from pandas.tseries.offsets import FY5253Quarter, FY5253
 from pandas._libs.tslibs.offsets import WeekDay
@@ -604,3 +605,23 @@ def test_offset(self):
         assert_offset_equal(offset2,
                             datetime(2013, 1, 15),
                             datetime(2013, 3, 30))
+
+
+def test_bunched_yearends():
+    # GH#14774 cases with two fiscal year-ends in the same calendar-year
+    fy = FY5253(n=1, weekday=5, startingMonth=12, variation='nearest')
+    dt = Timestamp('2004-01-01')
+    assert fy.rollback(dt) == Timestamp('2002-12-28')
+    assert (-fy).apply(dt) == Timestamp('2002-12-28')
+    assert dt - fy == Timestamp('2002-12-28')
+
+    assert fy.rollforward(dt) == Timestamp('2004-01-03')
+    assert fy.apply(dt) == Timestamp('2004-01-03')
+    assert fy + dt == Timestamp('2004-01-03')
+    assert dt + fy == Timestamp('2004-01-03')
+
+    # Same thing, but starting from a Timestamp in the previous year.
+    dt = Timestamp('2003-12-31')
+    assert fy.rollback(dt) == Timestamp('2002-12-28')
+    assert (-fy).apply(dt) == Timestamp('2002-12-28')
+    assert dt - fy == Timestamp('2002-12-28')