API Reference
Go package dosing — github.com/Open-Nucleus/open-pharm-dosing
import dosing "github.com/Open-Nucleus/open-pharm-dosing"
Types
FrequencyCode
The primary data type. Represents a single dosing frequency with all its metadata.
type FrequencyCode struct {
Code string // Canonical code: "BD", "TDS", etc.
Aliases []string // All accepted input forms
LocalePreferred map[string]string // Locale → preferred display code
Display map[string]string // Locale → human-readable text
Category Category // regular, interval, time_of_day, ...
Frequency int // Times per period (2 for BD)
Period int // Period count (1 for "per day")
PeriodUnit PeriodUnit // d, h, wk, mo
IntervalHours float64 // Hours between doses (12 for BD)
DefaultTimes []string // HH:MM default admin times
WakingOnly bool // Only during waking hours
AsNeeded bool // PRN flag
MaxPerDay int // 0 = no limit
MinInterval *float64 // Min hours between PRN doses
MealRelation MealRelation // none, before, after, with
Latin string // Latin origin
FhirCode string // FHIR TimingAbbreviation code
FhirSystem string // FHIR coding system URI
SortOrder int // For UI display ordering
}
DosingInstruction
A complete prescription line combining frequency with dose, route, duration, and modifiers.
type DosingInstruction struct {
Frequency *FrequencyCode // Required: the dosing frequency
MealModifier *FrequencyCode // Optional: AC, PC, CC modifier
Dose *Dose // Optional: amount + unit
Route string // PO, IV, IM, SC, etc.
Duration *Duration // Optional: 7 days, 2 weeks, etc.
MaxDose *MaxDose // Optional: max per day / per dose
Instructions []string // Additional: "with plenty of water"
}
Dose
type Dose struct {
Value float64 // e.g. 500
Unit string // mg, ml, mcg, units, tablets, etc.
LowValue *float64 // For range doses: "1-2 tablets"
HighValue *float64
}
Duration
type Duration struct {
Value int
Unit PeriodUnit // d, wk, mo
}
MaxDose
type MaxDose struct {
MaxPerDose *float64
MaxPerDay *float64
MaxPerDoseUnit string
MaxPerDayUnit string
}
ValidationWarning
type ValidationWarning struct {
Field string // Which field triggered the finding
Message string // Human-readable description
Level string // "error", "warning", "info"
}
Constants
// Categories
const (
CategoryRegular Category = "regular"
CategoryInterval Category = "interval"
CategoryTimeOfDay Category = "time_of_day"
CategoryMealRelative Category = "meal_relative"
CategoryPRN Category = "prn"
CategoryOneOff Category = "one_off"
CategoryExtended Category = "extended"
)
// Period units
const (
PeriodHour PeriodUnit = "h"
PeriodDay PeriodUnit = "d"
PeriodWeek PeriodUnit = "wk"
PeriodMonth PeriodUnit = "mo"
)
// Meal relations
const (
MealNone MealRelation = "none"
MealBefore MealRelation = "before"
MealAfter MealRelation = "after"
MealWith MealRelation = "with"
)
// Locales
const (
LocaleEnGB = "en-GB"
LocaleEnUS = "en-US"
)
Registry Functions
Get
func Get(code string) (*FrequencyCode, error)
Returns the FrequencyCode for the given canonical code. Returns ErrCodeNotFound if the code is not in the registry, ErrEmptyInput if empty.
fc, err := dosing.Get("TDS")
fmt.Println(fc.Frequency) // 3
fmt.Println(fc.IntervalHours) // 8
fmt.Println(fc.DefaultTimes) // ["08:00", "14:00", "20:00"]
fmt.Println(fc.Latin) // "ter die sumendus"
List
func List(opts ...ListOption) []*FrequencyCode
Returns all registered frequencies sorted by SortOrder. Accepts optional filters.
// All codes
all := dosing.List()
// Filter by category
prn := dosing.List(dosing.WithCategory(dosing.CategoryPRN))
Search
func Search(query string) []*FrequencyCode
Returns frequency codes matching the query as a case-insensitive substring of the code, any alias, or any display text.
results := dosing.Search("daily") // matches OD, BD, TDS, QDS, 5X_DAILY
results = dosing.Search("morning") // matches MANE, AM_PM
WithCategory
func WithCategory(c Category) ListOption
Filters List() results to a specific category.
Parser
Parse
func Parse(input string) (*FrequencyCode, error)
Converts a free-text dosing frequency string into a FrequencyCode. Handles canonical codes, aliases, mixed case, punctuation variants, and “every N hours” patterns.
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"}
fc, _ = dosing.Parse("TID") // → FrequencyCode{Code: "TDS"}
fc, _ = dosing.Parse("p.r.n.") // → FrequencyCode{Code: "PRN"}
ParseInstruction
func ParseInstruction(input string) (*DosingInstruction, error)
Parses a full dosing instruction string. Stub — returns ErrNotImplemented. Full implementation deferred to Phase 4.
FHIR Conversion
ToFhirTiming
func ToFhirTiming(code string) ([]byte, error)
Converts a canonical frequency code (or alias) to a FHIR R4 Timing JSON object.
timingJSON, _ := dosing.ToFhirTiming("BD")
// Output:
// {
// "repeat": {
// "frequency": 2,
// "period": 1,
// "periodUnit": "d",
// "timeOfDay": ["08:00", "20:00"]
// },
// "code": {
// "coding": [{"system": "...v3-GTSAbbreviation", "code": "BID"}]
// }
// }
FromFhirTiming
func FromFhirTiming(timing []byte) (*FrequencyCode, error)
Converts FHIR R4 Timing JSON to a FrequencyCode. Uses three strategies in order: FHIR code match, when-based match, structure match.
fc, _ := dosing.FromFhirTiming(timingJSON)
fmt.Println(fc.Code) // "BD"
ToFhirDosage
func ToFhirDosage(instruction *DosingInstruction) ([]byte, error)
Converts a DosingInstruction to a FHIR R4 Dosage JSON object. Includes timing, route, dose quantity, and max dose per period.
fc, _ := dosing.Parse("BD")
instruction := &dosing.DosingInstruction{
Frequency: fc,
Dose: &dosing.Dose{Value: 500, Unit: "mg"},
Route: "PO",
}
dosageJSON, _ := dosing.ToFhirDosage(instruction)
FromFhirDosage
func FromFhirDosage(dosage []byte) (*DosingInstruction, error)
Converts FHIR R4 Dosage JSON to a DosingInstruction. Extracts frequency, dose, route, and max dose.
result, _ := dosing.FromFhirDosage(dosageJSON)
fmt.Println(result.Frequency.Code) // "BD"
fmt.Println(result.Dose.Value) // 500
fmt.Println(result.Route) // "PO"
Text Generation
ToText
func ToText(code string, locale string) (string, error)
Returns the human-readable display text for a frequency code. Falls back to en-GB for unsupported locales.
text, _ := dosing.ToText("BD", dosing.LocaleEnGB) // "Twice daily"
text, _ = dosing.ToText("PRN", dosing.LocaleEnGB) // "As needed"
ToLabel
func ToLabel(code string, locale string) (string, error)
Returns the locale-preferred short code label. For example, “BD” in en-GB vs “BID” in en-US.
label, _ := dosing.ToLabel("BD", dosing.LocaleEnGB) // "BD"
label, _ = dosing.ToLabel("BD", dosing.LocaleEnUS) // "BID"
label, _ = dosing.ToLabel("TDS", dosing.LocaleEnUS) // "TID"
InstructionToText
func InstructionToText(instruction *DosingInstruction, locale string) (string, error)
Converts a DosingInstruction to a human-readable string. Output pattern: {dose} {frequency} {route} for {duration}, {meal modifier} (max {max}).
fc, _ := dosing.Parse("BD")
ac, _ := dosing.Parse("AC")
text, _ := dosing.InstructionToText(&dosing.DosingInstruction{
Frequency: fc,
MealModifier: ac,
Dose: &dosing.Dose{Value: 500, Unit: "mg"},
Route: "PO",
Duration: &dosing.Duration{Value: 7, Unit: dosing.PeriodDay},
}, dosing.LocaleEnGB)
// "500mg twice daily by mouth for 7 days, before meals"
Schedule Generation
Schedule
func Schedule(code string, start time.Time, days int) ([]time.Time, error)
Generates concrete administration times for the given frequency code. Day 1 skips times that have already passed relative to start. PRN and pure meal modifier codes return errors.
start := time.Date(2025, 1, 15, 9, 30, 0, 0, time.UTC)
// Fixed-time schedule (skips already-passed times on day 1)
times, _ := dosing.Schedule("BD", start, 3)
// Day 1: 20:00 (08:00 skipped — before 09:30)
// Day 2: 08:00, 20:00
// Day 3: 08:00, 20:00
// Rolling interval
times, _ = dosing.Schedule("Q1H", start, 1)
// 24 times: 09:30, 10:30, 11:30, ...
// One-off
times, _ = dosing.Schedule("STAT", start, 1)
// [2025-01-15 09:30]
// PRN returns error
_, err := dosing.Schedule("PRN", start, 1)
// ErrPRNNoSchedule
ScheduleWithTimes
func ScheduleWithTimes(code string, start time.Time, days int, customTimes []string) ([]time.Time, error)
Like Schedule but uses custom HH:MM times instead of the code’s DefaultTimes. Rolling-interval codes ignore custom times.
times, _ := dosing.ScheduleWithTimes("BD", start, 1, []string{"07:00", "19:00"})
Validation
Validate
func Validate(code string) error
Checks whether the given code resolves to a known frequency code.
err := dosing.Validate("BD") // nil
err = dosing.Validate("XYZZY") // error
ValidateInstruction
func ValidateInstruction(instruction *DosingInstruction) []ValidationWarning
Performs clinical sense-checking on a DosingInstruction, returning multi-level findings. Checks include:
- PRN without maximum dose →
warning - Meal modifier on incompatible category →
warning - Duration with one-off code →
warning - Dose value ≤ 0 →
error - Empty dose unit →
warning - Range dose low > high →
error - Missing route when dose present →
info - Computed daily dose exceeds max →
warning - Duration with PRN →
info
fc, _ := dosing.Parse("PRN")
warnings := dosing.ValidateInstruction(&dosing.DosingInstruction{
Frequency: fc,
Dose: &dosing.Dose{Value: 500, Unit: "mg"},
})
// warnings includes:
// {Field: "max_dose", Level: "warning", Message: "PRN frequency without maximum dose limit"}
// {Field: "route", Level: "info", Message: "route not specified"}
Errors
| Variable | Value | Returned By |
|---|---|---|
ErrCodeNotFound | frequency code not found | Get, Parse |
ErrEmptyInput | empty input | Get, Parse |
ErrFhirConversion | FHIR conversion error | ToFhirTiming, ToFhirDosage |
ErrFhirNoMatch | no matching frequency code for FHIR timing | FromFhirTiming |
ErrPRNNoSchedule | PRN codes have no fixed schedule | Schedule, ScheduleWithTimes |
ErrMealRelNoSchedule | meal-relative codes require a base frequency | Schedule, ScheduleWithTimes |
ErrInvalidDays | days must be positive | Schedule, ScheduleWithTimes |
ErrInvalidTimeFormat | custom time must be HH:MM format | ScheduleWithTimes |
ErrNotImplemented | not implemented | ParseInstruction |