def add_events( cv, rows, url, start_date=datetime.datetime.now().date(), days=90, ): source = 'google' if 'google' in url \ else 'outlook' if 'forecsys' in url \ else None end_date = start_date + datetime.timedelta(days=days) ical_string = urllib.request.urlopen(url).read() calendar = icalendar.Calendar.from_ical(ical_string) events = recurring_ical_events.of(calendar).between(start_date, end_date) row_dict = {cal_row_key(row.title, row.date): row for row in rows if row.date is not None} for row in rows: if row.source == source \ and row.date is not None \ and pd.to_datetime(row.date.start) >= start_date: row.source = None for event in events: event = describe_event(event.copy()) if event['date'] is None: continue if source == 'outlook' \ and pd.to_datetime(event['date'].start).date() \ in work_calendar['vacation']: continue key = cal_row_key(event['title'], event['date']) finded = row_dict[key] if key in row_dict \ else cv.collection.add_row(title=event['title']) update_row(finded, event) finded.source = source for row in rows: if row.source is None: row.remove()
def icsParser(currentSSMParameter): #Adept this part according to your need auth_parameters = json.loads(currentSSMParameter) print ("Start: Calendar Parser") icsURL=auth_parameters["url_ics"] print("Start Calendar connection ") icsCalendar = requests.get(url=icsURL) if icsCalendar.status_code == 200: print ("Successful connection. HTTP STATUS: "+str(icsCalendar.status_code)) else: print("Some error occurs in connection. HTTP STATUS: "+str(icsCalendar.status_code)) icsCalendar = Calendar.from_ical(icsCalendar.text) today = datetime.today() events = recurring_ical_events.of(icsCalendar).at(today) for event in events: #Update strech if event signature update if "Sobreaviso" in event['SUMMARY']: print ("Today: "+str(today)) print ("START ON-CALL: "+str(event['DTSTART'].dt)) print ("END ON-CAL: "+str(event['DTEND'].dt)) summary = event['SUMMARY'].splitlines() email = summary[2].split(" - ") email = email[1].strip() print ("ANALYST EMAIL: "+email) response = {'email':email} return (response)
def _convert(self, data): entries = [] # parse ics file calendar = icalendar.Calendar.from_ical(data) start_date = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) end_date = start_date.replace(year=start_date.year + 1) events = recurring_ical_events.of(calendar).between( start_date, end_date) entries = [] for e in events: if e.name == "VEVENT": dtstart = None if type(e.get("dtstart").dt) == datetime.date: dtstart = e.get("dtstart").dt elif type(e.get("dtstart").dt) == datetime.datetime: dtstart = e.get("dtstart").dt.date() if self._offset is not None: dtstart += datetime.timedelta(days=self._offset) summary = str(e.get("summary")) entries.append(CollectionAppointment(dtstart, summary)) return entries
def fetch(): results = [] url = "https://ics.teamup.com/feed/ks37xo2ni1ai6nmu88/4596912.ics" start_date = datetime.date.today() end_date = start_date + datetime.timedelta(days=7) ical_string = urllib.request.urlopen(url).read() calendar = icalendar.Calendar.from_ical(ical_string) events = recurring_ical_events.of(calendar).between(start_date, end_date) for element in events: event = {} dtstart = element.get("DTSTART", None) dtend = element.get("DTEND", None) summary = element.get("SUMMARY", None) description = element.get("DESCRIPTION", None) if description is not None: description = (description[:175] + '...') if len(description) > 75 else description event['datetime'] = dtstart.dt event['begin'] = dtstart.dt event['end'] = dtend.dt event['title'] = summary event['about'] = description event['source'] = url results.append(event) return results
def calgen(first_day,last_day,month_or_week): ''' generate calendar''' # create/clear temp csv file for processing and safety temp_file = "temp.csv" temp_file = os.path.join(folder_location, temp_file) open(temp_file,"w").close() in_start_date = first_day.split("/") in_end_date = last_day.split("/") file_name_gen = f"{''.join(in_start_date)}-{''.join(in_end_date)}-calendar_table.csv" file_name_gen = os.path.join(folder_location, file_name_gen) # get ical file and assign it to a calendar variable ical_string = urllib.request.urlopen(URL).read() gen_calendar = icalendar.Calendar.from_ical(ical_string) out_start_date, out_end_date = get_date_range(in_start_date, in_end_date,month_or_week) events = recurring_ical_events.of(gen_calendar).between(out_start_date, out_end_date) writeEventsToFile(events,temp_file) sort_csv(temp_file,file_name_gen,5) os.remove(temp_file) return file_name_gen
def calcEntries(): today = datetime.today() count = 5 dates = [] maxDate = [] if K4CG_ACTIVE: k4cg = list(rrule(MONTHLY, count=count, byweekday=TH(1), dtstart=today, wkst=MO)) for dateEntry in k4cg: dates.append(k4cgEntry(dateEntry)) maxDate.append(dateEntry) if FABLAB_ACTIVE: fablab = list(rrule(MONTHLY, count=count, byweekday=TU(3), dtstart=today, wkst=MO)) for dateEntry in fablab: dates.append(fablabEntry(dateEntry)) maxDate.append(dateEntry) if NERDBERG_ACTIVE: if len(maxDate) == 0: maxDate.append(today + timedelta(weeks=12)) ical_string = urllib.request.urlopen("https://kalender.nerdberg.de/events.ics").read() calendar = icalendar.Calendar.from_ical(ical_string) events = recurring_ical_events.of(calendar).between(today, max(maxDate)) for event in events: if event['Summary'] == "Chaostreff": dates.append(nerdbergEntry(event['dtstart'].dt.replace(tzinfo=None))) dates.sort(key=lambda entry: entry.date) return dates
def retrieve_calendar(url, specification): """Get the calendar entry from a url. Also unfold the events to past and future. see https://dateutil.readthedocs.io/en/stable/rrule.html """ try: calendar_text = get_text_from_url(url) calendars = icalendar.Calendar.from_ical(calendar_text, multiple=True) # collect latest event information ical_events = [] today = datetime.datetime.utcnow() one_year_ahead = today.replace(year=today.year + 1) one_year_before = today.replace(year=today.year - 1) for calendar in calendars: ical_events.extend(recurring_ical_events.of(calendar).between(one_year_before, one_year_ahead)) # collect events and their recurrences events = {} # id: event timeshift = int(specification["timeshift"]) for calendar_event in ical_events: event = convert_ical_event_to_dhtmlx(calendar_event, timeshift) events[event["id"]] = event return events except: ty, err, tb = sys.exc_info() error = error_to_dhtmlx(ty, err, tb, url=url) return {error["id"]: error}
def check_schedule(self, timer): context = ssl._create_unverified_context() ical_string = urllib.request.urlopen(calendar_url, context=context).read() calendar = icalendar.Calendar.from_ical(ical_string) events = recurring_ical_events.of(calendar).at(datetime.datetime.now()) if events: self.arduino.show() else: self.arduino.hide()
def collect_components_from(self, calendars): today = datetime.datetime.utcnow() one_year_ahead = today.replace(year=today.year + 1) one_year_before = today.replace(year=today.year - 1) for calendar in calendars: events = recurring_ical_events.of(calendar).between(one_year_before, one_year_ahead) with self.lock: for event in events: json_event = self.convert_ical_event(event) self.components.append(json_event)
def get_data(self): hk, hv = self.get_hidden() headers = { 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:86.0) Gecko/20100101 Firefox/86.0', } data = { hk: hv, "f_id_kommune": self._municipality, "f_id_strasse": self._street, "f_abfallarten": self._trashtypes, "f_zeitraum": f"{dt.now().strftime('%Y0101')}-{dt.now().strftime('%Y1231')}", } if self._district: data["f_id_bezirk"] = self._district url = f"https://api.abfallplus.de/?key={self._key}&modus={self._modus}&waction=export_ics" _LOGGER.debug(f"get_ical headers: {headers}") _LOGGER.debug(f"get_ical data: {data}") _LOGGER.debug(f"get_ical URL: {url}") try: r = requests.post(url, headers=headers, data=data) except Exception as e: _LOGGER.error(f"Couldn't get ical from url") _LOGGER.error(e) return _LOGGER.debug(f"get_ical was successful") try: cal = Calendar.from_ical(r.content) reoccuring_events = recurring_ical_events.of(cal).between( dt.now(), dt.now() + td(self._lookahead)) except Exception as e: _LOGGER.error(f"Couldn't parse ical file, {e}") return self._state = "unknown" for event in reoccuring_events: if event.has_key("SUMMARY"): if re.match(self._pattern, event.get("SUMMARY")): self._state = event["DTSTART"].dt.strftime( self._timeformat) self._state_attributes["remaining"] = ( event["DTSTART"].dt - date.today()).days self._state_attributes["description"] = event.get( "DESCRIPTION", "") break
def retrieve_calendar(url, specification): """Get the calendar entry from a url. Also unfold the events to past and future. see https://dateutil.readthedocs.io/en/stable/rrule.html """ try: calendar_text = get_text_from_url(url) except requests.exceptions.ConnectionError: traceback.print_exc() return [] calendars = icalendar.Calendar.from_ical(calendar_text, multiple=True) # collect latest event information ical_events = [] today = datetime.datetime.utcnow() one_year_ahead = today.replace(year=today.year + 1) one_year_before = today.replace(year=today.year - 1) for calendar in calendars: ical_events.extend( recurring_ical_events.of(calendar).between(one_year_before, one_year_ahead)) # collect events and their recurrences events = {} # id: event timeshift = int(specification["timeshift"]) for calendar_event in ical_events: start = calendar_event["DTSTART"].dt end = calendar_event.get("DTEND", calendar_event["DTSTART"]).dt geo = calendar_event.get("GEO", None) if geo: geo = {"lon": geo.longitude, "lat": geo.latitude} name = calendar_event.get("SUMMARY", "") sequence = str(calendar_event.get("SEQUENCE", 0)) uid = calendar_event["UID"] event = { "start_date": date_to_string(start, timeshift), "end_date": date_to_string(end, timeshift), "start_date_iso": start.isoformat(), "end_date_iso": end.isoformat(), "start_date_iso_0": start.isoformat(), "end_date_iso_0": end.isoformat(), "text": name, "description": calendar_event.get("DESCRIPTION", ""), "location": calendar_event.get("LOCATION", None), "geo": geo, "uid": uid, "ical": calendar_event.to_ical().decode("UTF-8"), "sequence": sequence, "recurrence": None, "url": calendar_event.get("URL"), } event_id = (uid, event["start_date"]) events[event_id] = event return events
def get_events(self, timeline_start, timeline_end, timezone=None): """Input an arrow (time) object for: * the beginning of timeline (events have to end after this time) * the end of the timeline (events have to begin before this time) * timezone if events should be formatted to local time Returns a list of events sorted by date """ if type(timeline_start) == arrow.arrow.Arrow: if timezone == None: timezone = 'UTC' t_start = timeline_start t_end = timeline_end else: raise Exception('Please input a valid arrow (time) object!') # parse non-recurring events # Recurring events time-span has to be in this format: # "%Y%m%dT%H%M%SZ" (python strftime) fmt = lambda date: (date.year, date.month, date.day, date.hour, date.minute, date.second) t_start_recurring = fmt(t_start) t_end_recurring = fmt(t_end) # Fetch recurring events recurring_events = (recurring_ical_events.of(ical).between( t_start_recurring, t_end_recurring) for ical in self.icalendars) events = ( { 'title': events.get('SUMMARY').lstrip(), 'begin': arrow.get(events.get('DTSTART').dt).to(timezone) if ( arrow.get(events.get('dtstart').dt).format('HH:mm') != '00:00') else arrow.get(events.get('DTSTART').dt).replace(tzinfo=timezone), 'end':arrow.get(events.get("DTEND").dt).to(timezone) if ( arrow.get(events.get('dtstart').dt).format('HH:mm') != '00:00') else arrow.get(events.get('DTEND').dt).replace(tzinfo=timezone) } for ical in recurring_events for events in ical) # if any recurring events were found, add them to parsed_events if events: self.parsed_events += list(events) # Sort events by their beginning date self.sort() return self.parsed_events
def test_keep_recurrence_attributes_default(calendars): with open(calendar_path, "rb") as file: content = file.read() calendar = Calendar.from_ical(content) today = datetime.today() one_year_ahead = today.replace(year=today.year + 1) events = of(calendar).between(today, one_year_ahead) for event in events: assert event.get("RRULE", False) is False assert event.get("RDATE", False) is False assert event.get("EXDATE", False) is False
def event_exists( url="https://ics.teamup.com/feed/ksrpvuwdkfnxsqxg6p/4896450.ics"): c = icalendar.Calendar.from_ical(requests.get(url).text) events = recurring_ical_events.of(c).between( datetime(2020, 4, 16, 18, 0) - timedelta(minutes=30), datetime(2020, 4, 16, 19, 0) + timedelta(minutes=30)) if len(events) == 0: raise HTTPException( status_code=500, detail="No matching event is found in the CCT calendar!") events.sort(key=lambda x: x["DTEND"].dt - x["DTSTART"].dt) event_name = events[0]["SUMMARY"] if events[0][ "SUMMARY"] != "Donnerstagssitzung" else f"Donnerstagssitzung am {date.today().strftime('%d.%m.%Y')}" return event_name
def extract(ical, start_date, end_date, calendar_owner, db): filtered = icalendar.Calendar() calendar_name = ical.get('X-WR-CALNAME') template_variables.set(db, 'calendar_name', calendar_name) template_variables.set(db, 'start_date', start_date) template_variables.set(db, 'end_date', end_date) if calendar_owner: template_variables.set(db, 'calendar_owner', calendar_owner) for c in ical.walk(): if c.get('dtstart') and c.get('dtend'): filtered.add_component(c) vevents = recurring_ical_events.of(filtered).between(start_date, end_date) for v in vevents: store_vevent(db, v, calendar_owner or calendar_name)
def convert(self, ics_data): # parse ics file try: calendar = icalendar.Calendar.from_ical(ics_data) except Exception as err: _LOGGER.error(f"Parsing ics data failed:{str(err)}") _LOGGER.debug(ics_data) return [] # calculate start- and end-date for recurring events start_date = datetime.datetime.now().replace(hour=0, minute=0, second=0, microsecond=0) if self._offset is not None: start_date -= datetime.timedelta(days=self._offset) end_date = start_date.replace(year=start_date.year + 1) events = recurring_ical_events.of(calendar).between( start_date, end_date) entries = [] for e in events: if e.name == "VEVENT": # calculate date dtstart = None if type(e.get("dtstart").dt) == datetime.date: dtstart = e.get("dtstart").dt elif type(e.get("dtstart").dt) == datetime.datetime: dtstart = e.get("dtstart").dt.date() if self._offset is not None: dtstart += datetime.timedelta(days=self._offset) # calculate waste type summary = str(e.get("summary")) if self._regex is not None: match = self._regex.match(summary) if match: summary = match.group(1) if self._split_at is not None: summary = re.split(self._split_at, summary) for t in summary: entries.append((dtstart, t.strip().title())) else: entries.append((dtstart, summary)) return entries
def read_calendar(cal): events = [] gcal = Calendar.from_ical(cal) today = date.today() next_month = today+timedelta(days=30) for event in recurring_ical_events.of(gcal).between(today, next_month): description = replace_url_to_link(fix_double_url(event['description'])) events += [ { "time": event['dtstart'].dt.astimezone(pytz.utc), "title": event['summary'], "location": event['location'].title().lower(), "organizer": read_organizer(event), "description": description } ] events.sort(key=lambda e: e['time']) return events
def updateAvail(self, cal): events = recurring_ical_events.of(cal).between(self.startTime, self.endTime) for event in events: if event.get('X-MICROSOFT-CDO-BUSYSTATUS') == 'BUSY': if (self.startTime.replace(day=1, month=1, year=1) <= event.get('dtstart').dt.replace(tzinfo=None, day=1, month=1, year=1) < self.endTime.replace(day=1, month=1, year=1)) or \ (event.get('dtstart').dt.replace(tzinfo=None, day=1, month=1, year=1) < self.endTime.replace( day=1, month=1, year=1)): self.setUnavailable( event.get('dtstart').dt, event.get('dtend').dt) print(event.get('summary'), end='') print(': ', end='') print(event.get('dtstart').dt) return
def lectureEventsJournee(calendar, shift=0): now = utcnow().to("Europe/Paris").shift(days=shift) #on récupère la liste des évènements du jour events = recurring_ical_events.of(calendar).at( (now.year, now.month, now.day)) #events = recurring_ical_events.of(calendar).between((2020, 9, 29), (2020, 9, 30)) #pour voir les évènements d'un jour en particulier (en phase de tests) TOUTE_JOURNEE = [ ] #liste des évènements qui durent tte la journée. l'agenda est maintenu de sorte à ce que ça ne corresponde qu'à "cours en présentiel/distanciel" EVENTS = [] #les cours for event in events: if not isinstance(event["DTSTART"].dt, datetime): #c'est un truc qui dure toute la journée TOUTE_JOURNEE.append(event) else: EVENTS.append(event) return TOUTE_JOURNEE, EVENTS
def IsTimeInEvent(timespec: str, now=datetime.now(pytz.utc)): if timespec == None or timespec == "" or timespec.lower() == "always": return True if now.tzinfo == None: raise ValueError("Now time must have timezone!") try: cal = Calendar.from_ical(timespec) events = recurring_ical_events.of(cal).at(now) return any(events) except: pass times = timespec.split(";") start = datetime.fromisoformat(times[0]).replace(tzinfo=pytz.utc) end = datetime.fromisoformat(times[1]).replace(tzinfo=pytz.utc) return start <= now and end >= now
def lezioniRange(start, end): lezioni = [] ical_string = requests.get(CALENDAR_URL).text calendar = icalendar.Calendar.from_ical(ical_string) events = recurring_ical_events.of(calendar).between(start, end) for event in events: name = event["SUMMARY"] description = event["DESCRIPTION"] url = re.findall( 'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\), ]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', description) try: url = url[0] except: url = "" start = fixDateTime(str(event["DTSTART"].dt)) end = fixDateTime(str(event["DTEND"].dt)) evento = {"materia": name, "url": url, "inizio": start, "fine": end} lezioni.append(evento) return sorted(lezioni, key=lambda i: i['inizio'])
def NextEvent(timespec: str, now=datetime.now(pytz.utc)): if timespec == None or timespec == "" or timespec.lower() == "always": return None if now.tzinfo == None: raise ValueError("Now time must have timezone!") try: oneyear = now + timedelta(days=366) cal = Calendar.from_ical(timespec) events = recurring_ical_events.of(cal).between(now, oneyear) if any: return events[0]['DTSTART'].dt.strftime("%c") else: return "Never" except: times = timespec.split(";") start = datetime.fromisoformat(times[0]).replace(tzinfo=pytz.utc) return start.strftime("%c")
def get_calendar_data(): tz = pytz.timezone(TIME_ZONE) start_dt = datetime.now().astimezone(tz) end_dt = start_dt + timedelta(days=CALENDARS_DAYS_AHEAD) allevents = [] for cal in CALENDARS: logging.info("get calendar info") ical_string = urllib.request.urlopen(cal['url']).read() calendar = icalendar.Calendar.from_ical(ical_string) caldat = recurring_ical_events.of(calendar).between(start_dt, end_dt) #caldat = events(url=cal['url'], fix_apple=cal['apple'], start=start_dt, end=end_dt) logging.info("processing calendar info") for eventdat in caldat: if type(eventdat['DTSTART'].dt) is date: allevents = allevents + add_fullday_events( eventdat, cal['name']) else: allevents.append(add_timed_event(eventdat, cal['name'])) return allevents
def convert(self, ics_data): # parse ics file try: calendar = icalendar.Calendar.from_ical(ics_data) except: # there s an error, simply show the data string _LOGGER.debug(ics_data) return [] # calculate start- and end-date for recurring events start_date = datetime.datetime.now().replace( hour=0, minute=0, second=0, microsecond=0 ) if self._offset is not None: start_date -= datetime.timedelta(days=self._offset) end_date = start_date.replace(year=start_date.year + 1) events = recurring_ical_events.of(calendar).between(start_date, end_date) entries = [] for e in events: if e.name == "VEVENT": # calculate date dtstart = None if type(e.get("dtstart").dt) == datetime.date: dtstart = e.get("dtstart").dt elif type(e.get("dtstart").dt) == datetime.datetime: dtstart = e.get("dtstart").dt.date() if self._offset is not None: dtstart += datetime.timedelta(days=self._offset) # calculate waste type summary = str(e.get("summary")) if self._regex is not None: match = self._regex.match(summary) if match: summary = match.group(1) entries.append((dtstart, summary)) return entries
def __call__(self, fh, fh_w): try: cal = Calendar.from_ical(fh.read()) except ValueError as e: msg = "Parsing error: {}".format(e) raise IcalError(msg) now = datetime.now() start = now - timedelta(days=self.days) end = now + timedelta(days=self.days) events = recurring_ical_events.of(cal).between(start, end) for event in tqdm(events): summary = event["SUMMARY"] summary = summary.replace('\\,', ',') location = None if event.get("LOCATION", None): location = event['LOCATION'].replace('\\,', ',') if not any((summary, location)): summary = u"(No title)" else: summary += " - " + location if location and self.include_location else '' fh_w.write(u"* {}".format(summary)) fh_w.write(u"\n") if isinstance(event["DTSTART"].dt, datetime): fh_w.write(u" {}--{}\n".format( org_datetime(event["DTSTART"].dt, self.tz), org_datetime(event["DTEND"].dt, self.tz))) else: # all day event fh_w.write(u" {}--{}\n".format( org_date(event["DTSTART"].dt, timezone('UTC')), org_date(event["DTEND"].dt - timedelta(days=1), timezone('UTC')))) description = event.get("DESCRIPTION", None) if description: if bool(BeautifulSoup(description, "html.parser").find()): description = pypandoc.convert_text(description, "org", format="html") description = '\n'.join(description.split('\\n')) description = description.replace('\\,', ',') fh_w.write(u"{}\n".format(description)) fh_w.write(u"\n")
def open_cal(): if os.path.isfile(filename): if file_extension == 'ics': print("Extracting events from file:", filename, "\n") f = open(sys.argv[1], 'rb') gcal = Calendar.from_ical(f.read()) revents = recurring_ical_events.of(gcal).between(istart, istop) # for component in gcal.walk(): for component in revents: event = CalendarEvent("event") v = (dir(component).count('get') ) # Only proces data if object is a valid event if (v != 0): if component.get('TRANSP') == 'TRANSPARENT': continue #skip all day events and the like if component.get('SUMMARY') == None: continue #skip blank items event.summary = component.get('SUMMARY') event.uid = component.get('UID') if hasattr(component.get('dtstart'), 'dt'): event.start = component.get('dtstart').dt if hasattr(component.get('dtend'), 'dt'): event.end = component.get('dtend').dt event.url = component.get('URL') events.append(event) f.close() else: print("You entered ", filename, ". ") print(file_extension.upper(), " is not a valid file format. Looking for an ICS file.") exit(0) else: print("I can't find the file ", filename, ".") print( "Please enter an ics file located in the same folder as this script." ) exit(0)
def parse_events(self, cal_resource): """ :param ical_resource: :return: """ events = [] calendar = Calendar.from_ical(cal_resource) logging.info('Found the following events!') start_date = (2020,1,1) end_date = (2021,1,1) for event in recurring_ical_events.of(calendar).between(start_date, end_date): if event.name == "VEVENT": event_data = { "subject": event["summary"].to_ical().decode('utf-8'), "description": event["description"].to_ical().decode('utf-8'), "start_date": event["dtstart"].dt, "location": event["location"].to_ical().decode('utf-8') } logging.info(event_data) #print(event_data) events.append(event_data) self.schedule_events(events)
def events_from_ics(calendar_names, start_time, n): import recurring_ical_events import requests from icalendar import Calendar, Event event_objs=[] print(calendar_names) for name in calendar_names: r=requests.get(name) cal=Calendar.from_ical(r.content) calname=cal['X-WR-CALNAME'] print(f'Getting events for calendar with name {calname}...') events=recurring_ical_events.of(cal).between(start_time,datetime.datetime.now()) for event in events: start = event['DTSTART'].dt.isoformat() end = event['DTEND'].dt.isoformat() print(start, event['SUMMARY']) event_objs.append({'start': start, 'end': end, 'summary': event['SUMMARY'], 'calendar_name': calname, 'fullname': calname + '/' + event['SUMMARY']}) return event_objs
def get_calendar(self, content=content): return of(self.get_calendar(content))
import icalendar import recurring_ical_events import urllib.request start_date = (2019, 3, 5) end_date = (2019, 4, 1) url = "http://tinyurl.com/y24m3r8f" ical_string = urllib.request.urlopen(url).read() # https://stackoverflow.com/a/645318 calendar = icalendar.Calendar.from_ical(ical_string) events = recurring_ical_events.of(calendar).between(start_date, end_date) for event in events: start = event["DTSTART"].dt duration = event["DTEND"].dt - event["DTSTART"].dt print("start {} duration {}".format(start, duration)) # start 2019-03-18 04:00:00+01:00 duration 1:00:00 # start 2019-03-20 04:00:00+01:00 duration 1:00:00 # start 2019-03-19 04:00:00+01:00 duration 1:00:00 # start 2019-03-07 02:00:00+01:00 duration 1:00:00 # start 2019-03-08 01:00:00+01:00 duration 2:00:00 # start 2019-03-09 03:00:00+01:00 duration 0:30:00 # start 2019-03-10 duration 1 day, 0:00:00