Skip to content

Fix Successor/Predecessor of Date, DateTime, and Time to return null on overflow/underflow (#1734, #1733)#1806

Merged
antvaset merged 3 commits into
mainfrom
fix/successor-predecessor-overflow-null
Jul 12, 2026
Merged

Fix Successor/Predecessor of Date, DateTime, and Time to return null on overflow/underflow (#1734, #1733)#1806
antvaset merged 3 commits into
mainfrom
fix/successor-predecessor-overflow-null

Conversation

@c-schuler

Copy link
Copy Markdown
Contributor

Problem

successor of DateTime(9999,12,31,23,59,59,999) and successor of @T23:59:59.999 threw an EvaluationError (TypeOverflow / "year 10000 falls above the accepted bounds") instead of returning null. Same for predecessor of the minimum DateTime/Time (TypeUnderflow). The current CQL reference is explicit:

"If the argument is already the maximum value for the type, a null is returned. If the result of the operation cannot be represented (i.e. would result in an overflow), the result is null."

The engine's own doc comments still said "a run-time error is thrown" - a leftover from an earlier version of the spec.

Fix

SuccessorEvaluator and PredecessorEvaluator now return null when a Date, DateTime, or Time overflows/underflows the representable range:

  • Date/DateTime: compute the shifted temporal, then a getYear() bounds check against a named MAX_YEAR/MIN_YEAR constant is null if out of [1, 9999] (no exception-driven control flow).
  • Time: the precision-based boundary checks return null instead of throwing.
  • Updated the stale doc comments to match the current spec.

Also fixes a latent bug in SuccessorEvaluator's Time overflow detection: the MINUTE and SECOND checks compared getMinute() == 23 instead of == 59, so successor of @T23:59 wrapped to @T00:00 instead of overflowing, while @T23:23 was wrongly treated as overflow.

Scope

Limited to the temporal types (Date, DateTime, Time), which is what #1734/#1733 and the conformance tests cover. Numeric types (Integer/Long/Decimal/Quantity) still throw on overflow; per the current spec they should also return null, but that requires reworking the checkMaxDecimal/checkMinDecimal helpers and the internal (value, quantity) overloads (used by interval-boundary computation) to propagate null, and there are no conformance tests for it. Left as a separate, carefully-scoped follow-up.

Tests

Un-doctored the vendored engine-fhir copy of CqlArithmeticFunctionsTest.xml - these had been marked invalid="true" to match the old throwing behavior:

Test Expression Before After
SuccessorOverflowDt successor of DateTime(9999,12,31,23,59,59,999) invalid null
SuccessorOverflowT successor of @T23:59:59.999 invalid null
PredecessorUnderflowDt predecessor of DateTime(0001,1,1,0,0,0,0) invalid null
PredecessorUnderflowT predecessor of @T00:00:00.000 invalid null

Validated: spotlessApply, :engine:detekt, engine arithmetic + Successor/Predecessor evaluator unit tests, and both vendored suites (R4 + DSTU3) — all green. Full :engine:jvmTest + :engine-fhir:test run completing.

Upstream follow-up

The canonical cql-tests (https://github.com/cqframework/cql-tests) suite still marks these four invalid="true", which now contradicts the current spec. A companion cql-tests PR is needed to change them to expect null; until then the conformance dashboard will report them as failing (same situation as the Exp/Ln fix, #1716).

Fixes #1734
Fixes #1733.

@c-schuler c-schuler self-assigned this Jul 9, 2026
@c-schuler c-schuler added the bug label Jul 9, 2026
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Related Issues

The following open issues may be related to this PR:

Issue Title Score Matched Terms
#1505 'Collapse' on at least 2 closed datetime intervals without a high value always fails 57 "com cqframework", "falls above", "https github", "above accepted", "github com", "year 10000", "accepted bounds", "10000 falls", error, true, cqframework, github, quantity, falls, before, com, exception, expect, above, datetime, behavior, year, max, interval, thrown, null, until, 0001, https, 10000, bounds, per, date, value, 9999, accepted, src (path), evaluator (path), opencds (path), engine (path), executing (path), main (path), org (path), cql (path), cqf (path), elm (path)
#1794 Point from Interval[null, null] is returning null instead of erroring 42.5 "instead returning", "time error", "invalid true", "returning null", "run time", "null instead", result, error, xml, true, instead, maximum, return, bug, run, returning, version, time, max, invalid, interval, spec, min, null, expression, https, per, tests, fhirpath (path), org (path), cql (path), hl7 (path), test (path)
#734 Width allows Interval as a parameter 36.5 "integer long", "decimal quantity", "date datetime", "long decimal", "datetime time", result, error, type, integer, quantity, boundary, decimal, long, argument, datetime, time, types, interval, null, https, tests, date, value, successor (path), predecessor (path), engine (path), org (path), cql (path), elm (path), hl7 (path)
#1400 patientBirthDatePropertyName marked as deprecated but still required for SystemFunctionResolver 32.5 "com cqframework", "https github", "github com", "cqframework cql", error, instead, cqframework, github, com, marked, expect, time, problem, behavior, against, spec, null, https, returned, cannot, date, src (path), functions (path), main (path), org (path), cql (path), elm (path), hl7 (path)
#1377 Use a git module for the XML-based tests 32.5 "com cqframework", "cql tests", "fhir test", "https github", "github com", "engine fhir", "cqframework cql", updated, xml, cqframework, github, com, fhir, https, suite, based, tests, engine (path), cql (path), test (path)

Tip: If this PR addresses any of these issues, please link them using Closes #NNN or Refs #NNN in the PR description.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

Formatting check succeeded!

@codecov

codecov Bot commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 34.78261% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.78%. Comparing base (9f93b18) to head (6f912e0).

Files with missing lines Patch % Lines
...cqf/cql/engine/elm/executing/SuccessorEvaluator.kt 33.33% 2 Missing and 6 partials ⚠️
...f/cql/engine/elm/executing/PredecessorEvaluator.kt 36.36% 1 Missing and 6 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #1806      +/-   ##
============================================
+ Coverage     68.76%   68.78%   +0.02%     
  Complexity     1699     1699              
============================================
  Files           536      536              
  Lines         30554    30540      -14     
  Branches       6987     6993       +6     
============================================
- Hits          21009    21007       -2     
+ Misses         6989     6971      -18     
- Partials       2556     2562       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@antvaset
antvaset enabled auto-merge (squash) July 12, 2026 22:48
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
59.6% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@antvaset
antvaset merged commit 159be56 into main Jul 12, 2026
9 of 10 checks passed
@antvaset
antvaset deleted the fix/successor-predecessor-overflow-null branch July 12, 2026 23:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Operation Predecessor DateTime and Time overflow does not return null Operation Predecessor DateTime and Time underflow does not return null

4 participants