Skip to content

The engine's answers depend on the server's time zone, and a dormant translator bug (CQL issue 1871) has no guard #845

Closed

Description

@Taleef7

Two engine behaviours that CI cannot see today: results that depend on the server's time zone, and a translator bug that stays dormant only until the CQL engine is upgraded.

1. The server's time zone changes results

  • cql-exec-fhir parses a FHIR dateTime with cql.DateTime.parse(data). When the string has no offset, such as a date-only "2026-06-30", cql-execution's DateTime constructor fills in the host's current offset (new Date().getTimezoneOffset()).
  • fqm-execution builds the measurement period and the execution time at offset 0.
  • CQL says an offset-less value takes the evaluation request's offset. Upstream agreed in Birthdate Timezone projecttacoma/fqm-execution#259, and Use execution timezone offset for ToDateTime(String) cqframework/cql-execution#387 fixed ToDateTime(String) but deliberately left patient-data parsing unchanged.
  • Measured through calculateOfficial: on a CMS165 test case with the hypertension onset made date-only 2026-06-30, the patient is out of the IPP under UTC, US Eastern and Hawaii, and in the IPP under Asia/Tokyo.
  • US offsets change answers at the primitive level too. A date-only 2026-06-30 before 2026-07-01T00:00Z is true at offset 0 and null at −5 or −10, while the mirrored after stays true. Whether a measure flips depends on which side of a comparison the ELM puts the value.
  • The authored engine (packages/measure-engine/src/cql/cql-execution-engine.ts) builds its period and "now" from offset-less strings, so it takes the host offset as well.
  • Nothing sets TZ: not the image (backend-ts/Dockerfile), the deploy workflows, or the test scripts. The base image defaults to UTC and CI runners are UTC, so production is probably UTC, but the hosting platform could override it and nothing reports it. A laptop in US Eastern runs the suite at −4/−5.
  • The deck gate cannot catch this. The CMS decks hold no date-only clinical values (only birth dates, which are Dates), and the gate scores identically under UTC, −5 and −10.

2. A dormant translator bug becomes live on any engine upgrade (cqframework/clinical_quality_language#1871)

  • The shipped QICoreCommon 4.0.000 ELM compiles abatementInterval's final else null into an interval with closed null bounds. Every official measure and every 2027 translation carries it.
  • On cql-execution 3.3.2 (ours), a resolved Condition with no abatement date has a null prevalence interval, which is correct: never prevalent.
  • On cql-execution 4.0.0-beta.1, the same ELM gives [onset, 9999-12-31], so the Condition is prevalent forever. Repairing the else branch to Null fixes 4.0.
  • It is used by:
    • the IPP/exclusion diagnoses of CMS122, CMS125, CMS130 and CMS165;
    • hospice, frailty, advanced illness and palliative care;
    • CMS2's bipolar history;
    • CMS137's first SUD episode.
  • It is safe today: fqm-execution 1.8.5 and its master both pin cql-execution ^3.3.2. But an engine upgrade would change results with nothing failing.

Fix

  • Pin UTC wherever the process starts: ENV TZ=UTC in the image, plus the server entry and the test runner, so a laptop or a platform setting cannot change it.
  • Report the process's UTC offset on /health, and warn when it is not 0.
  • Add a test: the CMS165 date-only onset, evaluated in a child process under a non-UTC TZ, gives the UTC answer. Mutation-check it: the test must fail without the pin.
  • Add a test: the shipped QICoreCommon.prevalenceInterval returns null for a resolved Condition with no abatement date, so an engine change that alters it fails CI.

Acceptance

  • Both tests are in the default suite.
  • /health shows offset 0 on TWH and Maui after deploy.

Activity

  1. added this to the Ready for January milestone on Oct 19, 2026
  2. added
    bugSomething isn't working
    cql-engineCQL / measure-evaluation engine
    on Oct 19, 2026
  3. changed the title [-]The engine's answers depend on the server's time zone, and a dormant translator bug (#1871) has no guard[/-] [+]The engine's answers depend on the server's time zone, and a dormant translator bug (CQL issue 1871) has no guard[/+] on Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcql-engineCQL / measure-evaluation engine

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions