PCT National Phase Entry Rules: A Deterministic Calculation Engine for Docketing

The transition from an international application to national prosecution is the single highest-risk inflection point in a patent portfolio: one 30-month anchor produces a different effective deadline in every designated office, and a missed entry is an irreversible loss of rights across an entire family. This guide closes the gap between a WIPO priority date and the calendar-adjusted, statute-anchored due date a docketing system must emit — the deterministic month arithmetic, the per-office rule resolution, the closure-day roll under PCT Rule 80.5, and the audit record that makes each computation survive review.

The engine treats national phase entry as four separable, independently testable stages: base-date resolution, version-pinned rule lookup, statutory-window arithmetic, and closure-adjusted emission with an immutable hash. It sits directly beneath the Core Docketing Architecture & Deadline Taxonomy reference and consumes base dates supplied by the WIPO PATENTSCOPE Integration layer, while the deeper date-arithmetic primitives it relies on are owned by the Automated Deadline Calculation & Rule Engines framework.

PCT national phase entry deadline engine for one application and office A left-to-right five-stage pipeline. Stage 1 resolves the base date from the earliest priority date, falling back to the international filing date under PCT Article 8 when the priority chain is broken. Stage 2 looks the designated office up in a version-pinned jurisdiction rule file that supplies the 30-month Article 22 default, the 31-month EP Article 39 window, and per-office overrides. Stage 3 adds the statutory window in calendar months using relativedelta with end-of-month clamping. Stage 4 rolls a deadline landing on a closed day forward under PCT Rule 80.5, keyed to the national office of entry's own closure calendar, not WIPO's. Stage 5 emits a ResolvedEntryDeadline sealed with a SHA-256 audit hash. A restoration branch peels off stage 5 to a paralegal review queue whenever Rule 49.6 due-care or unintentional-delay eligibility, a priority fallback, or an unmapped office is flagged. NATIONAL PHASE ENTRY DEADLINE ENGINE — ONE APPLICATION, ONE OFFICE Broken priority chain → Art. 8 filing date 1 Base Date earliest priority 2 Rule Lookup version-pinned 3 Window Math relativedelta clamp 4 Rule 80.5 Roll national calendar 5 Emit + Audit SHA-256 record Jurisdiction rule file (version-pinned) 30 mo Art. 22 default · 31 mo EP Art. 39 per-office overrides · unmapped → REVIEW Rule 49.6 flag · fallback · unmapped Paralegal review queue due-care / unintentional · attorney sign-off
The five-stage entry engine: the base date resolves from the earliest priority date (Art. 8 filing-date fallback when the chain is broken), a version-pinned rule file supplies the per-office window, calendar-month arithmetic clamps end-of-month overflow, Rule 80.5 rolls the raw date off the national office closure calendar, and each result is sealed with a SHA-256 audit hash. Restoration eligibility, a priority fallback, or an unmapped office peels off to the paralegal review queue before anything drives a reminder.

Compliance & Scope Boundaries

This engine computes statutory baselines. It is decision-support, never the controlling authority, and several boundaries belong in code review before anything ships:

  • Computation is advisory. The controlling deadline is whatever the designated Office recognizes. Every emitted date must trace back to the exact priority date, rule version, and office closure calendar that produced it. No output enters a reminder pipeline without that lineage.
  • Verify windows against live WIPO data. Individual offices file and withdraw reservations under PCT Article 22 that change the effective period. Always reconcile the rule file against the WIPO PCT Contracting States table before deploying; a hardcoded window is a stale window.
  • Do not scrape the human-facing register. PATENTSCOPE and the office registers expose sanctioned machine-readable channels; automated retrieval must honor their published rate limits and robots.txt. Feed acquisition is out of scope here and belongs to the ingestion layer.
  • Restoration is attorney work. Late entry under PCT Rule 49.6 (due-care or unintentional-delay standards) requires a petition, a fee, and a supporting declaration. The engine may flag eligibility, but it must never auto-docket a restored deadline without human sign-off.
  • Data minimization. Extract only the fields deadline calculation needs — priority date, designated office, demand status. Applicant and inventor personal data is gated per Security & Access Control Boundaries.

