Skip to content

Commit

Permalink
Throw Exceptions for null offsets in DateTime. Pass in offsets from S…
Browse files Browse the repository at this point in the history
…tate whenever possible. Fix most units with non-null offsets. There are still test failures to investigate. Lots of cleanup needs to be done afterward.
  • Loading branch information
lukedegruchy committed Nov 14, 2023
1 parent 10c5ad5 commit a39a75e
Show file tree
Hide file tree
Showing 20 changed files with 237 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.testng.Assert.assertTrue;

import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Calendar;
Expand Down Expand Up @@ -245,17 +246,19 @@ public void TestDateToFhirDate() {

@Test
public void TestDateTimeToFhirDateTime() {
final ZoneOffset defaultOffset = OffsetDateTime.now().getOffset();

IPrimitiveType<java.util.Date> expectedDate = new DateTimeType("2019-02-03");
IPrimitiveType<java.util.Date> actualDate = this.typeConverter
.toFhirDateTime(new DateTime("2019-02-03", null));
.toFhirDateTime(new DateTime("2019-02-03", defaultOffset));
assertEquals(expectedDate.getValue(), actualDate.getValue());

expectedDate = new DateTimeType("2019");
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", null));
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", defaultOffset));
assertEquals(expectedDate.getValue(), actualDate.getValue());

expectedDate = new DateTimeType("2019");
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", null));
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", defaultOffset));
assertEquals(expectedDate.getValueAsString(), actualDate.getValueAsString());

expectedDate = new DateTimeType("2019-10-10T01:00:00-06:00");
Expand Down Expand Up @@ -335,6 +338,8 @@ public void TestConceptToFhirCodeableConcept() {

@Test
public void TestIntervalToFhirPeriod() {
final ZoneOffset defaultOffset = OffsetDateTime.now().getOffset();

Period expected = new Period().setStartElement(new DateTimeType("2019-02-03"))
.setEndElement(new DateTimeType("2019-02-05"));
Period actual = (Period) this.typeConverter
Expand All @@ -343,7 +348,7 @@ public void TestIntervalToFhirPeriod() {

expected = new Period().setStartElement(new DateTimeType("2019")).setEndElement(new DateTimeType("2020"));
actual = (Period) this.typeConverter.toFhirPeriod(
new Interval(new DateTime("2019", null), true, new DateTime("2020", null), true));
new Interval(new DateTime("2019", defaultOffset), true, new DateTime("2020", defaultOffset), true));
assertTrue(expected.equalsDeep(actual));

actual = (Period) this.typeConverter.toFhirPeriod(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.testng.Assert.assertTrue;

import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Calendar;
Expand Down Expand Up @@ -245,17 +246,19 @@ public void TestDateToFhirDate() {

@Test
public void TestDateTimeToFhirDateTime() {
final ZoneOffset defaultOffset = OffsetDateTime.now().getOffset();

IPrimitiveType<java.util.Date> expectedDate = new DateTimeType("2019-02-03");
IPrimitiveType<java.util.Date> actualDate = this.typeConverter
.toFhirDateTime(new DateTime("2019-02-03", null));
.toFhirDateTime(new DateTime("2019-02-03", defaultOffset));
assertEquals(expectedDate.getValue(), actualDate.getValue());

expectedDate = new DateTimeType("2019");
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", null));
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", defaultOffset));
assertEquals(expectedDate.getValue(), actualDate.getValue());

expectedDate = new DateTimeType("2019");
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", null));
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", defaultOffset));
assertEquals(expectedDate.getValueAsString(), actualDate.getValueAsString());

expectedDate = new DateTimeType("2019-10-10T00:00:00-07:00");
Expand Down Expand Up @@ -334,6 +337,8 @@ public void TestConceptToFhirCodeableConcept() {

@Test
public void TestIntervalToFhirPeriod() {
final ZoneOffset defaultOffset = OffsetDateTime.now().getOffset();

Period expected = new Period().setStartElement(new DateTimeType("2019-02-03"))
.setEndElement(new DateTimeType("2019-02-05"));
Period actual = (Period) this.typeConverter
Expand All @@ -342,7 +347,7 @@ public void TestIntervalToFhirPeriod() {

expected = new Period().setStartElement(new DateTimeType("2019")).setEndElement(new DateTimeType("2020"));
actual = (Period) this.typeConverter.toFhirPeriod(
new Interval(new DateTime("2019", null), true, new DateTime("2020", null), true));
new Interval(new DateTime("2019", defaultOffset), true, new DateTime("2020", defaultOffset), true));
assertTrue(expected.equalsDeep(actual));

actual = (Period) this.typeConverter.toFhirPeriod(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.testng.Assert.assertTrue;

import java.math.BigDecimal;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Calendar;
Expand Down Expand Up @@ -244,17 +245,19 @@ public void TestDateToFhirDate() {

@Test
public void TestDateTimeToFhirDateTime() {
final ZoneOffset defaultOffset = OffsetDateTime.now().getOffset();

IPrimitiveType<java.util.Date> expectedDate = new DateTimeType("2019-02-03");
IPrimitiveType<java.util.Date> actualDate = this.typeConverter
.toFhirDateTime(new DateTime("2019-02-03", null));
.toFhirDateTime(new DateTime("2019-02-03", defaultOffset));
assertEquals(expectedDate.getValueAsString(), actualDate.getValueAsString());

expectedDate = new DateTimeType("2019");
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", null));
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", defaultOffset));
assertEquals(expectedDate.getValueAsString(), actualDate.getValueAsString());

expectedDate = new DateTimeType("2019");
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", null));
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", defaultOffset));
assertEquals(expectedDate.getValueAsString(), actualDate.getValueAsString());

expectedDate = new DateTimeType("2019-10-10T00:00:00-07:00");
Expand Down Expand Up @@ -333,6 +336,8 @@ public void TestConceptToFhirCodeableConcept() {

@Test
public void TestIntervalToFhirPeriod() {
final ZoneOffset defaultOffset = OffsetDateTime.now().getOffset();

Period expected = new Period().setStartElement(new DateTimeType("2019-02-03"))
.setEndElement(new DateTimeType("2019-02-05"));
Period actual = (Period) this.typeConverter
Expand All @@ -341,7 +346,7 @@ public void TestIntervalToFhirPeriod() {

expected = new Period().setStartElement(new DateTimeType("2019")).setEndElement(new DateTimeType("2020"));
actual = (Period) this.typeConverter.toFhirPeriod(
new Interval(new DateTime("2019", null), true, new DateTime("2020", null), true));
new Interval(new DateTime("2019", defaultOffset), true, new DateTime("2020", defaultOffset), true));
assertTrue(expected.equalsDeep(actual));

actual = (Period) this.typeConverter.toFhirPeriod(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,7 @@
import org.hl7.fhir.r5.model.Range;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.TimeType;
import org.opencds.cqf.cql.engine.runtime.Code;
import org.opencds.cqf.cql.engine.runtime.Concept;
import org.opencds.cqf.cql.engine.runtime.CqlType;
import org.opencds.cqf.cql.engine.runtime.Date;
import org.opencds.cqf.cql.engine.runtime.DateTime;
import org.opencds.cqf.cql.engine.runtime.Interval;
import org.opencds.cqf.cql.engine.runtime.Precision;
import org.opencds.cqf.cql.engine.runtime.Quantity;
import org.opencds.cqf.cql.engine.runtime.Ratio;
import org.opencds.cqf.cql.engine.runtime.Time;
import org.opencds.cqf.cql.engine.runtime.Tuple;
import org.opencds.cqf.cql.engine.runtime.*;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

Expand Down Expand Up @@ -244,17 +234,19 @@ public void TestDateToFhirDate() {

@Test
public void TestDateTimeToFhirDateTime() {
final ZoneOffset defaultOffset = OffsetDateTime.now().getOffset();

IPrimitiveType<java.util.Date> expectedDate = new DateTimeType("2019-02-03");
IPrimitiveType<java.util.Date> actualDate = this.typeConverter
.toFhirDateTime(new DateTime("2019-02-03", null));
.toFhirDateTime(new DateTime("2019-02-03", defaultOffset));
assertEquals(expectedDate.getValueAsString(), actualDate.getValueAsString());

expectedDate = new DateTimeType("2019");
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", null));
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", defaultOffset));
assertEquals(expectedDate.getValueAsString(), actualDate.getValueAsString());

expectedDate = new DateTimeType("2019");
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", null));
actualDate = this.typeConverter.toFhirDateTime(new DateTime("2019", defaultOffset));
assertEquals(expectedDate.getValueAsString(), actualDate.getValueAsString());

expectedDate = new DateTimeType("2019-10-10T00:00:00-07:00");
Expand Down Expand Up @@ -333,6 +325,8 @@ public void TestConceptToFhirCodeableConcept() {

@Test
public void TestIntervalToFhirPeriod() {
final ZoneOffset defaultOffset = OffsetDateTime.now().getOffset();

Period expected = new Period().setStartElement(new DateTimeType("2019-02-03"))
.setEndElement(new DateTimeType("2019-02-05"));
Period actual = (Period) this.typeConverter
Expand All @@ -341,7 +335,7 @@ public void TestIntervalToFhirPeriod() {

expected = new Period().setStartElement(new DateTimeType("2019")).setEndElement(new DateTimeType("2020"));
actual = (Period) this.typeConverter.toFhirPeriod(
new Interval(new DateTime("2019", null), true, new DateTime("2020", null), true));
new Interval(new DateTime("2019", defaultOffset), true, new DateTime("2020", defaultOffset), true));
assertTrue(expected.equalsDeep(actual));

actual = (Period) this.typeConverter.toFhirPeriod(null);
Expand All @@ -353,8 +347,11 @@ public void TestIntervalToFhirPeriod() {
private static final ZoneId REGINA = ZoneId.of("America/Regina"); // Saskatchewan does not have standard time (non-DST) all year round
private static final LocalDateTime DST_2023_11_01 = LocalDateTime.of(2023, Month.NOVEMBER, 1, 0, 0, 0);
private static final LocalDateTime NON_DST_2023_11_13 = LocalDateTime.of(2023, Month.NOVEMBER, 13, 0, 0, 0);
// LUKETODO: rename
@Test
public void TestIntervalToFhirPeriod2() {
final ZoneOffset defaultOffset = OffsetDateTime.now().getOffset();

final ZoneId zoneId = MONTREAL;
final LocalDateTime now = DST_2023_11_01;
final Instant instant = Instant.now(); //can be LocalDateTime
Expand All @@ -364,7 +361,7 @@ public void TestIntervalToFhirPeriod2() {

var expected = new Period().setStartElement(new DateTimeType("2019")).setEndElement(new DateTimeType("2020"));
var actual = (Period) this.typeConverter.toFhirPeriod(
new Interval(new DateTime("2019", null, offsetDateTime), true, new DateTime("2020", null, offsetDateTime), true));
new Interval(new DateTime("2019", defaultOffset, offsetDateTime), true, new DateTime("2020", defaultOffset, offsetDateTime), true));
assertTrue(expected.equalsDeep(actual));
}

Expand Down Expand Up @@ -714,4 +711,8 @@ public void TestTupleToCqlTuple() {

this.typeConverter.toCqlTuple(new Patient());
}

// public BigDecimal getBigDecimalZoneOffset() {
// return TemporalHelper.zoneToOffset(engine.getState().getEvaluationZonedDateTime().getOffset());
// }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.opencds.cqf.cql.engine.exception.InvalidOperatorArgument;
import org.opencds.cqf.cql.engine.runtime.Date;
import org.opencds.cqf.cql.engine.runtime.DateTime;
import org.opencds.cqf.cql.engine.runtime.TemporalHelper;

import java.time.ZoneOffset;
import java.time.format.DateTimeParseException;
Expand Down Expand Up @@ -46,8 +47,9 @@ public static Boolean convertsToDateTime(Object argument, ZoneOffset offset) {

else if (argument instanceof Date) {
try {
// LUKETODO: convert offset to a BigDecimal
new DateTime(
null,
TemporalHelper.zoneToOffset(offset),
((Date) argument).getDate().getYear(),
((Date) argument).getDate().getMonthValue(),
((Date) argument).getDate().getDayOfMonth(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static Object ToDateTime(Object operand, State state) {

if (operand instanceof String) {
try {
return new DateTime((String) operand, null);
return new DateTime((String) operand, state.getEvaluationDateTime().getZoneOffset());
} catch (DateTimeParseException dtpe) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ public Object visitConvertsToDate(ConvertsToDate elm, State state) {
@Override
public Object visitConvertsToDateTime(ConvertsToDateTime elm, State state) {
Object operand = visitExpression(elm.getOperand(), state);
return ConvertsToDateTimeEvaluator.convertsToDateTime(operand, null);
return ConvertsToDateTimeEvaluator.convertsToDateTime(operand, state.getEvaluationDateTime().getZoneOffset());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.opencds.cqf.cql.engine.runtime;

import org.cqframework.cql.cql2elm.Cql2ElmVisitor;
import org.cqframework.cql.cql2elm.CqlCompilerException;
import org.opencds.cqf.cql.engine.exception.CqlException;
import org.opencds.cqf.cql.engine.exception.InvalidDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -66,7 +68,14 @@ public DateTime(OffsetDateTime dateTime, Precision precision) {
public DateTime(String dateString, ZoneOffset offset) {
this(dateString, offset, OffsetDateTime.now());
}

// For unit testing only, in particular, to test DST/non-DST
// LUKETODO: do we need nowOffsetDateTime anymore?
public DateTime(String dateString, ZoneOffset offset, OffsetDateTime nowOffsetDateTime) {
if (offset == null) {
throw new CqlException("Cannot pass a null offset");
}

zoneOffset = offset;
this.defaultTimezone = TimeZone.getDefault();
this.nowOffsetDateTime = OffsetDateTime.now();
Expand Down Expand Up @@ -133,7 +142,12 @@ public DateTime(BigDecimal offset, int ... dateElements) {
}

// For unit testing only
// LUKETODO: do we need nowOffsetDateTime anymore?
DateTime(BigDecimal offset, TimeZone defaultTimezone, OffsetDateTime nowOffsetDateTime, int ... dateElements) {
if (offset == null) {
throw new CqlException("BigDecimal offset must be non-null");
}

if (dateElements.length == 0) {
throw new InvalidDateTime("DateTime must include a year");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,7 @@ public void testHighBoundary() {
Assert.assertTrue(EquivalentEvaluator.equivalent(result, new Date(2014, 12)));

result = engine.expression(library, "HighBoundaryDateTime").value();
// result OLD code: 2014-01-01T08:59:59.999 -05:00
// result NEW code: 2014-01-01T08:59:59.999 -04:00
// LUKETODO: We don't provide an offset and so the test fails
final DateTime expectedDateTime = new DateTime(null, 2014, 1, 1, 8, 59, 59, 999);
// expected DateTime OLD code: 2014-01-01T08:59:59.999 -05:00
// expected DateTime NEW code: 2014-01-01T08:59:59.999 -05:00
final DateTime expectedDateTime = new DateTime(getBigDecimalZoneOffset(), 2014, 1, 1, 8, 59, 59, 999);
Assert.assertTrue(EquivalentEvaluator.equivalent(result, expectedDateTime));

result = engine.expression(library, "HighBoundaryTime").value();
Expand Down Expand Up @@ -351,7 +346,8 @@ public void testLowBoundary() {
Assert.assertTrue(EquivalentEvaluator.equivalent(result, new Date(2014, 1)));

result = engine.expression(library, "LowBoundaryDateTime").value();
Assert.assertTrue(EquivalentEvaluator.equivalent(result, new DateTime(null, 2014, 1, 1, 8, 0, 0, 0)));
final DateTime expectedDateTime = new DateTime(getBigDecimalZoneOffset(), 2014, 1, 1, 8, 0, 0, 0);
Assert.assertTrue(EquivalentEvaluator.equivalent(result, expectedDateTime));

result = engine.expression(library, "LowBoundaryTime").value();
Assert.assertTrue(EquivalentEvaluator.equivalent(result, new Time(10, 30, 0, 0)));
Expand Down Expand Up @@ -552,7 +548,8 @@ public void testPredecessor() {
// Assert.assertEquals("cm", ((Quantity) result).getUnit());

result = engine.expression(library, "PredecessorOfJan12000").value();
Assert.assertTrue(EquivalentEvaluator.equivalent(result, new DateTime(null, 1999, 12, 31)));
final DateTime expectedDateTime = new DateTime(getBigDecimalZoneOffset(), 1999, 12, 31);
Assert.assertTrue(EquivalentEvaluator.equivalent(result, expectedDateTime));

result = engine.expression(library, "PredecessorOfNoon").value();
Assert.assertTrue(EquivalentEvaluator.equivalent(result, new Time(11, 59, 59, 999)));
Expand Down Expand Up @@ -745,7 +742,8 @@ public void testSuccessor() {
assertThat((BigDecimal)result, comparesEqualTo(new BigDecimal("1.01000001")));

result = engine.expression(library, "SuccessorOfJan12000").value();
Assert.assertTrue(EquivalentEvaluator.equivalent(result, new DateTime(null, 2000, 1, 2)));
final DateTime expectedDateTime = new DateTime(getBigDecimalZoneOffset(), 2000, 1, 2);
Assert.assertTrue(EquivalentEvaluator.equivalent(result, expectedDateTime));

result = engine.expression(library, "SuccessorOfNoon").value();
Assert.assertTrue(EquivalentEvaluator.equivalent(result, new Time(12, 0, 0, 1)));
Expand Down
Loading

0 comments on commit a39a75e

Please sign in to comment.