You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
CQL intervals may have null boundaries, and the meaning depends on whether the boundary is closed or open:
Closed (Interval[null, 5]) — the boundary is the minimum/maximum value of the point type. Unambiguous, and every part of the spec agrees.
Open (Interval(null, 5]) — the boundary is unknown. This is where the spec has historically been inconsistent.
FHIR-35915 ("Clarify interval computation semantics with open null boundaries", Technical Correction, resolved Persuasive against CQL 1.5) settles it: an open null boundary is an uncertainty over a bounded range, not a plain null. The normalization given in the ticket:
If interval.low is null then interval.low = Uncertainty(minimumValue, interval.high)
If interval.high is null then interval.high = Uncertainty(interval.low, maximumValue)
The bound matters. In Interval(null, 7] the unknown low is not arbitrary — it is at most 7, because the interval must be valid. Many comparisons are therefore determinate even though a boundary is unknown, and returning null for them is wrong.
The ticket was accepted on the grounds that the JavaScript reference implementation (cql-execution) already behaves this way.
The problem
The correction was applied to Chapter 5 (Language Semantics) only. Other parts of the spec still describe plain null propagation:
Appendix B — CQL Reference: Start returns null when the low boundary is open and the low value is null; Overlaps, In, Includes, Meets, Before/After are all defined as "using the semantics described in the Start and End operators".
ELM schema documentation (expression.xsd, Interval): states that computations involving an open null boundary will result in null.
An implementation written against Appendix B alone — which is the natural reading, since that's where the operator semantics live — will produce null where the accepted semantics require true or false.
Worked examples
Cases where the two readings diverge (X overlaps Y):
Expression
Appendix B
FHIR-35915
Interval(null, 7] overlaps Interval[5, 10]
null
true
Interval[7, null) overlaps Interval[5, 10]
null
true
Interval(null, 5] meets after Interval[11, null)
null
false
Cases where null is genuinely correct under both readings — the uncertainty range really does straddle the comparison point:
Expression
Result
Interval(null, 7] overlaps Interval[1, 6]
null
(If the low boundary were 6, X is the unit interval {7} and there is no overlap; if it were 2, there is. Genuinely unknown.)
Cases that were already correct under both readings, worth keeping as regression guards:
Expression
Result
Interval(null, 3] overlaps Interval[5, 10]
false
Interval[12, null) overlaps Interval[5, 10]
false
Test expressions from the ticket itself:
define TestIntervalNull1: end of (Interval[1, 10] intersect Interval[5, null)) <=10// true
define TestIntervalNull2: end of (Interval[1, 10] intersect Interval[5, null)) >=5// true
define TestIntervalNull3: end of (Interval[1, 10] intersect Interval[5, null)) >10// false
define TestIntervalNull4: end of (Interval[1, 10] intersect Interval[5, null)) <5// false
Investigation tasks
Determine what Blaze currently returns for the divergent cases above. Add them as tests first, marked with expected values per FHIR-35915, so the gap is visible.
Check whether the CQL conformance suite (cqframework/cql-tests) actually carries TestMeetsAfterNull with the corrected false and the four TestIntervalNull* cases. A Persuasive resolution means accepted, not necessarily that the tests were regenerated — if they're absent, the suite won't catch this either way and we need our own coverage.
Compare against cql-execution (JS, cited in the ticket as already conforming) and the Java engine in cqframework/clinical_quality_language, which may not have been updated. Knowing where each stands tells us what conformance actually looks like in practice.
Decide on the boundary representation. The cheapest route is a boundary value that is either a point or a [lo, hi] range, with the comparison operators returning true/false when the range does not straddle and null when it does. Every interval operator then falls out without special-casing.
Check whether Blaze already has an uncertainty representation for date/time imprecision (duration between on partial dates produces uncertainties by the same Chapter 5 rules). If so, reuse it rather than introducing a second mechanism.
Clarify what start of / end of should return to CQL-land when the boundary is uncertain. Chapter 5 says uncertainty is implicitly converted and does not surface in the syntax, so this needs a defined answer rather than leaking an internal type.
Scope check: which operators are affected beyond the comparison set? At minimum intersect, union, except, width of, duration/difference of, collapse, expand.
Follow-up
Consider filing a separate HL7 JIRA ticket noting that Appendix B's Start/End definitions and the ELM schema documentation were not updated per FHIR-35915. Narrow, non-substantive, and it removes the trap for the next implementer.
Context
CQL intervals may have
nullboundaries, and the meaning depends on whether the boundary is closed or open:Interval[null, 5]) — the boundary is the minimum/maximum value of the point type. Unambiguous, and every part of the spec agrees.Interval(null, 5]) — the boundary is unknown. This is where the spec has historically been inconsistent.FHIR-35915 ("Clarify interval computation semantics with open null boundaries", Technical Correction, resolved Persuasive against CQL 1.5) settles it: an open null boundary is an uncertainty over a bounded range, not a plain
null. The normalization given in the ticket:The bound matters. In
Interval(null, 7]the unknown low is not arbitrary — it is at most7, because the interval must be valid. Many comparisons are therefore determinate even though a boundary is unknown, and returningnullfor them is wrong.The ticket was accepted on the grounds that the JavaScript reference implementation (
cql-execution) already behaves this way.The problem
The correction was applied to Chapter 5 (Language Semantics) only. Other parts of the spec still describe plain null propagation:
Startreturnsnullwhen the low boundary is open and the low value isnull;Overlaps,In,Includes,Meets,Before/Afterare all defined as "using the semantics described in theStartandEndoperators".expression.xsd,Interval): states that computations involving an open null boundary will result innull.An implementation written against Appendix B alone — which is the natural reading, since that's where the operator semantics live — will produce
nullwhere the accepted semantics requiretrueorfalse.Worked examples
Cases where the two readings diverge (
X overlaps Y):Interval(null, 7] overlaps Interval[5, 10]nulltrueInterval[7, null) overlaps Interval[5, 10]nulltrueInterval(null, 5] meets after Interval[11, null)nullfalseCases where
nullis genuinely correct under both readings — the uncertainty range really does straddle the comparison point:Interval(null, 7] overlaps Interval[1, 6]null(If the low boundary were
6,Xis the unit interval{7}and there is no overlap; if it were2, there is. Genuinely unknown.)Cases that were already correct under both readings, worth keeping as regression guards:
Interval(null, 3] overlaps Interval[5, 10]falseInterval[12, null) overlaps Interval[5, 10]falseTest expressions from the ticket itself:
Investigation tasks
cqframework/cql-tests) actually carriesTestMeetsAfterNullwith the correctedfalseand the fourTestIntervalNull*cases. A Persuasive resolution means accepted, not necessarily that the tests were regenerated — if they're absent, the suite won't catch this either way and we need our own coverage.cql-execution(JS, cited in the ticket as already conforming) and the Java engine incqframework/clinical_quality_language, which may not have been updated. Knowing where each stands tells us what conformance actually looks like in practice.[lo, hi]range, with the comparison operators returningtrue/falsewhen the range does not straddle andnullwhen it does. Every interval operator then falls out without special-casing.duration betweenon partial dates produces uncertainties by the same Chapter 5 rules). If so, reuse it rather than introducing a second mechanism.start of/end ofshould return to CQL-land when the boundary is uncertain. Chapter 5 says uncertainty is implicitly converted and does not surface in the syntax, so this needs a defined answer rather than leaking an internal type.intersect,union,except,width of,duration/difference of,collapse,expand.Follow-up
Consider filing a separate HL7 JIRA ticket noting that Appendix B's
Start/Enddefinitions and the ELM schema documentation were not updated per FHIR-35915. Narrow, non-substantive, and it removes the trap for the next implementer.References
Start,End,Overlaps)cqframework/cql-tests