Prerequisites & Dependency Map

The calculator has a small, explicit dependency surface. Pin every item so a behavioral change is a reviewable diff rather than ambient drift.

Dependency Minimum version Role
Python 3.11 Native zoneinfo, datetime.UTC, structural pattern matching
pydantic 2.5 Input validation and output schema enforcement
python-dateutil 2.8 relativedelta calendar-month arithmetic
tzdata 2024.1+ IANA zone database on platforms without a system copy
PyYAML 6.0 Loads the version-pinned jurisdiction rule file

Upstream inputs that must be resolved before the calculator runs:

  1. Priority date — the earliest valid priority date, normalized to an ISO date, sourced from the priority chain validated by the WIPO PATENTSCOPE Integration layer. Where the chain is broken, fall back to the international filing date under PCT Article 8.
  2. Designated Office — an ISO 3166-1 alpha-2 code (US, EP, JP, CN), or EP for the regional route.
  3. Chapter II demand status — whether a demand for international preliminary examination was filed before 19 months from priority, which historically governed the 30-vs-31-month choice for a handful of offices.
  4. Jurisdiction rule file — a version-pinned mapping of office → statutory window, restoration policy, and translation requirement, cited to WIPO and national sources.
  5. Office closure calendar — the designated Office’s non-working days, pinned to a release tag, used for the Rule 80.5 roll-forward.
# pct_entry_rules.yaml
# Source of truth: WIPO PCT Applicant's Guide, National Chapters + PCT Contracting States table.
# https://www.wipo.int/pct/en/guide/  and  https://www.wipo.int/pct/en/pct_contracting_states.html
rule_version: "2026.07.0"
default:
  months: 30            # PCT Art. 22(1) baseline for most designated Offices
  restoration: true     # Rule 49.6 available unless an Office reserved out
  petition_required: true
offices:
  EP:                   # European regional route
    months: 31          # PCT Art. 39(1)(b) window as fixed by EPC Rule 159(1)
    restoration: true
    petition_required: false
    note: "Regional entry into EP; translation + Rule 159(1) acts due at 31 months"
  US:
    months: 30          # 37 CFR 1.491(a)
    restoration: true
    petition_required: true
    note: "35 USC 371; national stage. Restoration via 37 CFR 1.452 (unintentional)"
  JP:
    months: 30          # Japan uniform 30-month period
    restoration: true
    petition_required: true
  CN:
    months: 30          # China 30-month; 32 months with surcharge (not restoration)
    restoration: false
    petition_required: false
    note: "CNIPA 30-month window; 2-month surcharge extension is procedural, not Rule 49.6"

Step-by-Step Implementation

The calculator is a deterministic pipeline anchored to a single application and designated Office. Each step is independently verifiable — run its snippet in isolation and assert the intermediate value before composing the whole.

Step 1 — Resolve the base priority date

The statutory window originates from the earliest valid priority date. When the priority chain is broken or absent, fall back deterministically to the international filing date under PCT Article 8, and record that the fallback fired.

from datetime import date
from dataclasses import dataclass

@dataclass(frozen=True)
class BaseDate:
    value: date
    used_fallback: bool

def resolve_base_date(priority_date: date | None, filing_date: date) -> BaseDate:
    """Earliest priority date, or the international filing date if the chain is broken."""
    if priority_date is None:
        # PCT Art. 8 / Art. 11(1): with no valid priority claim the window runs
        # from the international filing date itself.
        return BaseDate(filing_date, used_fallback=True)
    return BaseDate(priority_date, used_fallback=False)

# Verify: a present priority date is used verbatim, no fallback.
# assert resolve_base_date(date(2023, 4, 15), date(2023, 9, 1)).used_fallback is False

Step 2 — Load the version-pinned jurisdiction rule

Never infer a window from the office code implicitly. Look the office up in the pinned rule file, and fall back to the default block only with an explicit, logged flag so an unmapped office is visible in the audit trail rather than silently defaulted.

import yaml
from typing import Any

def load_rules(path: str) -> dict[str, Any]:
    with open(path, "r", encoding="utf-8") as fh:
        return yaml.safe_load(fh)

