Classifying Standby and Reserve Duty in Python

The exact task this guide solves is deciding, for a block of standby or reserve time, how much of it counts as duty, whether it contributes to the flight duty period, and whether any of it counts as rest. This is the classification that most often over- or under-credits a crew member, because “reserve” is not one thing: airport standby is fully duty, short-call reserve partially erodes the subsequent FDP, and long-call reserve at home can count as rest until the crew is called. Getting the mapping wrong either rejects a legal pairing or, worse, lets an illegal one through. This page builds a classifier over the shared crew duty time taxonomy, maps each category to its §117.21 and EASA treatment, and pins the boundaries. Its output feeds the duty time validation rule engines and depends on clean events from flight data ingestion.

Prerequisites

The categories and their treatment

Four categories cover almost all real rosters, and each maps to a distinct treatment. Airport standby is duty in full and counts toward the FDP the moment it begins. Short-call reserve — the crew can be called with little notice — is not fully duty, but the reserve period reduces the maximum FDP available once a call arrives. Long-call reserve with adequate notice can count as rest until the call, provided a protected rest opportunity is preserved. Home reserve below the notice threshold behaves like long-call. The classifier’s job is to turn a raw standby block plus its facility and notice metadata into the correct trio of durations: duty minutes, FDP contribution, and rest credit.

Step 1 — Model the categories and their rules

Each category carries three booleans-in-effect: does it count as duty, does it contribute to FDP, and can it be credited as rest. Expressing them as data rather than branches keeps the classifier auditable.

from dataclasses import dataclass
from enum import Enum


class StandbyKind(Enum):
    AIRPORT = "airport_standby"
    SHORT_CALL = "short_call_reserve"
    LONG_CALL = "long_call_reserve"
    HOME_RESERVE = "home_reserve"


@dataclass(frozen=True)
class StandbyRule:
    counts_as_duty: bool
    contributes_to_fdp: bool
    rest_creditable: bool
    fdp_reduction_ratio: float   # fraction of standby that erodes later FDP


STANDBY_RULES = {
    StandbyKind.AIRPORT:      StandbyRule(True,  True,  False, 1.0),
    StandbyKind.SHORT_CALL:   StandbyRule(True,  False, False, 0.25),
    StandbyKind.LONG_CALL:    StandbyRule(False, False, True,  0.0),
    StandbyKind.HOME_RESERVE: StandbyRule(False, False, True,  0.0),
}

Verify: STANDBY_RULES[StandbyKind.AIRPORT].contributes_to_fdp is True and STANDBY_RULES[StandbyKind.LONG_CALL].rest_creditable is True — the two extremes of the spectrum.

Step 2 — Classify a block into its durations

The classifier takes a standby block and, given the applicable rule, returns the duty, FDP-contribution, and rest-credit minutes. Airport standby contributes its full length to the FDP; short-call reserve reduces the later FDP by a fraction of the reserve served; long-call reserve credits its length as rest until a call arrives.

from datetime import datetime


@dataclass(frozen=True)
class StandbyBlock:
    kind: StandbyKind
    start_utc: datetime
    end_utc: datetime
    facility_is_accommodation: bool


def classify(block: StandbyBlock) -> dict:
    rule = STANDBY_RULES[block.kind]
    minutes = (block.end_utc - block.start_utc).total_seconds() / 60.0
    return {
        "kind": block.kind.value,
        "duty_minutes": minutes if rule.counts_as_duty else 0.0,
        "fdp_contribution_minutes": minutes if rule.contributes_to_fdp else 0.0,
        "fdp_reduction_minutes": minutes * rule.fdp_reduction_ratio,
        "rest_credit_minutes": (
            minutes if rule.rest_creditable and block.facility_is_accommodation else 0.0
        ),
    }

Verify: an eight-hour airport standby returns 480 duty minutes and 480 FDP-contribution minutes; an eight-hour long-call reserve at a suitable accommodation returns 0 duty minutes and 480 rest-credit minutes; the same long-call block not at an accommodation credits no rest.

Step 3 — Apply the FDP reduction when a call arrives

Short-call reserve does not add to the FDP directly, but when the crew is called out, a fraction of the reserve already served erodes the FDP they can then fly. Applying that reduction at the call event keeps the subsequent duty limit correct.

def reduced_fdp_limit(base_fdp_minutes: int, reserve_result: dict) -> int:
    """Erode the base FDP ceiling by the reserve reduction, never below zero."""
    return max(0, int(base_fdp_minutes - reserve_result["fdp_reduction_minutes"]))
Standby and reserve classification matrix Four standby categories mapped across three treatments. Airport standby counts as duty and contributes to the flight duty period. Short-call reserve counts as duty and erodes the later FDP. Long-call reserve and home reserve count as neither duty nor FDP and can be credited as rest when served at a suitable accommodation. Duty FDP Rest credit Airport standby Short-call reserve Long-call reserve Home reserve YesYes Yeserodes Yes* Yes* * rest credit requires a suitable accommodation
The classification spectrum: airport standby is fully duty, short-call reserve erodes the later FDP, and long-call or home reserve credits rest only when a suitable accommodation is provided.

Failure modes and troubleshooting

Frequently Asked Questions

Is all reserve time duty time?

No. Airport standby is duty in full, and short-call reserve counts as duty while eroding later FDP, but long-call and home reserve generally are not duty and can be credited as rest until the crew is called, provided the rest conditions are met.

Why does short-call reserve reduce the FDP instead of adding to it?

Because the crew has been on call and partially fatigued without flying. The regulation reflects this by shrinking the duty they may then perform, rather than counting the reserve as flown duty. The reduction is a fraction of the reserve served.

What makes standby count as rest?

Two conditions: the category must be one the regulation allows to be rest (long-call or home reserve), and it must be served at a suitable accommodation with a protected, uninterrupted rest opportunity. Airport standby never qualifies.

How does a call-out split a reserve block?

The reserve time before the call is classified as reserve, and the assignment after the call is a normal flight duty period whose ceiling has been reduced by the reserve served. The call event is the split point, which is why the ingestion feed must carry it.

Back to Crew Duty Time Taxonomy Mapping.