from datetime import datetime, timedelta from math import ceil from typing import Optional class MinariDateTime: DAY_NAMES = ( 'Mirdu', 'Hëmi', 'Drak', 'Þodon', 'Charm', 'Ro’unn', ) MONTH_NAMES = ( 'Mëbel', 'Dirann', 'Ma’uþ', 'Gerub', 'Þrei', 'Dimoc', 'Xentor', 'Mëðir', 'Draþ', 'Quaden', 'Ridïmel', 'Rodom', ) SPECIAL_NAMES = ( 'Hëður', 'Rideyy', 'Morkh', 'Khmerd', 'Chamog', ) ENTITIES = { 'Hëður': { 'entity': 'Garquon', 'element': 'Meren' }, 'Mëbel': { 'entity': 'Þoraðrin', 'element': 'Aðun' }, 'Dirann': { 'entity': 'Detërien', 'element': 'Merðen' }, 'Ma’uþ': { 'entity': 'Elin', 'element': 'Fronn' }, 'Rideyy': { 'entity': 'Hëriel', 'element': 'Enðir' }, 'Gerub': { 'entity': 'Iliþon', 'element': 'Miþon' }, 'Þrei': { 'entity': 'Amenar', 'element': None }, 'Dimoc': { 'entity': 'Iminiru', 'element': 'Sëdur' }, 'Morkh': { 'entity': 'Luminar', 'element': 'Holar' }, 'Xentor': { 'entity': 'Motimor', 'element': 'Rort' }, 'Mëðïr': { 'entity': 'Mirian', 'element': 'Ilšir' }, 'Draþ': { 'entity': 'Heloor', 'element': None }, 'Khmerd': { 'entity': 'Zuþeron', 'element': 'Gord' }, 'Quaden': { 'entity': 'Umonar', 'element': 'Ulquon' }, 'Ridïmel': { 'entity': 'Feiriamen', 'element': 'Reina' }, 'Rodom': { 'entity': 'Nozoru', 'element': 'Zima' }, 'Chamog': { 'entity': 'Ketirai', 'element': 'Loar' } } def __init__(self, timestamp: datetime): self.set_timestamp(timestamp.replace(hour=0, minute=0, second=0, microsecond=0)) self._minari_year = None self._minari_month = None self._minari_day = None self._special_day = None self._minari_weekday = None def set_timestamp(self, timestamp: datetime): self._timestamp = timestamp + timedelta(days=11) self._calculated = False @staticmethod def _get_doy(timestamp: datetime) -> int: onejan = datetime(timestamp.year, 1, 1) time_diff = timestamp - onejan return ceil(time_diff.total_seconds() / 86400) @staticmethod def _is_leap(timestamp: datetime) -> bool: year = timestamp.year if year % 400 == 0: return True if year % 100 == 0: return False if year % 4 == 0: return True return False def _calculate_minari_parts(self): if self._calculated: return minari_leap = self._is_leap(self._timestamp) doy = self._get_doy(self._timestamp) self._minari_year = self._timestamp.year - 1873 self._minari_month = None self._minari_day = None special_days = ( (0, None, 0), (91, None, 1), (182, None, 2), (183, True, 2), (273, False, 3), (274, True, 3), (364, False, 4), (365, True, 4), ) for special_doy, leap_only, special_num in special_days: if doy == special_doy and (leap_only is None or leap_only == minari_leap): self._special_day = special_num break if self._special_day is None: decr = 0 minari_doy = doy for special_doy, leap_only, _ in special_days: if minari_doy > special_doy and (leap_only is None or leap_only == minari_leap): decr += 1 minari_doy -= decr - 1 self._minari_month = ceil(minari_doy / 30) self._minari_day = minari_doy % 30 if self._minari_day == 0: self._minari_day = 30 self._minari_weekday = self._minari_day % 6 self._calculated = True @property def minari_year(self): if not self._calculated: self._calculate_minari_parts() return self._minari_year @property def minari_month(self): if not self._calculated: self._calculate_minari_parts() return self._minari_month @property def minari_day(self): if not self._calculated: self._calculate_minari_parts() return self._minari_day @property def minari_special(self): if not self._calculated: self._calculate_minari_parts() return self._special_day def __str__(self): output = f'{self.minari_year} ' if self._special_day is not None: output += self.SPECIAL_NAMES[self._special_day] else: month_name = self.MONTH_NAMES[self._minari_month] weekday_name = self.DAY_NAMES[self._minari_weekday] output += f'{month_name} {self._minari_day} ({weekday_name})' return output