def resolve_rule(office: str, rules: dict[str, Any]) -> tuple[dict[str, Any], bool]:
    """Return (rule, used_default). used_default=True means the office was unmapped."""
    office = office.upper()
    if office in rules["offices"]:
        return rules["offices"][office], False
    return rules["default"], True

Step 3 — Compute the statutory entry deadline

Add the office’s window in calendar months. relativedelta clamps an overflowing day-of-month (e.g. 31 August + 1 month → 30 September) rather than spilling into the next month, which is exactly the behavior WIPO’s own period computation assumes.

from datetime import date
from dateutil.relativedelta import relativedelta

def statutory_deadline(base: date, months: int) -> date:
    """Priority date + N months, with end-of-month clamping (Art. 22 / Art. 39)."""
    return base + relativedelta(months=months)

# Verify: 30 months from a 2023-04-15 priority = 2025-10-15.
# assert statutory_deadline(date(2023, 4, 15), 30) == date(2025, 10, 15)

Step 4 — Roll off national office closure days (Rule 80.5)

PCT Rule 80.5 extends any period expiring on a day the relevant office is closed to the next day it is open. The relevant office is the national Office of entry, not WIPO — so the same 30-month anchor produces different effective dates in different countries. Re-test the condition in a loop so rolling off a weekend cannot land on a holiday.

from datetime import date, timedelta

def roll_off_closures(target: date, closures: frozenset[date]) -> tuple[date, bool]:
    """Roll forward past weekends and designated-office closure days (Rule 80.5)."""
    adjusted = False
    while target.weekday() >= 5 or target in closures:  # 5=Sat, 6=Sun
        target += timedelta(days=1)
        adjusted = True
    return target, adjusted

# Verify: 2025-10-15 is a Wednesday with no closures -> unchanged, not adjusted.
# assert roll_off_closures(date(2025, 10, 15), frozenset()) == (date(2025, 10, 15), False)

Step 5 — Emit each deadline with an immutable audit record

The output is never a bare date. It carries the office, the applied window, the closure-shift flag, the rule version, and a SHA-256 hash of the exact inputs, so a compliance dashboard can reconstruct precisely which priority date and rule produced it — the same discipline enforced across the parent Core Docketing Architecture & Deadline Taxonomy.

import hashlib

def build_audit_hash(base: date, office: str, months: int, rule_version: str) -> str:
    payload = f"{base.isoformat()}|{office}|{months}|{rule_version}"
    return hashlib.sha256(payload.encode("utf-8")).hexdigest()

API Contract & Schema

Docketing platforms consume this calculator through a stateless, idempotent boundary. Strict Pydantic v2 validation rejects malformed input before any arithmetic, and an idempotency key deduplicates a re-computed deadline across overlapping runs.

from datetime import date
from typing import Literal
from pydantic import BaseModel, Field, field_validator

class EntryRequest(BaseModel):
    application_number: str = Field(pattern=r"^PCT/[A-Z]{2}\d{4}/\d{6}$")
    priority_date: date | None
    filing_date: date
    designated_office: str = Field(pattern=r"^[A-Z]{2}$")
    chapter_ii_demand: bool = False

    @field_validator("filing_date")
    @classmethod
    def filing_not_future(cls, v: date) -> date:
        if v > date.today():
            raise ValueError("filing_date is in the future")
        return v

class ResolvedEntryDeadline(BaseModel):
    application_number: str
    designated_office: str
    base_date: date
    statutory_months: int
    entry_deadline: date
    closure_adjusted: bool
    restoration_available: bool
    used_priority_fallback: bool
    used_default_rule: bool
    rule_version: str
    audit_hash: str
    compliance_status: Literal["ACTIVE", "REVIEW_REQUIRED"] = "ACTIVE"

    @property
    def idempotency_key(self) -> str:
        # Re-computing the same application/office/base date collapses to one entry.
        return f"{self.application_number}:{self.designated_office}:{self.base_date.isoformat()}"

A caller replaying the same idempotency_key receives the identical resolved deadline without re-triggering downstream reminder webhooks. Any request that resolves through used_default_rule=True — an unmapped office — is emitted as REVIEW_REQUIRED so a paralegal confirms the window before it drives a reminder.

The full computation composes the five steps into one pure function:

def compute_entry_deadline(
    req: EntryRequest, rules: dict, closures: frozenset[date]
) -> ResolvedEntryDeadline:
    base = resolve_base_date(req.priority_date, req.filing_date)
    rule, used_default = resolve_rule(req.designated_office, rules)
    raw = statutory_deadline(base.value, int(rule["months"]))
    deadline, adjusted = roll_off_closures(raw, closures)
    return ResolvedEntryDeadline(
        application_number=req.application_number,
        designated_office=req.designated_office.upper(),
        base_date=base.value,
        statutory_months=int(rule["months"]),
        entry_deadline=deadline,
        closure_adjusted=adjusted,
        restoration_available=bool(rule["restoration"]),
        used_priority_fallback=base.used_fallback,
        used_default_rule=used_default,
        rule_version=rules["rule_version"],
        audit_hash=build_audit_hash(
            base.value, req.designated_office.upper(),
            int(rule["months"]), rules["rule_version"],
        ),
        compliance_status="REVIEW_REQUIRED" if used_default else "ACTIVE",
    )

Edge Cases & Failure Modes

The happy path is trivial; the value of this calculator is in the failures it refuses to hide.

  • The relevant office is the office of entry. Rule 80.5 rolls off the national Office’s closure calendar, not WIPO’s. Feeding the wrong calendar (or WIPO’s) silently mis-rolls the effective date. The closure set must be selected by designated_office.
  • 30 vs 31 months is not a demand toggle anymore. After the 2002 PCT amendment, filing a Chapter II demand no longer buys the extra month in most offices — the 30/31 choice is now fixed per office by national law. Keying the window off chapter_ii_demand for a modern application over-counts by a month. The rule file, not the demand flag, is authoritative.
  • End-of-month priority dates. A 31 August priority + 30 months must clamp to the last valid day of the target month, not spill forward. relativedelta handles this; naive timedelta(days=30*N) does not and drifts by days.
  • Leap-day priority. A 29 February priority resolves to 28 February in a non-leap target year. Verify this explicitly rather than trusting a fixed day offset.
  • Restoration is not a longer window. A CN 32-month surcharge extension is procedural and applies to everyone; a Rule 49.6 restoration is a discretionary petition after the window closed. Modeling either as “add two months” is wrong — the first is a fee, the second needs attorney sign-off.
  • Broken priority chain. When the priority claim is invalid, the window runs from the international filing date under Article 8. The used_priority_fallback flag must surface this so the shortened window is reviewed, not trusted.
  • Deadline already in the past on emit. A newly computed date earlier than today means a stale anchor or a late-arriving feed. Escalate to supervising counsel and raise a malpractice-risk alert rather than docketing silently.

For offices that also expose a live register, reconcile the computed entry against the office’s own status feed — for EP regional entry that alignment is handled by the EPO Register Sync Architecture, and for US national-stage numbers by the USPTO Data Schema Mapping guide.

Verification & Regression Testing

Anchor the calculator to known-good dates and run the suite on every rule-file change. These assertions are the contract:

from datetime import date

RULES = {
    "rule_version": "2026.07.0",
    "default": {"months": 30, "restoration": True, "petition_required": True},
    "offices": {
        "EP": {"months": 31, "restoration": True, "petition_required": False},
        "CN": {"months": 30, "restoration": False, "petition_required": False},
    },
}

def test_default_30_month_window():
    req = EntryRequest(
        application_number="PCT/US2023/012345",
        priority_date=date(2023, 4, 15), filing_date=date(2023, 9, 1),
        designated_office="US",
    )
    out = compute_entry_deadline(req, RULES, frozenset())
    assert out.entry_deadline == date(2025, 10, 15)   # 30 months, no closures
    assert out.used_default_rule is True              # US not in test rule file
    assert out.compliance_status == "REVIEW_REQUIRED"

def test_ep_31_month_window():
    req = EntryRequest(
        application_number="PCT/US2023/012345",
        priority_date=date(2023, 4, 15), filing_date=date(2023, 9, 1),
        designated_office="EP",
    )
    out = compute_entry_deadline(req, RULES, frozenset())
    assert out.entry_deadline == date(2025, 11, 15)   # 31 months under EPC R.159(1)
    assert out.restoration_available is True

