← Methods repository
concept

Grace Period and Permissible Gap Rules

The pre-specified maximum allowable gap between consecutive dispensings (or administrations/visits) before exposure is considered discontinued and an exposure episode is closed, determining persistence, on-treatment person-time, and exposure misclassification.

Exposure_Definitionexposure_definitiongrace-periodpermissible-gaptreatment-episode-constructionpersistencedays-supplyexposure-misclassificationstockpiling
Methods reference only. Use primary source citations and local policy before applying this in a study protocol, regulatory submission, payer dossier, or clinical decision.

In plain language

A grace period (also called a permissible gap) is a pre-set number of days that a researcher allows to pass between the end of one prescription fill and the start of the next before deciding that a patient has actually stopped taking the drug. When a gap between fills is shorter than the grace period, the treatment episode stays open — the interruption is treated as a brief delay at the pharmacy, not a real stop. When the gap is longer than the grace period, the episode is closed and the patient is recorded as having discontinued treatment at the point their last supply ran out. Choosing the grace period is one of the most consequential decisions in any study that tracks how long patients stay on a drug, because a shorter window labels more patients as quitters while a longer window can make poor adherers look persistent.

A grace period (permissible gap) rule is the operational decision that converts a sequence of discrete dispensing records into continuous exposure episodes. Each fill confers a covered window of length `days_supply` starting at `fill_date`. When the next fill arrives before that window expires (allowing for stockpiling) the episode continues; when the gap between the run-out date of one fill and the start of the next exceeds the grace period, the episode is closed and the patient is classified as having discontinued at the run-out date (often plus a fraction of the grace period). The grace period is therefore not a nuisance parameter — it simultaneously defines who is persistent, how much on-treatment person-time each patient contributes, and where exposure is misclassified. Gardarsdottir et al. showed empirically that the number, length, and count of constructed treatment episodes is directly governed by the gap length chosen, which is why the rule must be specified in the protocol and varied in sensitivity analysis rather than left to a default.

Core conceptual distinction

The grace period is distinct from, but interacts with, three neighboring constructs. (1) Grace period vs days_supply / stockpiling rule: `days_supply` sets the nominal covered window; the stockpiling (carry-over) rule decides whether early refills bank surplus supply forward; the grace period is the slack added after the (possibly carried-over) supply runs out before discontinuation is declared. (2) Permissible gap vs episode-gap (new-episode) threshold: a short grace period bridges within-episode interruptions; a separate, usually longer, threshold governs whether a return after a long gap starts a new episode (re-initiation/rechallenge) versus continuing the old one. (3) Grace period as exposure-window definition vs as a censoring/lag device: in an as-treated analysis the grace period extends the on-treatment window (and thus attributes more outcomes to the drug); in an intention-to-treat/first-line analysis the grace period mainly affects the persistence endpoint, not outcome attribution. The estimand must state which. The fundamental trade-off is bias-directional: too short a grace period creates spurious discontinuations and gaps (artifactual non-persistence, and immortal-time/exposure misclassification if outcomes occurring during the "gap" are treated as unexposed), while too long a grace period carries non-adherent person-time as "exposed," diluting both benefit and harm toward the null and inflating apparent persistence.

Pros, cons, and trade-offs

(vs the specific alternatives): - vs a fixed default gap (e.g., 30 days for everyone): A drug-class-calibrated, refill-pattern-informed grace period (e.g., 50% of typical `days_supply`, or an empirically derived gap distribution) reduces differential misclassification across drugs with different refill cadence (90-day mail-order vs 30-day retail) and different half-lives. Cost: more programming and a defensible justification per drug. Prefer a calibrated rule for any comparative or regulatory study where two arms have different supply patterns. - vs no grace period (strict run-out = discontinuation): A grace period prevents counting routine refill timing variation (weekend pickups, mail-order lag, sample/free supply) as discontinuation. Cost: it lengthens on-treatment person-time and can mask true early stopping. Prefer a grace period in essentially all persistence and as-treated analyses; prefer strict run-out only for conservative safety signal detection where over-attributing exposure is the worse error. - vs an as-treated analysis with informative-censoring weights: A grace period plus simple censoring at episode end is transparent and defensible but ignores that discontinuation is rarely random. Cost: differential discontinuation by arm biases naive as-treated estimates. Prefer adding inverse-probability-of-censoring weights (or an ITT/first-line contrast) when discontinuation is common and arm-dependent; the grace period alone does not fix informative censoring.

When to use

. Whenever discrete fills/administrations must be assembled into exposure episodes: persistence and time-to-discontinuation endpoints; PDC/MPR denominators that depend on episode boundaries; as-treated and per-protocol exposure windows in comparative effectiveness/safety; defining "current use" for self-controlled or nested case-control designs; and any utilization/cost analysis where treatment-episode length drives the outcome. Specify the grace period, the stockpiling rule, and the new-episode threshold together, and pre-register at least one alternative for sensitivity.

When NOT to use — and when it is actively misleading or dangerous

. - Do not let a single grace period span arms with different refill cadence (e.g., a once-daily oral with 90-day mail-order vs a titrated agent with 30-day retail fills). A common absolute gap (say 30 days) is lenient for the 30-day drug and strict for the 90-day drug, producing differential exposure misclassification that biases the comparison in an unpredictable direction. Calibrate per drug or express the gap as a proportion of `days_supply`. - Do not apply a refill-based grace period to drugs not captured as outpatient pharmacy claims. Clinician-administered infusions/injectables (J-codes on the medical claim), inpatient-dispensed drugs, and 340B/white-bagged products have no `days_supply`; a pharmacy grace-period rule silently classifies all such person-time as unexposed. - Do not treat outcomes occurring inside the grace period as unexposed in an as-treated analysis — that is a recipe for immortal-time-style misclassification. The grace-period window must be classified as exposed (or the analysis must use a time-varying exposure with an explicit lag), not retro-assigned based on whether a refill eventually appeared. - Do not use a long grace period when the question is early discontinuation or first-fill-only (primary non-adherence). A long grace masks exactly the signal of interest.

Data-source operational depth

. - Claims (FFS): The native substrate — `person_id`, `fill_date`, `days_supply`, NDC. Require continuous medical + pharmacy enrollment across the episode so an apparent gap is a true treatment gap, not unobserved person-time. Failure modes: Medicare Advantage (MA-only) person-time lacks adjudicated FFS pharmacy claims, so a "gap" can be pure missingness — restrict to Part D / commercial pharmacy-benefit enrollment and exclude MA-only spans before applying the rule. 90-day mail-order and 100-day supplies inflate `days_supply`; samples, $4 generics, and cash purchases are invisible and manufacture false gaps; same-day duplicate/reversed claims and partial fills distort run-out dates (de-duplicate and net out reversals first); claims adjudication lag at the end of the data window truncates the last episode. - EHR: Exposure is the order or medication-administration record, not a dispensing, so a true grace period is hard to define — an active order may persist long after the patient stopped taking the drug, and external-care leakage (fills at pharmacies outside the system) creates phantom gaps. Prefer linkage to pharmacy claims to anchor `days_supply`; if relying on orders, treat the gap rule as an order-duration assumption and report its sensitivity. - Registry / linked: Registries adjudicate persistence/discontinuation clinically but rarely capture every fill; link to claims for the dispensing history and to a death index so that the last fill before death is not misread as discontinuation. In elderly claims cohorts, death is a competing risk for discontinuation: if one arm has higher mortality, naive persistence comparisons are biased unless death is handled (competing-risks or censoring at death with a stated estimand). Linkage adds selection (linkable subset only) and order/fill/service-date discrepancies that must be reconciled before episodes are built.

Worked claims example

Question: 12-month persistence on a once-daily oral DPP-4 inhibitor in a commercial + Medicare FFS database, with the grace period as the key tuning parameter. (1) Eligibility: ≥365 days continuous medical + pharmacy (Part A/B/D or commercial) enrollment before the first fill; exclude MA-only person-time so gaps are observed. (2) Index = first DPP-4 fill (`fill_date`); de-duplicate same-day claims and net out reversals so each fill has one clean `days_supply`. (3) Build the covered window: each fill covers `[fill_date, fill_date + days_supply)`; apply a stockpiling rule that shifts a refill's start to the prior run-out date when it arrives early (cap carry-over at, e.g., 30 days to avoid unbounded banking). (4) Apply the grace period: walk fills in order; if `next_fill_date - prior_run_out_date <= grace`, the episode continues; otherwise close the episode and set the discontinuation date = prior_run_out_date (some protocols add grace/2). (5) Primary grace = 30 days; pre-specified sensitivity at 15, 60, and 90 days, plus a proportional rule (grace = 0.5 × `days_supply`) so the 30-day-retail and 90-day-mail subgroups are treated comparably. (6) Persistence = time from index to discontinuation; censor at disenrollment, death (competing risk — report both cause-specific and Aalen-Johansen cumulative incidence), and end of data. Diagnostics: distribution of constructed episode lengths and gap lengths by grace value, % discontinued at each grace value, and the gap-length-vs-episode-count curve (per Gardarsdottir) to show the result's sensitivity to the rule.

