Skip to content

Instantly share code, notes, and snippets.

@michaeluhl
Created August 30, 2026 15:04
Show Gist options
  • Select an option

  • Save michaeluhl/ab2655429999bee0706da2a555ea76a3 to your computer and use it in GitHub Desktop.

Select an option

Save michaeluhl/ab2655429999bee0706da2a555ea76a3 to your computer and use it in GitHub Desktop.
Small Script to Filter iCal Events by Date
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "icalendar>=7.2.2",
# "requests>=2.34.2"
# ]
# ///
from argparse import ArgumentParser, Namespace
from contextlib import AbstractContextManager, nullcontext
import datetime as dt
from io import StringIO
from pathlib import Path
from sys import stdin, stdout
from zoneinfo import ZoneInfo
import icalendar
import requests
def opener(uri: str) -> AbstractContextManager:
if uri == "-":
return nullcontext(stdin)
url = requests.utils.urlparse(uri)
if url.scheme in ("http", "https"):
return StringIO(requests.get(uri).content.decode())
elif url.scheme == "file":
return Path.from_uri(uri).open("rt")
else:
return Path(uri).open("rt")
def ensure_datetime(input: dt.date | dt.datetime) -> dt.datetime:
return (
input
if isinstance(input, dt.datetime)
else dt.datetime.combine(input, time=dt.time(tzinfo=ZoneInfo("UTC")))
)
def update_zones(options: Namespace):
z = (
ZoneInfo(options.timezone)
if options.timezone
else dt.datetime.now().astimezone().tzinfo
)
if options.start_after:
options.start_after = options.start_after.replace(tzinfo=z)
if options.start_before:
options.start_before = options.start_before.replace(tzinfo=z)
def main(options: Namespace) -> None:
update_zones(options)
with opener(options.SOURCE) as input:
cal: icalendar.Calendar = icalendar.Calendar.from_ical(input.read())
filtered: icalendar.Calendar = icalendar.Calendar.new()
for event in cal.events:
if (
options.start_after
and ensure_datetime(event.start) < options.start_after
):
continue
if (
options.start_before
and ensure_datetime(event.start) > options.start_before
):
continue
filtered.add_component(event)
stdout.write(filtered.to_ical().decode("UTF8"))
if __name__ == "__main__":
parser = ArgumentParser(description="A script to filter iCalendar events")
parser.add_argument("SOURCE", type=str, help="Source (a file or URL)")
parser.add_argument(
"-a",
"--start-after",
type=dt.datetime.fromisoformat,
default=None,
help="Include only events with start dates after the specified value (ISO 8601 format)",
)
parser.add_argument(
"-b",
"--start-before",
type=dt.datetime.fromisoformat,
default=None,
help="Include only events with start dates before the specified value (ISO 8601 format)",
)
parser.add_argument(
"-z",
"--timezone",
type=str,
default=None,
help="Timezone to use for date comparisons, defaults to local",
)
main(parser.parse_args())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment