diff --git a/conservancy_beancount/books.py b/conservancy_beancount/books.py index 4cc427f6cba6cc198efe9666dc81a6981f95f7dd..c2d04df11abf892aef95820b451ea2ee2fcc6dd4 100644 --- a/conservancy_beancount/books.py +++ b/conservancy_beancount/books.py @@ -50,6 +50,19 @@ class FiscalYear(NamedTuple): else: return date.year + def first_date(self, year: Year) -> datetime.date: + if isinstance(year, datetime.date): + year = self.for_date(year) + return datetime.date(year, self.month, self.day) + + def last_date(self, year: Year) -> datetime.date: + return self.next_fy_date(year) - datetime.timedelta(days=1) + + def next_fy_date(self, year: Year) -> datetime.date: + if isinstance(year, datetime.date): + year = self.for_date(year) + return datetime.date(year + 1, self.month, self.day) + def range(self, from_fy: Year, to_fy: Optional[Year]=None) -> Iterable[int]: """Return a range of fiscal years diff --git a/tests/test_books_fiscal_year.py b/tests/test_books_fiscal_year.py index 9327ab0e04528eed31f29d04a2715e8891dde453..35366d286b7ee54da0967335f011def21f768245 100644 --- a/tests/test_books_fiscal_year.py +++ b/tests/test_books_fiscal_year.py @@ -134,3 +134,53 @@ def test_range_offset_only(cy_fy, year_offset): expected = range(year, year + year_offset + 1) actual = list(cy_fy.range(year_offset)) assert actual == list(expected) + +@pytest.mark.parametrize('year', range(2016, 2022)) +def test_first_date_year_arg(conservancy_fy, year): + assert conservancy_fy.first_date(year) == datetime.date(year, 3, 1) + +@pytest.mark.parametrize('date', [ + datetime.date(2019, 1, 1), + datetime.date(2019, 10, 10), + datetime.date(2020, 2, 2), + datetime.date(2020, 12, 12), +]) +def test_first_date_date_arg(conservancy_fy, date): + year = date.year + if date.month < 3: + year -= 1 + assert conservancy_fy.first_date(date) == datetime.date(year, 3, 1) + +@pytest.mark.parametrize('year', range(2016, 2022)) +def test_last_date_year_arg(conservancy_fy, year): + day = 28 if year % 4 else 29 + assert conservancy_fy.last_date(year - 1) == datetime.date(year, 2, day) + +@pytest.mark.parametrize('date', [ + datetime.date(2019, 1, 1), + datetime.date(2019, 10, 10), + datetime.date(2020, 2, 2), + datetime.date(2020, 12, 12), +]) +def test_last_date_date_arg(conservancy_fy, date): + year = date.year + if date.month >= 3: + year += 1 + day = 28 if year % 4 else 29 + assert conservancy_fy.last_date(date) == datetime.date(year, 2, day) + +@pytest.mark.parametrize('year', range(2016, 2022)) +def test_next_fy_date_year_arg(conservancy_fy, year): + assert conservancy_fy.next_fy_date(year) == datetime.date(year + 1, 3, 1) + +@pytest.mark.parametrize('date', [ + datetime.date(2019, 1, 1), + datetime.date(2019, 10, 10), + datetime.date(2020, 2, 29), + datetime.date(2020, 12, 12), +]) +def test_next_fy_date_date_arg(conservancy_fy, date): + year = date.year + if date.month >= 3: + year += 1 + assert conservancy_fy.next_fy_date(date) == datetime.date(year, 3, 1)