def test_rule_80_5_rolls_off_closure():
    # 30 months from 2023-06-15 = 2025-12-15 (Mon). Treat it as an office closure
    # day; Rule 80.5 must roll the entry deadline to Tuesday 2025-12-16.
    req = EntryRequest(
        application_number="PCT/US2023/012345",
        priority_date=date(2023, 6, 15), filing_date=date(2023, 9, 1),
        designated_office="CN",
    )
    out = compute_entry_deadline(req, RULES, frozenset({date(2025, 12, 15)}))
    assert out.entry_deadline == date(2025, 12, 16)
    assert out.closure_adjusted is True

def test_broken_priority_chain_falls_back():
    req = EntryRequest(
        application_number="PCT/US2023/012345",
        priority_date=None, filing_date=date(2023, 9, 1), designated_office="CN",
    )
    out = compute_entry_deadline(req, RULES, frozenset())
    assert out.base_date == date(2023, 9, 1)          # Art. 8 filing-date fallback
    assert out.used_priority_fallback is True

The default case pins the 30-month arithmetic and the unmapped-office review flag, the EP case proves the 31-month override, the Rule 80.5 case proves the closure roll-forward fires, and the fallback case proves a broken priority chain surfaces rather than hides.

Operational Action Summary

Operational Action: Treat pct_entry_rules.yaml as code — gate it through peer review by patent counsel, pin rule_version, and reconcile every window against the WIPO PCT Contracting States table before each release.

Operational Action: Select the Rule 80.5 closure calendar by designated_office, never a global calendar, and log every computation (priority date, applied rule, shift flag, output, audit hash) to append-only storage.

Operational Action: Route every REVIEW_REQUIRED result — unmapped office, priority-fallback, or restoration-eligible late entry — to a paralegal queue before it drives a reminder, and configure tiered alerts at T-120, T-60, T-30, and T-7 days with T-0/T+1 escalation to supervising counsel.

Frequently Asked Questions

What is the deadline for PCT national phase entry — 30 or 31 months?
The PCT Article 22 baseline is 30 months from the earliest priority date, and most designated Offices apply it. A minority — notably the European regional route, where EPC Rule 159(1) fixes the Article 39 window — use 31 months. The choice is fixed per Office in the rule file, not derived from the office code.
Does filing a Chapter II demand still extend the deadline to 31 months?
No, not since the 2002 PCT amendment. For applications with an international filing date on or after 1 April 2002, the 30-month period under Article 22 applies whether or not a demand for international preliminary examination was filed. The 30-vs-31-month distinction is now purely a per-Office matter of national law.
What happens if the 30-month PCT deadline falls on a weekend or holiday?
Under PCT Rule 80.5 the period is extended to the next day on which the relevant Office is open. The "relevant Office" is the national or regional Office of entry — not WIPO — so the same 30-month anchor can produce different effective dates in different countries. The engine rolls the raw date forward past weekends and the pinned office closure calendar and sets closure_adjusted when it does.
Can a missed national phase entry deadline be recovered?
Sometimes. PCT Rule 49.6 allows restoration where the failure to enter in time was either unintentional or occurred in spite of due care, depending on the standard the Office applies. It requires a petition, a fee, and a supporting statement, and some Offices have reserved out entirely. The engine only flags restoration eligibility; the petition itself is attorney work and must never be auto-docketed.
Which date does the national phase window run from?
The earliest valid priority date claimed in the international application. When the priority claim is invalid or absent, the window runs from the international filing date under PCT Article 8, and the engine sets used_priority_fallback so the shortened window is reviewed rather than trusted.

For authoritative references, practitioners should consult the WIPO PCT Applicant’s Guide for the national-chapter windows, the WIPO PCT Contracting States table for current reservations, and USPTO MPEP § 1893 for US national-stage requirements. Python implementations should rely on the standard-library zoneinfo module and dateutil.relativedelta for calendar-correct arithmetic.

← Up to Core Docketing Architecture & Deadline Types