Handling Arbitrary Units Like 'Per Shift' That UCUM Doesn't Cover

Editorial illustration in zellij-islamic-geometric style depicting four zellij tiles of FHIR value shapes for arbitrary units

UCUM covers real physical quantities well. "Voids per shift", "steps per day since discharge", "medication administrations per encounter" — clinical events per organizational time bucket — are not physical quantities and not in UCUM. The FHIR spec has an answer for that gap, but it is a specific pattern that many teams miss. The site's Clinical UCUM unit converter recognises canonical UCUM only and leaves arbitrary counts to a different mechanism. For the wider FHIR framing, our broader FHIR coverage has more.

The Two Options FHIR Gives You

  • Curly-brace annotation on a UCUM base — {voids}/8.h for voids per eight-hour shift
  • Non-Quantity value shape — Observation.valueString, valueCodeableConcept, or valueInteger with a code

Both are valid FHIR. Each has different downstream implications.

When Annotation Works

If the underlying quantity is a real number with dimensional meaning — a count per time — annotation on a UCUM unit is the safest choice.

{voids}/8.h — the base is per-time; the count is annotated. Downstream converters that ignore annotations still know they are dealing with a per-time rate. Downstream systems that read the annotation get the human context.

For the canonical set to build on, canonical UCUM units the FHIR spec assumes you'll use is the entry.

When Annotation Doesn't Work

Some "units" are not units at all. Care team names ("per nurse"), organizational buckets ("per shift" without a defined duration), or classification tiers (mild/moderate/severe) do not have numeric dimension.

For these, use valueString, valueCodeableConcept, or valueInteger — not valueQuantity with a fake unit. Forcing them into UCUM produces payloads that downstream unit converters mangle.

The valueInteger Pattern

For clean counts without a per-time dimension — "number of missed doses" — valueInteger is the right shape. Do not attach a fake unit.

Downstream logic that expects a Quantity has to be prepared for these alternatives. That is the trade: the payload is cleaner but the receivers have to handle multiple value shapes. For the writing side, UCUM in Observation.valueQuantity: the strict version covers the strict pattern.

The valueCodeableConcept Pattern

For coded categorical results — "positive/negative", severity tiers — valueCodeableConcept with a proper coded value is the right choice. Not valueQuantity with a "positive/negative code."

That is what the FHIR value[x] polymorphism exists for. Use the correct shape.

The Storage Question

If your source system emits arbitrary units, you have two choices:

  • Rewrite at ingestion to a FHIR-native shape
  • Store the original arbitrary unit alongside a FHIR-clean shape

The second is safer if the source is authoritative. For the trade-off, storing quantities: original unit, canonical unit, or both covers it.

What Not To Do

  • Do not invent a private UCUM extension — UCUM extension via curly-brace is standardized, private extensions are not
  • Do not use valueQuantity with a made-up code — every consumer will treat it as UCUM
  • Do not silently drop the original unit when converting — you cannot roundtrip

Documenting Arbitrary Units

For any arbitrary unit you keep in your data model, document what it means, when it applies, and what the receiver should do with it. That documentation is what makes the alternative-value-shape choice defensible.

The Short Version

UCUM does not cover organizational buckets or non-dimensional counts. Curly-brace annotation handles annotated real units. Use valueInteger, valueString, or valueCodeableConcept for what UCUM cannot express. Never invent codes.

Zellij-geometric diagram of four value shapes — Quantity + annotation, valueInteger, valueString, valueCodeableConcept — arranged as tessellated tiles with per-shape use cases annotated, in indigo and violet accents on ivory

Sources

Aaliyah Jenkins

Interoperability specialist in Indianapolis. Covers MLLP, HL7v2 transport, and the parts of healthcare integration that haven't changed in 20 years.