import datetime as dt import re, logging import zoneinfo from .sync import Event class IcalHelper: _WEEKDAY_MAP = { 'Mon': 0, 'Tue': 1, 'Wed': 2, 'Thu': 3, 'Fri': 4, 'Sat': 5, 'Sun': 6 } tzDE = zoneinfo.ZoneInfo('Europe/Berlin') deltaWeek = dt.timedelta(weeks=1) deltaDay = dt.timedelta(days=1) firstDay = dt.date(year=dt.datetime.now().year, month=1, day=1) def __init__(self): self._l = logging.getLogger(__name__) self.reTime = re.compile(r'(\d{1,2}):(\d{2})') def getStart(self, event: Event, holidays): firstDay = self._getFirstOccurence(event, holidays) if firstDay is None: return None, None return firstDay, dt.datetime.combine(firstDay, self._getTime(event.start), tzinfo=self.tzDE) def _getFirstWeekdayInYear(self, weekday): candidate = self.firstDay while candidate.weekday() != self._WEEKDAY_MAP[weekday]: candidate += self.deltaDay self._l.log(5, 'First %s in year is %s', weekday, candidate) return candidate def _getSortedHolidays(self, holidays): return sorted(holidays['holidays'], key=lambda h: h['from']) def _getSortedFeasts(self, holidays): return sorted(holidays['feasts']) def _isFeast(self, candidate, feasts): return candidate in feasts def _isHoliday(self, candidate, holidays): for h in holidays: if h['from'] <= candidate <= h['to']: return True return False def _getLastHolidayException(self, holidays): holidayList = list(self._getSortedHolidays(holidays)) holidayList.reverse() lastHoliday = holidayList[0] lastHolidayDay = lastHoliday['to'] feastList = list(self._getSortedFeasts(holidays)) feastList.reverse() lastFeast = feastList[0] lastFeastDay = lastFeast return max(lastHolidayDay, lastFeastDay) def _getFirstOccurence(self, event: Event, holidays): firstWeekday = self._getFirstWeekdayInYear(event.day) candidate = firstWeekday while candidate.year == firstWeekday.year: if self._isFeast(candidate, holidays['feasts']): self._l.log(5, 'Found feast %s', candidate) candidate += self.deltaWeek continue if self._isHoliday(candidate, holidays['holidays']): self._l.log(5, 'Found holiday %s', candidate) candidate += self.deltaWeek continue self._l.log(5, 'Found first occurence %s', candidate) return candidate return None # firstWeekday.tzinfo = dt.timezone.tzname('Europe/Berlin') def _getTime(self, startStr): match = self.reTime.match(startStr) return dt.time(int(match.group(1)), int(match.group(2))) def getHolidayExceptions(self, event: Event, startDay, holidays): lastHolidayDay = self._getLastHolidayException(holidays) pointer = startDay exeptions = [] while pointer <= lastHolidayDay: ex = dt.datetime.combine(pointer, self._getTime(event.start), tzinfo=self.tzDE) if self._isFeast(pointer, holidays['feasts']): exeptions.append(ex) if self._isHoliday(pointer, holidays['holidays']) and not event.external: exeptions.append(ex) pointer += self.deltaWeek if len(exeptions) > 0: return exeptions return None