EASA FTL Compliance Frameworks
Modern flight operations and crew scheduling systems cannot rely on static spreadsheets or hardcoded thresholds when managing European Union Aviation Safety Agency Flight Time Limitations. An effective EASA FTL compliance framework requires a modular, constraint-driven architecture that translates regulatory text into deterministic, executable logic. This work sits inside the broader Core Architecture & Regulatory Mapping domain and is the European counterpart to the FAA Part 117 rule schema design that governs US carriers. By treating Subpart FTL provisions as versioned rule sets rather than immutable business constants, scheduling platforms gain the agility required to adapt to AMC/GM updates, operator-specific ORO.FTL.110 deviations, and cross-jurisdiction harmonisation efforts.
The Compliance Challenge This Framework Solves
The scoped problem is narrow but unforgiving: given a proposed roster and each crew member’s recent duty history, decide — deterministically and in milliseconds — whether every flight duty period, rest period, and cumulative window conforms to Regulation (EU) No 965/2012, Annex III, Subpart FTL. Unlike a simple daily cap, EASA FTL is stateful. The maximum flight duty period (FDP) permitted for a single report is a function of the crew member’s acclimatisation state, the local report time, and the number of sectors flown, while the legality of that same duty also depends on rolling totals stretching back a full year. A pairing that looks legal in isolation can be illegal because of what the crew member did six days ago.
Three properties make this hard to encode. First, the reference time that keys the FDP table shifts as crew cross time zones, so the “same” 06:00 report can carry a 13-hour ceiling for an acclimatised pilot and a reduced ceiling for one whose acclimatisation state is unknown. Second, the limits are layered: a duty can satisfy the daily FDP table under ORO.FTL.205 yet breach a cumulative cap under ORO.FTL.210. Third, rest requirements under ORO.FTL.235 are relative to the preceding duty, so every verdict is path-dependent. A framework that hopes to pass an authority inspection must model all three as first-class data, not as branches buried in a scheduling loop.
Schema and Data Structure Design
The framework’s data model separates three concerns that operators routinely conflate: raw operational events, derived duty structures, and the versioned regulatory parameters that evaluate them. Keeping regulatory constants in their own effective-dated table is what allows an AMC/GM revision to be diffed and rolled back without touching a line of scheduling code.
The core entities are the crew_member and their time-varying acclimatisation_state; the duty_period bounded by rest; the flight_duty_period (the FDP subset that the ORO.FTL.205 table constrains); the sector rows that carry block-out and block-on instants; the rest_period that gates the next duty; and the effective-dated ftl_ruleset that supplies FDP tables, cumulative caps, and rest minima keyed by revision date. Every temporal column is stored in UTC with an explicit originating zone as metadata, so local report time is derived at evaluation, never stored ambiguously.
Field naming, unit standardisation, and audit lineage across the scheduling, payroll, and compliance modules all follow the shared crew duty time taxonomy mapping. Anchoring the schema to that taxonomy is what prevents a rule engine from misclassifying a positioning segment as a sector, or a standby callout as the start of an FDP — the two most common sources of false violations.
Regulatory Mapping
Every schema decision above is driven by a specific Subpart FTL provision. The mapping below is the ground truth the evaluator encodes; the authoritative wording lives in the EASA Air Operations Regulation (Subpart FTL).
- ORO.FTL.205 — Flight duty period. Sets the maximum basic daily FDP. For an acclimatised crew, CS FTL.1.205 Table 2 grants up to 13:00 hours for a report in the favourable window (roughly 06:00–13:29 local) flying one to two sectors, reduced by 30 minutes per additional sector down to a floor of 9:00 hours, and reduced further for reports at night. This is the table the
flight_duty_periodentity is measured against. - ORO.FTL.105 — Definitions (acclimatisation). Establishes the acclimatised / not-acclimatised / unknown states. The associated AMC table selects the reference time from elapsed time since reporting and the number of time zones crossed, which is why
acclimatisation_statemust be a time-varying entity rather than a static crew attribute. - ORO.FTL.210 — Flight times and duty periods (cumulative). Caps total duty at 60 hours in any 7 consecutive days, 110 hours in any 14 days, and 190 hours in any 28 days; and total flight time at 100 hours in any 28 days, 900 hours in any calendar year, and 1000 hours in any 12 consecutive calendar months. These six rolling windows are the reason the schema stores segment-level rows, not daily totals.
- ORO.FTL.220 — Split duty and ORO.FTL.225 — Standby. Govern how a break within a duty may extend the FDP, and how airport/other standby counts toward duty. Both alter which sectors belong to the constraining FDP.
- ORO.FTL.235 — Rest periods. Requires a minimum rest before an FDP of at least the length of the preceding duty period, subject to a floor of 12 hours at home base and 10 hours away from base. The
rest_periodentity is validated against the duty that precedes it, never in isolation. - ORO.FTL.110 — Operator responsibilities. Allows operator-specific variations approved by the authority, which is why ruleset rows are keyed by operator as well as effective date.
The precise conditions that flip a duty between the reduced and standard ceilings — standby timing, sector count, and acclimatised windows — are worked through in EASA FTL 8 vs 10 hour duty periods explained.
Constraint-Driven Rule Engine
Rule engine implementation begins with a formalised constraint model. EASA FTL provisions map naturally to temporal state machines and interval algebra, where duty periods, FDPs, rest requirements, and split-duty configurations are evaluated as overlapping or sequential time blocks. The engineering challenge lies in translating regulatory nuance into deterministic evaluation paths: the distinction between a standard and an extended FDP hinges on acclimatisation state, sector count, and local report time, all of which must be normalised into a unified temporal schema before evaluation.
Rather than applying post-hoc validation, the framework embeds constraint evaluation directly into the pairing optimisation loop, acting as a deterministic gatekeeper during schedule generation and disruption recovery.
Python Implementation Walkthrough
Production implementations express regulatory parameters as typed models so that a malformed payload fails at the boundary rather than deep inside the evaluator. Using pydantic for the regulatory contract guarantees that FDP ceilings, sector counts, and acclimatisation states are validated before they reach the constraint solver. The model below captures the inputs the ORO.FTL.205 lookup needs.
from datetime import datetime, timezone
from enum import Enum
from pydantic import BaseModel, field_validator
class AcclimatisationState(str, Enum):
ACCLIMATISED = "B" # to home base reference time
LOCAL = "D" # to local departure time
UNKNOWN = "X" # state cannot be determined
class FlightDutyPeriod(BaseModel):
crew_id: str
report_time_utc: datetime
report_zone: str # e.g. "Europe/Amsterdam"
sectors: int
state: AcclimatisationState
@field_validator("report_time_utc")
@classmethod
def must_be_utc(cls, v: datetime) -> datetime:
if v.tzinfo is None or v.utcoffset() != timezone.utc.utcoffset(None):
raise ValueError("report_time_utc must be timezone-aware UTC")
return v
The FDP ceiling is a pure function of the local report hour, the sector count, and the acclimatisation state. Keeping it pure is what makes every verdict reproducible from the audit log months later. The reference time and table follow CS FTL.1.205; timezone handling relies on the standard library zoneinfo and datetime modules documented in the Python datetime documentation.
from datetime import timedelta
from zoneinfo import ZoneInfo
# CS FTL.1.205 Table 2, acclimatised, banded by local report hour.
# Values are the 1-2 sector ceiling in minutes; each extra sector -30, floor 540.
_BASE_CEILING = {range(6, 14): 780, range(14, 18): 750, range(18, 22): 690}
def max_fdp_minutes(fdp: FlightDutyPeriod) -> int:
local = fdp.report_time_utc.astimezone(ZoneInfo(fdp.report_zone))
base = 660 # conservative night-band default
for band, ceiling in _BASE_CEILING.items():
if local.hour in band:
base = ceiling
break
reduced = base - max(0, fdp.sectors - 2) * 30
ceiling = max(reduced, 540) # 9-hour floor
if fdp.state is AcclimatisationState.UNKNOWN:
ceiling = min(ceiling, 660) # unknown-state reduction
return ceiling
An FDP is legal when its actual length does not exceed this ceiling. Split-duty extensions under ORO.FTL.220 and standby uplift under ORO.FTL.225 are applied as adjustments to ceiling before the comparison, keeping each regulatory provision an isolated, testable term. The compliance verdict returned to the optimiser carries structured violation metadata so schedulers resolve conflicts algorithmically rather than auditing pairings by hand.
Rolling Window and Temporal Aggregation
The daily FDP check is stateless, but ORO.FTL.210 is not: it requires rolling totals over 7, 14, and 28 days for duty, and 28-day, calendar-year, and 12-month windows for flight time. Timezone drift is the main source of cumulative error, so every input is stamped in ISO 8601 UTC before it enters the aggregation layer. Where the data lives in PostgreSQL, frame-bounded window functions express these caps directly; see the PostgreSQL window functions reference for the frame semantics.
-- Rolling 7-day duty total per crew member, evaluated at each duty start.
SELECT
crew_id,
duty_start_utc,
SUM(duty_minutes) OVER (
PARTITION BY crew_id
ORDER BY duty_start_utc
RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW
) AS rolling_7d_minutes
FROM duty_period
ORDER BY crew_id, duty_start_utc;
For pre-flight validation inside the optimiser, the same logic runs in memory over Polars or Pandas frames, where a rolling group-by keyed on the duty timestamp reproduces the SQL frame without a database round trip. Whichever engine is used, the window boundaries must be inclusive of the exact regulatory span — an off-by-one on the 28-day boundary silently under-counts duty and lets an illegal roster through.
Integration Points
This framework is one stage in a longer chain and depends on clean contracts with its neighbours:
- Upstream — flight data ingestion. Roster data, aircraft assignments, and operational disruptions arrive through an event-driven pipeline that publishes state changes to a compliance evaluation queue. Each event triggers recalculation of the affected crew member’s cumulative windows. Timestamps that arrive without a zone are rejected at this boundary, not coerced.
- Vocabulary — crew duty time taxonomy mapping. Supplies the canonical field names and unit conventions so sectors, positioning, standby, and rest are classified identically everywhere.
- Downstream — duty time validation rule engines. Consume the normalised, classified events this framework produces and run the shared evaluation contract across jurisdictions.
- Perimeter — system security and access boundaries. Compliance endpoints sit behind role-based access controls so only authorised dispatchers, compliance officers, and automated schedulers can override an FTL parameter, and every evaluation emits a cryptographically signed audit record for non-repudiation. When primary evaluation services degrade, a synchronised local cache of the latest approved ORO.FTL baseline allows offline validation; queued evaluations reconcile with the central state machine once connectivity returns, preventing compliance gaps during network partitions.
Testing and Edge Cases
Because FTL logic is stateful and time-sensitive, example-based tests miss the cases that matter. Property-based testing with hypothesis generates thousands of synthetic rosters and asserts invariants — for instance, that no accepted FDP ever exceeds its ORO.FTL.205 ceiling, and that no accepted rest period is shorter than the preceding duty subject to the ORO.FTL.235 floor. The boundary conditions that most often break EASA implementations are specific:
- Daylight-saving transitions. A duty spanning a DST change in
report_zoneshifts local report hour relative to UTC; the FDP band must be selected from the local wall-clock time at report, not from a fixed UTC offset. - Acclimatisation reset. After crossing enough time zones, the reference time moves from home base to local, changing the FDP ceiling for an otherwise identical report; tests must cover the transition, not just the endpoints.
- 28-day window edges. A duty landing exactly on the rolling-window boundary must be counted inclusively; property tests should assert the total is invariant to whether the boundary duty is expressed in local or UTC time.
Predicate logic is validated against the latest published AMC/GM interpretations before every deployment, with the regulatory constants version-controlled so a rule revision is a reviewable diff rather than a code change.
Explore This Topic in Depth
- EASA FTL 8 vs 10 hour duty periods explained — the exact regulatory triggers, standby and sector edge cases, and a validation routine that maps a duty configuration to the correct reduced or standard FDP ceiling.
Related
- FAA Part 117 rule schema design — the US-jurisdiction counterpart for dual-registry carriers.
- Crew duty time taxonomy mapping — the shared vocabulary this framework classifies events against.
- System security and access boundaries — RBAC and signed audit trails for compliance endpoints.
- Flight data ingestion and system sync — the upstream pipeline that feeds UTC-normalised events.
- Duty time validation rule engines — the downstream evaluators that consume this framework’s output.
Back to Core Architecture & Regulatory Mapping.