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.
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:
- 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. - Designated Office — an ISO 3166-1 alpha-2 code (
US,EP,JP,CN), orEPfor the regional route. - 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.
- Jurisdiction rule file — a version-pinned mapping of office → statutory window, restoration policy, and translation requirement, cited to WIPO and national sources.
- 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_demandfor 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.
relativedeltahandles this; naivetimedelta(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
CN32-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_fallbackflag 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?
Does filing a Chapter II demand still extend the deadline to 31 months?
What happens if the 30-month PCT deadline falls on a weekend or holiday?
closure_adjusted when it does.
Can a missed national phase entry deadline be recovered?
Which date does the national phase window run from?
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.
Related
- How to Map PCT Rule 47 Deadlines to National Phase — separating IB transmittal triggers from statutory entry windows.
- USPTO vs EPO National Phase Entry Differences — the 30- vs 31-month deadlines and required acts side by side.
- PCT Chapter I vs Chapter II National Phase Timing — how a demand under Article 31 shifts the entry window.
- Recovering a Missed PCT National Phase Entry via USPTO Restoration Petition — the revival and reinstatement paths after a missed 30-month deadline.
- WIPO PATENTSCOPE Integration — the feed that supplies validated priority dates.
- EPO Register Sync Architecture — reconciling regional entry into EP under Rule 159(1) EPC.
- USPTO Data Schema Mapping — resolving US national-stage application numbers and 37 CFR 1.491 requirements.
- Automated Deadline Calculation & Rule Engines — the deterministic date-arithmetic primitives this engine builds on.