HistoricalDate jest trochę przkombinowane i źle zaprojektowane.
Przede wszystkim, rozdziel ideę daty i długości czasu. Nie ma sensu "dodaj rok 12 naszej ery do roku 2000 naszej ery", znacznie więcej sensu ma "dodaj 12 lat do roku 2000 naszej ery".
Można by też ogólnie uprościć implementację, żeby pod spodem była jedna liczba reprezentująca rok, a na zewnątrz eksponować "faktyczny rok" i "faktyczną erę". Wtedy np dodawanie i porównywanie dat robi się trywialne i cała klasa skraca się o połowę (przy okazji zastąpiłem __le__ etc przez auto-generowane porównania i zamieniłem więcej metod na property):
@functools.total_ordering
class HistoricalDate:
def __init__(self, year=None, era=Era.CE):
assert year != 0 # albo exception
year = year or date.today().year
if era == Era.CE:
self._year_counter = year
else:
self._year_counter = 1 - year
@classmethod
def _from_year_counter(cls, year_counter):
ret = cls()
ret._year_counter = year_counter
return ret
@property
def year(self):
if self._year_counter <= 0:
return 1 - self._year_counter
return self._year_counter
@property
def era(self):
return Era.CE if self._year_counter > 0 else Era.BCE
@property
def century(self):
return ((self.year - 1) // 100) + 1
@property
def millennium(self):
return ((self.year - 1) // 1000) + 1
def __str__(self):
return f'{self.year} {self.era.name}'
def add_years(self, years):
return HistoricalDate._from_year_counter(self._year_counter + years)
def __eq__(self, other):
return self._year_counter == other._year_counter
def __lt__(self, other):
return self._year_counter < other._year_counter
Napisane na szybko :) Być może @dataclass by to mogło jeszcze bardziej uprościć, albo w ogóle zastąpić to jakąś zewnętrzną biblioteką...