Worked example

Scenario

Patient 2001 is prescribed a once-daily cholesterol-lowering tablet. We want to know how long she stayed on the drug during 2024. She fills the prescription three times. Our grace period is 30 days, meaning: if the next fill arrives within 30 days of the previous fill running out, the episode continues; if the gap exceeds 30 days, we record discontinuation at the prior run-out date and the later fill opens a new, separate episode.

Dataset

Raw pharmacy claims rows for patient 2001 — exactly what an analyst would see in a claims database.

person_idfill_datedrugdays_supply
20012024-01-01atorvastatin30
20012024-02-11atorvastatin30
20012024-05-01atorvastatin30

Steps

  • Fill A (Jan 1, 30-day supply) runs out on Jan 30. The patient has pills from Jan 1 through Jan 30.

  • Fill B arrives on Feb 11. The gap from Jan 31 (first pill-free day) to Feb 10 is 11 days — well within the 30-day grace period.

  • Because 11 days ≤ 30-day grace, the episode stays open. Fill B covers Feb 11 through Mar 11. Episode 1 now spans Jan 1 through Mar 11 (71 calendar days), with 60 days of actual pill coverage and an 11-day gap that was bridged.

  • Fill C arrives on May 1. The gap from Mar 12 (first pill-free day after Fill B) to Apr 30 is 50 days — longer than the 30-day grace period.

  • Because 50 days > 30-day grace, Episode 1 is closed. Discontinuation is dated at Mar 11 (the run-out of the last fill in that episode). Fill C opens a new Episode 2 running May 1 through May 30.

  • Result: Episode 1 ends Mar 11 (71-day span; 60 covered days; 11-day bridged gap). Episode 2 starts May 1 (30 covered days; 50-day gap ended the prior episode).

Result

Label

Episode 1: Jan 1 – Mar 11 (71-day span, 60 pill-covered days, 11-day gap bridged). Episode 2: May 1 – May 30 (30-day span). Gap of 50 days between Mar 12 and Apr 30 exceeded the 30-day grace and closed Episode 1.

Value
Episode 1 Span Days

71

Episode 1 Pill Covered Days

60

Bridged Gap Days

11

Episode Ending Gap Days

50

Grace Period Days

30

Gap A Bridged

True

Episode 2 Span Days

30

Timeline Spec

Title

Grace period in action: 30-day rule bridges a short gap, closes episode on a long gap (patient 2001, atorvastatin)

Window
Start

2024-01-01

End

2024-05-30

Label

Observation window: Jan 1 – May 30, 2024 (150 calendar days)

Events
  • Label

    Fill A

    Start

    2024-01-01

    Length Days

    30

    Quantity

    30 days_supply

  • Label

    Fill B

    Start

    2024-02-11

    Length Days

    30

    Quantity

    30 days_supply

  • Label

    Fill C (new episode)

    Start

    2024-05-01

    Length Days

    30

    Quantity

    30 days_supply

Spans
  • Kind

    covered

    Start

    2024-01-01

    End

    2024-01-30

    Label

    Fill A: 30 covered days

  • Kind

    gap

    Start

    2024-01-31

    End

    2024-02-10

    Label

    Gap A: 11 days ≤ 30-day grace → BRIDGED (episode continues)

  • Kind

    covered

    Start

    2024-02-11

    End

    2024-03-11

    Label

    Fill B: 30 covered days

  • Kind

    gap

    Start

    2024-03-12

    End

    2024-04-30

    Label

    Gap B: 50 days > 30-day grace → EPISODE ENDS (discontinuation dated Mar 11)

  • Kind

    covered

    Start

    2024-05-01

    End

    2024-05-30

    Label

    Fill C: 30 covered days (new Episode 2)

Result
Label

