open-pharma-dosing Structured medication dosing frequency encoding, parsing, and FHIR R4 Timing conversion.
The Problem
Every prescribing system deals with dosing frequencies — BD, TDS, OD, QDS, PRN, STAT, nocte, mane — yet there is no open-source, standalone library that maps these to structured timing data with bidirectional FHIR conversion.
The UK says BD, the US says BID. Both mean “twice daily, every 12 hours, typically at 08:00 and 20:00.” Clinicians think in shorthand. FHIR thinks in { "frequency": 2, "period": 1, "periodUnit": "d" }. This library bridges the gap.
Features
Parser
Accepts BD, b.i.d., twice daily, every 4 hours, TID, p.r.n. — canonical codes, aliases, mixed case, punctuation variants.
FHIR Converter
Bidirectional FHIR R4 Timing/Dosage conversion with roundtrip fidelity for all 33 codes.
Schedule Generator
Concrete administration times from a code + start date. Handles rolling intervals, extended periods, and custom times.
Validator
Clinical sense-checking: PRN without max dose, duration with one-off codes, daily dose exceeding limits.
Installation
Go (canonical implementation)
go get github.com/Open-Nucleus/open-pharm-dosing
Dart (coming soon)
dart pub add open_pharma_dosing
Python (coming soon)
pip install open-pharma-dosing
Quick Start
import dosing "github.com/Open-Nucleus/open-pharm-dosing"
// Parse clinical shorthand
fc, _ := dosing.Parse("BD") // → FrequencyCode{Code: "BD", ...}
fc, _ = dosing.Parse("b.i.d.") // → FrequencyCode{Code: "BD", ...}
fc, _ = dosing.Parse("twice daily") // → FrequencyCode{Code: "BD", ...}
fc, _ = dosing.Parse("every 4 hours") // → FrequencyCode{Code: "Q4H", ...}
// Convert to FHIR R4 Timing
timingJSON, _ := dosing.ToFhirTiming("BD")
// Convert back
fc, _ = dosing.FromFhirTiming(timingJSON)
fmt.Println(fc.Code) // "BD"
// Generate human-readable text
text, _ := dosing.ToText("BD", dosing.LocaleEnGB) // "Twice daily"
label, _ := dosing.ToLabel("BD", dosing.LocaleEnUS) // "BID"
// Generate concrete schedule
start := time.Now()
times, _ := dosing.Schedule("TDS", start, 7) // 7 days of TDS times
Standards & Provenance
Every code in this library is traceable to an authoritative source. No codes are invented.
| Source | What it provides | Reference |
|---|---|---|
| HL7 FHIR v3-GTSAbbreviation | Formal coded timing abbreviations (QD, BID, TID, QID, Q1H–Q8H, QOD, AM, BED, WK, MO) | terminology.hl7.org |
| FHIR R4 EventTiming | Timing.repeat.when codes (MORN, NIGHT, AC, PC, HS, C) |
hl7.org/fhir/R4 |
| NHS Dose Syntax Implementation Guide | UK guidance for populating FHIR Dosage structures | nhsconnect.github.io |
| NHS Dose Syntax API Standards | National API standard for dose syntax in prescribing systems | digital.nhs.uk |
| NHS App Medical Records | Official abbreviations list (b.d., t.d.s., q.d.s., o.d., p.r.n., nocte, stat, a.c., p.c.) | nhs.uk |
| NHS Scotland Dose Syntax | Structured dose instruction standard for Scottish prescribing (2015) | scimp.scot.nhs.uk |
Source Tiers
Each frequency code is classified by source tier:
- S1 HL7 GTS — Code exists in the HL7 v3-GTSAbbreviation CodeSystem (formal international standard)
- S2 NHS Convention — Documented in NHS prescribing guidance or the NHS App abbreviations list
- S3 Clinical Extension — Common prescribing pattern not in S1/S2; sourced from BNF usage and Latin tradition
Roadmap
- Phase 1: Core Go library — types, registry, parser, FHIR converter
- Phase 2: Text generation, schedule generator, validation
- Phase 3: Dart and Python ports
- Phase 4: Locale support (en-GB, en-US, fr, es, sw, ha, yo), ParseInstruction
- Phase 5: Complex regimens (tapering, split-dose, cyclical), EPMA integration
Design Decisions
- Zero dependencies. No external packages in any language implementation. The registry is embedded as compiled data.
- NHS-first, internationally compatible. Canonical codes use UK convention (BD, TDS, QDS, OD) with US aliases (BID, TID, QID, QD). The FHIR converter uses the HL7 GTS codes (BID, TID, QID).
- Registry as code, not config. 33 frequency entries are hand-written Go struct literals, type-checked at compile time.
- FHIR types are internal. Internal Go structs for FHIR Timing/Dosage construction, but
[]byte(JSON) at the public API boundary.