Skip to content

Equate open and closed Time interval boundaries - #148

Open
bryantaustin13 wants to merge 1 commit into
mainfrom
time-interval-open-closed
Open

bryantaustin13 wants to merge 1 commit into
mainfrom
time-interval-open-closed

Conversation

@bryantaustin13

Copy link
Copy Markdown
Contributor

Part of #85.

Problem

#110 made the comparator treat open and closed numeric interval boundaries as equivalent: [1.0, 4.0) equals [1.0, 3.99999999]. Time boundaries were left on the structural comparison, where the closed flags must match exactly. So an engine that answers with an open Time boundary fails against an expected closed one, even though both describe the same interval.

The two tests that conf/cql-execution-local.json still skips for #85 are this case. With their skips removed, cql-execution returns:

Test Expected cql-execution returns
ExceptTimeInterval Interval[@T05:59:59.999, @T08:59:59.998] Interval[@T05:59:59.999, @T08:59:59.999)
ExceptTime2 Interval[@T11:00:00.000, @T11:59:59.999] Interval(@T10:59:59.999, @T11:59:59.999]

CQL 1.5.3 Predecessor (Successor is the same, with adding):

For Date, DateTime, and Time values, predecessor is equivalent to subtracting a time-unit quantity for the lowest specified precision of the value.

and End:

If the high boundary of the interval is open, this operator returns the predecessor of the high value of the interval.

So the end of [@T05:59:59.999, @T08:59:59.999) is @T08:59:59.998, and the two forms are the same interval.

Change

In boundariesEqual (src/shared/results-utils.ts), when exactly one of two Time-literal boundaries is open, the open one moves one unit of its own precision inwards before the values are compared:

  • one hour for @T09
  • one minute for @T09:00
  • one second for @T09:00:00
  • one millisecond for @T09:00:00.000

One fractional digit is still millisecond precision, so .1 is 100 ms.

Deliberately not equated:

  • Boundaries at different precisions: @T08:59:59 is not treated as the closed form of @T09:00).
  • A move that would leave the day: there is no Time before @T00:00:00.000 or after @T23:59:59.999.
  • Date and DateTime boundaries: they keep the structural comparison. They would need calendar arithmetic and timezone offsets.

When both flags match, nothing changes.

Verification

Follow-up

Once this merges, the two ExceptTime2 / ExceptTimeInterval skips can be removed from conf/cql-execution-local.json. They are the config's last skips for #85. Of the other tests the issue was raised for, IntegerIntervalExcept1to3 and Except12 already pass on main. DecimalIntervalExcept1to3 is now skipped there for a different reason (FHIR-59229, CQL 2.0 Decimal semantics).

🤖 Generated with Claude Code

#110 equated open and closed numeric interval boundaries, but Time
boundaries kept the structural comparison, where the closed flags must
match. An engine answering Interval[@T05:59:59.999, @t08:59:59.999) for
an expected Interval[@T05:59:59.999, @t08:59:59.998] failed, though both
are the same interval: the predecessor of a Time is one unit of its
lowest specified precision (CQL Predecessor/Successor).

When exactly one of two Time-literal boundaries is open, it now moves
one unit of its own precision inwards (hour, minute, second or
millisecond) before the values are compared. Boundaries at different
precisions, and moves that would leave the day, are not equated. Date
and DateTime boundaries keep the structural comparison.

On cql-execution this fixes ExceptTimeInterval and ExceptTime2 and
changes nothing else; results on the Java engine are unchanged.

Part of #85.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant