Modeling Split-Duty Periods in Python
The exact task this guide solves is crediting a rest opportunity taken inside a flight duty period — a split duty — correctly, so the engine neither rejects a legal long day nor authorises one the break does not actually unlock. Under FAA §117.15 and the EASA split-duty provisions, a qualifying break in a suitable accommodation during the FDP can be partly excluded from the duty, extending the maximum permissible FDP. The subtlety is that only breaks meeting a minimum length and accommodation class qualify, and only part of the break credits back. A validator that inspects only the gaps between duties misses this entirely. This page models the split, computes the credit, and pins the qualifying boundaries. It extends the crew duty time taxonomy and feeds the extended ceiling to the duty time validation rule engines.
Prerequisites
- Python 3.11+ with timezone-aware
datetime. - A flight duty period with an in-duty break — the break start and end in UTC, plus the accommodation class.
- The split-duty rules seeded — the §117.15 minimum break and accommodation criteria and the EASA equivalents.
- The base FDP ceiling resolved from Table B before any split-duty extension.
What qualifies as a split duty
Not every mid-duty pause is a split duty. To qualify under §117.15, the break must be a scheduled rest opportunity of at least a minimum length, taken in a suitable accommodation, and the crew must be relieved of all duties during it. When those conditions hold, a portion of the break is excluded from the FDP, so the duty may run longer in wall-clock terms while the counted FDP stays within its ceiling. A break that is too short, or taken in a seat rather than an accommodation, is ordinary duty time and extends nothing. The classifier’s job is to test the qualifying conditions and, when they pass, compute the credited exclusion.
Step 1 — Model the break and its qualifying test
The break carries its length and accommodation class; the rule carries the minimum length and the set of qualifying classes. Testing eligibility from data keeps the rule auditable and jurisdiction-swappable.
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
class Accommodation(Enum):
NONE = "none" # a seat — never qualifies
QUIET_ROOM = "quiet_room" # suitable accommodation
FLAT_BUNK = "flat_bunk" # suitable accommodation, highest class
@dataclass(frozen=True)
class SplitRule:
min_break_minutes: int # e.g. 180 under §117.15
qualifying: frozenset[Accommodation]
credit_ratio: float # fraction of the break excluded from FDP
FAA_SPLIT = SplitRule(
min_break_minutes=180,
qualifying=frozenset({Accommodation.QUIET_ROOM, Accommodation.FLAT_BUNK}),
credit_ratio=0.5,
)
@dataclass(frozen=True)
class InDutyBreak:
start_utc: datetime
end_utc: datetime
accommodation: Accommodation
@property
def minutes(self) -> float:
return (self.end_utc - self.start_utc).total_seconds() / 60.0
Verify: a 200-minute break in a QUIET_ROOM clears both conditions of FAA_SPLIT; a 200-minute break in a NONE seat fails the accommodation test; a 120-minute break in a FLAT_BUNK fails the minimum-length test.
Step 2 — Compute the credited exclusion and extended ceiling
When the break qualifies, a fraction of it is excluded from the FDP. The extended ceiling is the base Table B limit plus the credited exclusion, so the crew may fly a longer wall-clock day while the counted FDP obeys the original limit.
def split_duty_credit(brk: InDutyBreak, rule: SplitRule) -> float:
"""Minutes of the break excluded from the FDP, or 0 if it does not qualify."""
qualifies = brk.minutes >= rule.min_break_minutes and brk.accommodation in rule.qualifying
return brk.minutes * rule.credit_ratio if qualifies else 0.0
def extended_fdp_ceiling(base_ceiling_minutes: int, brk: InDutyBreak, rule: SplitRule) -> int:
return base_ceiling_minutes + int(split_duty_credit(brk, rule))
Verify: with a base ceiling of 720 minutes and a qualifying 200-minute break at a 0.5 credit ratio, extended_fdp_ceiling(720, brk, FAA_SPLIT) returns 820 — the 100-minute credited exclusion lifts the ceiling; a non-qualifying break leaves it at 720.
Step 3 — Count the FDP against the extended ceiling
The counted FDP is the wall-clock duty minus the credited exclusion. Comparing it against the base ceiling — not the extended one — keeps the arithmetic honest: the exclusion is subtracted from the duty, not added to the limit, and the two framings must agree.
def counted_fdp(duty_minutes: float, brk: InDutyBreak, rule: SplitRule) -> float:
"""Wall-clock duty minus the credited in-duty rest."""
return duty_minutes - split_duty_credit(brk, rule)
def is_compliant(duty_minutes: float, base_ceiling: int, brk: InDutyBreak, rule: SplitRule) -> bool:
return counted_fdp(duty_minutes, brk, rule) <= base_ceiling
Failure modes and troubleshooting
- Crediting a seat as a break. A rest taken in a passenger seat is not a suitable accommodation and credits nothing. Remediation: gate the credit on the accommodation class, never on break length alone.
- Adding the whole break to the ceiling. Only a fraction of the break is credited; excluding the entire break overstates the extension. Remediation: apply the seeded
credit_ratio. - Double-counting the extension. Adding the credit to the ceiling and subtracting it from the duty double-counts it; the two framings are equivalent, not additive. Remediation: pick one framing and assert the other agrees.
- Below-minimum break treated as split. A break shorter than the minimum is ordinary duty; treating it as a split extends an FDP illegally. Remediation: enforce the minimum-length test first.
- Break outside the FDP. A rest before the FDP begins is pre-duty rest, not a split; only breaks bounded by flight segments qualify. Remediation: confirm the break falls between two segments of the same FDP.
Frequently Asked Questions
How much of a split-duty break credits back?
A fraction defined by the rule — commonly half of the qualifying break under the FAA provision. The credited portion is excluded from the counted flight duty period, which is what permits the longer wall-clock day.
Does the break need to be scheduled in advance?
Yes. A qualifying split-duty rest is a scheduled opportunity in a suitable accommodation with the crew relieved of all duties. An unscheduled ground delay in the aircraft is not a split-duty break and credits nothing.
Can any accommodation qualify?
Only classes the regulation recognises as suitable — a quiet room or a flat bunk, not a passenger seat. The accommodation class is part of the qualifying test, alongside the minimum length.
How does split duty interact with the base Table B ceiling?
Table B sets the base ceiling; the split-duty credit extends how long the wall-clock duty may run while keeping the counted FDP within that base ceiling. The two are resolved in sequence: base ceiling first, then the credited extension.
Related
- Crew Duty Time Taxonomy Mapping — the parent section and shared definitions.
- Classifying Standby and Reserve Duty in Python — the sibling case of crediting non-flying time.
- FAA Part 117 Table B FDP Limits as Queryable Data — the base ceiling the split extends.
- Rest Period Compliance Checks — how in-duty rest interacts with between-duty rest.
Back to Crew Duty Time Taxonomy Mapping.