Repository: adamjstewart/fiscalyear

Issue:
FiscalCalendar starting on 31st day of the month throws ValueError
If the `FiscalCalendar` starts on the 31st of any month with 31 days, then `FiscalDate().quarter` throws a `ValueError`.

Example code:
```python
from fiscalyear import FiscalDate, FiscalYear, fiscal_calendar

with fiscal_calendar(start_month=12, start_day=31):
    fiscal_date = FiscalDate(2019, 10, 22)
    fiscal_date.quarter
```

Traceback:
```
Traceback (most recent call last):
  File "<input>", line 3, in <module>
  File "/site-packages/fiscalyear.py", line 594, in quarter
    if self in q:
  File "/site-packages/fiscalyear.py", line 413, in __contains__
    return self.start.date() <= item <= self.end.date()
  File "/site-packages/fiscalyear.py", line 493, in end
    next_start = self.next_quarter.start
  File "/site-packages/fiscalyear.py", line 485, in start
    return FiscalDateTime(year, month, START_DAY, 0, 0, 0)
ValueError: day is out of range for month
```

The quarters generated are:

* December 31st
* March 31st
* June 31st ← Not a valid day, so it breaks.


Comments:

Patch:
diff --git a/fiscalyear.py b/fiscalyear.py
index 661c703..d4a569f 100644
--- a/fiscalyear.py
+++ b/fiscalyear.py
@@ -530,7 +530,12 @@ class FiscalQuarter(object):
         if month < START_MONTH:
             year += 1
 
-        return FiscalDateTime(year, month, START_DAY, 0, 0, 0)
+        # Find the last day of the month
+        # If START_DAY is later, choose last day of month instead
+        max_day = calendar.monthrange(year, month)[1]
+        day = min(START_DAY, max_day)
+
+        return FiscalDateTime(year, month, day, 0, 0, 0)
 
     @property
     def end(self):


Test patch:
diff --git a/test_fiscalyear.py b/test_fiscalyear.py
index 2395737..f80953c 100644
--- a/test_fiscalyear.py
+++ b/test_fiscalyear.py
@@ -275,6 +275,35 @@ class TestFiscalCalendar:
             with fiscalyear.fiscal_calendar(start_month=2, start_day=29):
                 pass
 
+    def test_corner_cases(self):
+        # start_day does not exist in all months
+        with fiscalyear.fiscal_calendar(start_month=5, start_day=31):
+            # Non-leap year
+            assert fiscalyear.FiscalQuarter(2019, 1).start.day == 31
+            assert fiscalyear.FiscalQuarter(2019, 1).end.day == 30
+
+            assert fiscalyear.FiscalQuarter(2019, 2).start.day == 31
+            assert fiscalyear.FiscalQuarter(2019, 2).end.day == 29
+
+            assert fiscalyear.FiscalQuarter(2019, 3).start.day == 30
+            assert fiscalyear.FiscalQuarter(2019, 3).end.day == 27
+
+            assert fiscalyear.FiscalQuarter(2019, 4).start.day == 28
+            assert fiscalyear.FiscalQuarter(2019, 4).end.day == 30
+
+            # Leap year
+            assert fiscalyear.FiscalQuarter(2020, 1).start.day == 31
+            assert fiscalyear.FiscalQuarter(2020, 1).end.day == 30
+
+            assert fiscalyear.FiscalQuarter(2020, 2).start.day == 31
+            assert fiscalyear.FiscalQuarter(2020, 2).end.day == 29
+
+            assert fiscalyear.FiscalQuarter(2020, 3).start.day == 30
+            assert fiscalyear.FiscalQuarter(2020, 3).end.day == 28
+
+            assert fiscalyear.FiscalQuarter(2020, 4).start.day == 29
+            assert fiscalyear.FiscalQuarter(2020, 4).end.day == 30
+
 
 class TestFiscalYear: