Notamify NOTAM Schedules & RRULE Parsing

This article expands on how Notamify represents and parses NOTAM schedules and how you can use them for client-side time filtering. It includes fully working Python and JavaScript examples.

TL;DR

  • Notamify exposes schedule recurrences using a standards-compliant RFC 5545 RRULE string plus a separate duration_hrs.

  • Duration is not encoded in the RRULE. We keep duration in duration_hrs to avoid non-standard RRULEs and to properly handle windows that cross midnight (e.g., 20:00–04:00).

  • Each schedule RRULE produces a list of start datetimes; you derive the end by start + duration_hrs.

  • Always intersect schedule windows with the NOTAM’s overall validity [starts_at, ends_at].

Data Model

Within each NOTAM, schedules live at notam.interpretation.schedules[]:

{
  "description": "On April 4-6, 11-13, 18-20, 25-27 from 20:00 to 04:00.",
  "duration_hrs": 8,
  "is_sunrise_sunset": false,
  "rrule": "DTSTART:20250404T200000Z\nRRULE:FREQ=DAILY;UNTIL=20250627T040000Z;BYDAY=TH,FR,SA,SU,MO,TU,WE;COUNT=15",
  "source": "APR 04-06 11-13 18-20 25-27 2000-0400"
}

Contract & invariants

  • rrule (string): An iCalendar RRULE string that includes a DTSTART line in UTC (ending with Z). RRULEs are bounded (via COUNT or UNTIL).

  • duration_hrs (number): The event window length in hours for each occurrence generated by the RRULE.

  • is_sunrise_sunset (boolean): If true, duration/instants derive from astronomical times (out of scope here).

  • description/source: Human-readable; do not parse from these.

Note: The NOTAM object also carries starts_at and ends_at—the overall validity window. Schedule windows must be intersected with this validity.

Client-Side Usage Pattern

  1. Parse the RRULE to get start datetimes.

  2. Compute end = start + duration_hrs.

  3. Intersect each [start, end) with:

    • The NOTAM validity [starts_at, ends_at), and

    • Any query window you care about (e.g., “show what’s active on 2025-05-10”).

When possible, prefer windowed enumeration (between(...)) to avoid expanding very long recurrences.


Python (dateutil)

Key callouts

  • rule = rrulestr(rrule_str) is the only dateutil-specific line you need to parse both DTSTART and RRULE.

  • Use rule.between(start, end, inc=True) whenever you can bound expansion.


JavaScript/TypeScript (rrule)

IMPORTANT: Use rrulestr from the rrule package. Other RRULE string parsing mechanism from the package might not work as expected.

Quick Recipe: “Is this NOTAM active at T?”

Python

JavaScript/TS


Summary

  • RRULE gives you when each occurrence starts; duration_hrs tells you how long it runs.

  • Expand, add duration, and intersect with the NOTAM’s validity (and your query).

  • Use dateutil (rrulestr) in Python and rrule (rrulestr) in JavaScript for robust, standards-compliant parsing.

Last updated