Episode 1: Jan 1 – Mar 11 (11-day gap bridged). Gap B = 50 days > grace → episode closed. Episode 2: May 1 – May 30.

Value
Episode 1 Covered Days

60

Episode 1 Bridged Gap Days

11

Episode Ending Gap Days

50

Episode 2 Covered Days

30

Caption

Two fills stitched into one episode when their gap (11 days) is within the 30-day grace period; a third fill starts a new episode because the gap (50 days) exceeds it. The grace period value is the single dial that decides whether an interruption is a bump in the road or the end of treatment.

Alt Text

Horizontal timeline for patient 2001 showing Fill A (Jan 1–Jan 30, covered), an 11-day bridged gap (Jan 31–Feb 10, shaded light), Fill B (Feb 11–Mar 11, covered), a 50-day episode-ending gap (Mar 12–Apr 30, shaded dark with discontinuation marker at Mar 11), and Fill C opening a new episode (May 1–May 30, covered).

Runnable example

python implementation

Build exposure episodes from claims-style pharmacy fills using a grace period with optional stockpiling carry-over. Required input (cleaned, de-duplicated, reversals netted out): rx : person_id, fill_date (datetime64), days_supply (int) # one row per...

import pandas as pd

GRACE_DAYS = 30      # permissible gap after run-out before the episode is closed
CARRYOVER_CAP = 30   # max banked surplus from early refills (set 0 to disable stockpiling)

def build_episodes(rx: pd.DataFrame) -> pd.DataFrame:
    rx = rx.sort_values(["person_id", "fill_date"]).copy()
    out = []
    for pid, g in rx.groupby("person_id", sort=False):
        ep_start = run_out = None
        n_fills = 0
        for fd, ds in zip(g["fill_date"], g["days_supply"]):
            if ep_start is None:
                ep_start, run_out, n_fills = fd, fd + pd.Timedelta(days=int(ds)), 1
                continue
            gap = (fd - run_out).days
            if gap <= GRACE_DAYS:
                # continue episode; stockpile early supply forward, capped to bound banking
                surplus = max(0, -gap)
                start = run_out if surplus > 0 else fd
                extra = min(surplus, CARRYOVER_CAP)
                run_out = max(run_out, start) + pd.Timedelta(days=int(ds) - (surplus - extra))
                n_fills += 1
            else:
                out.append((pid, ep_start, run_out, run_out, n_fills))  # discontinuation = run_out
                ep_start, run_out, n_fills = fd, fd + pd.Timedelta(days=int(ds)), 1
        if ep_start is not None:
            out.append((pid, ep_start, run_out, run_out, n_fills))
    return pd.DataFrame(out, columns=["person_id", "episode_start", "run_out",
                                      "discontinuation_date", "n_fills"])
r implementation

Episode construction with a permissible-gap rule using data.table. Input mirrors the Python version: rx : person_id, fill_date (Date), days_supply (integer) # one row per dispensing, cleaned/de-duplicated Returns one episode per row with discontinuation...

library(data.table)
GRACE_DAYS <- 30L
CARRYOVER_CAP <- 30L

build_episodes <- function(rx) {
  setDT(rx); setorder(rx, person_id, fill_date)
  rx[, {
    ep_start <- run_out <- NA; nfill <- 0L
    res <- list()
    for (i in seq_len(.N)) {
      fd <- fill_date[i]; ds <- as.integer(days_supply[i])
      if (is.na(ep_start)) { ep_start <- fd; run_out <- fd + ds; nfill <- 1L; next }
      gap <- as.integer(fd - run_out)
      if (gap <= GRACE_DAYS) {
        surplus <- max(0L, -gap)
        start   <- if (surplus > 0L) run_out else fd
        extra   <- min(surplus, CARRYOVER_CAP)
        run_out <- max(run_out, start) + (ds - (surplus - extra))
        nfill   <- nfill + 1L
      } else {
        res[[length(res) + 1L]] <- list(ep_start, run_out, run_out, nfill)
        ep_start <- fd; run_out <- fd + ds; nfill <- 1L
      }
    }
    res[[length(res) + 1L]] <- list(ep_start, run_out, run_out, nfill)
    rbindlist(lapply(res, function(x) data.table(episode_start = x[[1]],
      run_out = x[[2]], discontinuation_date = x[[3]], n_fills = x[[4]])))
  }, by = person_id]
}