Every developer who has stored Patient.birthDate as a full Date object at some point encounters a payload that carries "birthDate": "1957". FHIR allows partial dates — year alone, year-month, or year-month-day — for exactly the cases where the full date is unknown or protected. Handling partial dates well is a small discipline that keeps a lot of downstream code correct. The site's FHIR timestamp inspector surfaces the precision explicitly. For the wider FHIR framing, the FHIR comparison index has more.
Where Partial Dates Show Up
- Historical patients whose exact birth date was never recorded
- Privacy contexts where only year is retained
- Deceased dates where the exact day is unknown
- Encounter periods with partial precision
- Observations where only the year is clinically meaningful
Every one produces a legitimately partial date value.
The Three Shapes
"2026"— year only"2026-07"— year-month"2026-07-11"— full year-month-day
Each is a valid FHIR date. Each carries different precision. Downstream code that assumes all three are full dates produces wrong results.
For the type framing, FHIR date, dateTime, instant: three types, one confusion is the entry.
The Age-Calculation Trap
age = today - birthDate fails on partial dates. today - "1957" is not a straightforward subtraction.
Options:
- Reject partial dates for age calculation
- Assume mid-year for year-only, mid-month for year-month
- Return a range instead of a single age
Each has different semantics. Pick per the workload. Age reported for a clinical decision needs precision; age reported for a demographic summary can tolerate uncertainty.
For the arithmetic side, date arithmetic on FHIR values without regretting it is the entry.
The Sort-Order Trap
"2026" < "2026-01-01" lexically. Semantically, "2026" spans the whole year and could be before, during, or after January 1.
Sorting partial dates by string works but produces surprising results. Sort by precision-aware comparison instead.
The Display Trap
Rendering "1957" as "January 1, 1957" is a lie. The date is not January 1; the date is somewhere in 1957.
Render as "1957" when the value is year-only. Render as "July 1957" when year-month. Only render full dates when the precision is full.
For the UI-formatting mechanic, formatting a FHIR dateTime for a UI without lying about precision is the entry.
Storage Considerations
- Store the original string — preserves precision explicitly
- Store precision as a separate field — enables SQL queries
- Store as a range — start of period, end of period
Each is a valid choice. The range representation is easiest for downstream arithmetic; the string representation is closest to the wire.
FHIRPath And Partial Dates
FHIRPath handles partial dates natively. %today > birthDate works whether birthDate is partial or full — the comparison uses precision-aware semantics.
FHIR invariants written in FHIRPath can rely on this. External code cannot assume the same semantics.
The Inspector's Report
The inspector shows:
- The literal value
- The detected precision (year / year-month / year-month-day)
- The implied range
That readout is what tells you "the sender sent a year, not a full date."
The Short Version
Partial dates are legal FHIR. Handle year, year-month, and year-month-day distinctly. Render at the precision provided. Age calculation needs a precision-aware approach. Sorting by string is wrong. Store the original string plus a precision field for easy downstream reasoning.

Sources
- HL7 canonical R4 datatypes chapter covering partial date - HL7 canonical R4 datatypes chapter covering partial date semantics
