diff options
Diffstat (limited to 'datamaps/core/temporal.py')
-rw-r--r-- | datamaps/core/temporal.py | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/datamaps/core/temporal.py b/datamaps/core/temporal.py new file mode 100644 index 0000000..ddb7004 --- /dev/null +++ b/datamaps/core/temporal.py @@ -0,0 +1,121 @@ +import datetime + + +class FinancialYear: + """An object representing a financial year. + + Used by ``bcompiler`` internally when creating :py:class:`bcompiler.api.Master` objects. + Can be used to calculate start and ends dates and :py:class:`bcompiler.api.Quarter` objects. + + If parameter ``year`` must be in the range 150 - 2100. + + """ + + def __init__(self, year): + if isinstance(year, int) and (year in range(150, 2100)): + self.year = year + else: + raise ValueError("A year must be an integer between 1950 and 2100") + self._generate_quarters() + self._q1 = self.quarters[0] + self._q2 = self.quarters[1] + self._q3 = self.quarters[2] + self._q4 = self.quarters[3] + + self.start_date = self.q1.start_date + self.end_date = self.q4.end_date + + @property + def q1(self) -> datetime.date: + """Quarter 1 as a :py:class:`datetime.date` object + """ + return self._q1 + + @property + def q2(self): + """Quarter 2 as a :py:class:`datetime.date` object + """ + return self._q2 + + @property + def q3(self): + """Quarter 3 as a :py:class:`datetime.date` object + """ + return self._q3 + + @property + def q4(self): + """Quarter 4 as a :py:class:`datetime.date` object + """ + return self._q4 + + def __str__(self): + return f"FY{str(self.year)}/{str(self.year + 1)[2:]}" + + def _generate_quarters(self): + self.quarters = [Quarter(x, self.year) for x in range(1, 5)] + + + def __repr__(self): + return f"FinancialYear({self.year})" + + +class Quarter: + """An object representing a financial quarter. This is mainly required for building + a :py:class:`core.master.Master` object. + + Args: + quarter (int): e.g.1, 2, 3 or 4 + year (int): e.g. 2013 + """ + _start_months = { + 1: (4, 'April'), + 2: (7, 'July'), + 3: (10, 'October'), + 4: (1, 'January') + } + + _end_months = { + 1: (6, 'June', 30), + 2: (9, 'September', 30), + 3: (12, 'December', 31), + 4: (3, 'March', 31), + } + + + def __init__(self, quarter: int, year: int) -> None: + + if isinstance(quarter, int) and (quarter >= 1 and quarter <= 4): + self.quarter = quarter + else: + raise ValueError("A quarter must be either 1, 2, 3 or 4") + + if isinstance(year, int) and (year in range(1950, 2100)): + self.year = year + else: + raise ValueError("Year must be between 1950 and 2100 - surely that will do?") + + self.start_date = self._start_date(self.quarter, self.year) + self.end_date = self._end_date(self.quarter, self.year) + + def __str__(self): + return f"Q{self.quarter} {str(self.year)[2:]}/{str(self.year + 1)[2:]}" + + def _start_date(self, q, y): + if q == 4: + y = y + 1 + return datetime.date(y, Quarter._start_months[q][0], 1) + + def _end_date(self, q, y): + if q == 4: + y = y + 1 + return datetime.date(y, Quarter._end_months[q][0], Quarter._end_months[q][2]) + + def __repr__(self): + return f"Quarter({self.quarter}, {self.year})" + + @property + def fy(self): + """Return a :py:class:`core.temporal.FinancialYear` object. + """ + return FinancialYear(self.year) |