fix problem with failing date/time comparison tests

This commit is contained in:
Grahame Grieve 2019-05-20 16:01:11 +10:00
parent 58724b3694
commit 2b53f0368f
4 changed files with 76 additions and 81 deletions

View File

@ -861,73 +861,32 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
}
/**
* This method implements a datetime equality check using the rules as defined by FHIRPath.
* This method implements a datetime equality check using the rules as defined by FHIRPath (R2)
*
* This method returns:
* <ul>
* <li>true if the given datetimes represent the exact same instant with the same precision (irrespective of the timezone)</li>
* <li>true if the given datetimes represent the exact same instant but one includes milliseconds of <code>.[0]+</code> while the other includes only SECONDS precision (irrespecitve of the timezone)</li>
* <li>true if the given datetimes represent the exact same year/year-month/year-month-date (if both operands have the same precision)</li>
* <li>false if both datetimes have equal precision of MINUTE or greater, one has no timezone specified but the other does, and could not represent the same instant in any timezone</li>
* <li>null if both datetimes have equal precision of MINUTE or greater, one has no timezone specified but the other does, and could potentially represent the same instant in any timezone</li>
* <li>false if the given datetimes have the same precision but do not represent the same instant (irrespective of timezone)</li>
* <li>null otherwise (since these datetimes are not comparable)</li>
* </ul>
* Caveat: this implementation assumes local timezone for unspecified timezones
*/
public Boolean equalsUsingFhirPathRules(BaseDateTimeType theOther) {
BaseDateTimeType me = this;
// Per FHIRPath rules, we compare equivalence at the lowest precision of the two values,
// so if we need to, we'll clone either side and reduce its precision
int lowestPrecision = Math.min(me.getPrecision().ordinal(), theOther.getPrecision().ordinal());
TemporalPrecisionEnum lowestPrecisionEnum = TemporalPrecisionEnum.values()[lowestPrecision];
if (me.getPrecision() != lowestPrecisionEnum) {
me = new DateTimeType(me.getValueAsString());
me.setPrecision(lowestPrecisionEnum);
}
if (theOther.getPrecision() != lowestPrecisionEnum) {
theOther = new DateTimeType(theOther.getValueAsString());
theOther.setPrecision(lowestPrecisionEnum);
}
if (me.hasTimezoneIfRequired() != theOther.hasTimezoneIfRequired()) {
if (me.getPrecision() == theOther.getPrecision()) {
if (me.getPrecision().ordinal() >= TemporalPrecisionEnum.MINUTE.ordinal() && theOther.getPrecision().ordinal() >= TemporalPrecisionEnum.MINUTE.ordinal()) {
boolean couldBeTheSameTime = couldBeTheSameTime(me, theOther) || couldBeTheSameTime(theOther, me);
if (!couldBeTheSameTime) {
return false;
}
}
}
TemporalPrecisionEnum mp = this.myPrecision == TemporalPrecisionEnum.MILLI ? TemporalPrecisionEnum.SECOND : this.myPrecision;
TemporalPrecisionEnum op = theOther.myPrecision == TemporalPrecisionEnum.MILLI ? TemporalPrecisionEnum.SECOND : theOther.myPrecision;
TemporalPrecisionEnum cp = (mp.compareTo(op) < 0) ? mp : op;
FastDateFormat df = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS");
String ms = df.format(this.getValue());
String os = df.format(theOther.getValue());
if (!sub(ms, cp.stringLength()).equals(sub(os, cp.stringLength())))
return false;
if (mp != op)
return null;
if (this.myPrecision == TemporalPrecisionEnum.MILLI || theOther.myPrecision == TemporalPrecisionEnum.MILLI) {
float mf = Float.parseFloat(ms.substring(17));
float of = Float.parseFloat(os.substring(17));
if (mf != of)
return false;
}
return true;
}
// Same precision
if (me.getPrecision() == theOther.getPrecision()) {
if (me.getPrecision().ordinal() >= TemporalPrecisionEnum.MINUTE.ordinal()) {
long leftTime = me.getValue().getTime();
long rightTime = theOther.getValue().getTime();
return leftTime == rightTime;
} else {
String leftTime = me.getValueAsString();
String rightTime = theOther.getValueAsString();
return leftTime.equals(rightTime);
}
}
// Both represent 0 millis but the millis are optional
if (((Integer)0).equals(me.getMillis())) {
if (((Integer)0).equals(theOther.getMillis())) {
if (me.getPrecision().ordinal() >= TemporalPrecisionEnum.SECOND.ordinal()) {
if (theOther.getPrecision().ordinal() >= TemporalPrecisionEnum.SECOND.ordinal()) {
return me.getValue().getTime() == theOther.getValue().getTime();
}
}
}
}
return false;
private String sub(String ms, int i) {
return ms.length() < i ? ms : ms.substring(0, i);
}
private boolean couldBeTheSameTime(BaseDateTimeType theArg1, BaseDateTimeType theArg2) {
@ -944,9 +903,13 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
}
boolean hasTimezoneIfRequired() {
return getPrecision().ordinal() <= TemporalPrecisionEnum.DAY.ordinal() ||
getTimeZone() != null;
}
return getPrecision().ordinal() <= TemporalPrecisionEnum.DAY.ordinal() ||
getTimeZone() != null;
}
boolean hasTimezone() {
return getTimeZone() != null;
}
}

View File

@ -79,4 +79,20 @@ public enum TemporalPrecisionEnum {
return myCalendarConstant;
}
/**
* Given the standard string representation - YYYY-DD-MMTHH:NN:SS.SSS - how long is the string for the stated precision?
* @return
*/
public int stringLength() {
switch (this) {
case YEAR: return 4;
case MONTH: return 7;
case DAY: return 10;
case MINUTE: return 16;
case SECOND: return 19;
case MILLI: return 23;
}
return 0; // ??
}
}

View File

@ -232,8 +232,8 @@
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate = @1974-12-25</expression><output type="boolean">true</output></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate != @1974-12-25T12:34:00</expression></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate != @1974-12-25T12:34:00-10:00</expression><output type="boolean">true</output></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate != @1974-12-25T12:34:00+10:00</expression><output type="boolean">true</output></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate != @1974-12-25T12:34:00Z</expression><output type="boolean">true</output></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate != @1974-12-25T12:34:00+10:00</expression></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate != @1974-12-25T12:34:00Z</expression></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate != @T12:14:15</expression><output type="boolean">true</output></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate != @T12:14</expression><output type="boolean">true</output></test>
<test name="testLiteralDate" inputfile="patient-example.xml"><expression>Patient.birthDate &lt; today()</expression><output type="boolean">true</output></test>
@ -618,7 +618,7 @@
<test inputfile="patient-example.xml"><expression>@2012-04-15 = @2012-04-16</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15 = @2012-04-15T10:00:00</expression></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15T15:00:00 = @2012-04-15T10:00:00</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15T15:00:00Z = @2012-04-15T10:00:00</expression></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15T15:00:00Z = @2012-04-15T10:00:00</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15T15:00:00+02:00 = @2012-04-15T16:00:00+03:00</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>name = name</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>name.take(2) = name.take(2).first() | name.take(2).last()</expression><output type="boolean">true</output></test>
@ -641,7 +641,7 @@
<test inputfile="patient-example.xml"><expression>@2012-04-15 != @2012-04-16</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15 != @2012-04-15T10:00:00</expression></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15T15:00:00 != @2012-04-15T10:00:00</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15T15:00:00Z != @2012-04-15T10:00:00</expression></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15T15:00:00Z != @2012-04-15T10:00:00</expression><output type="boolean">true</output></test>
<test inputfile="patient-example.xml"><expression>@2012-04-15T15:00:00+02:00 != @2012-04-15T16:00:00+03:00</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>name != name</expression><output type="boolean">false</output></test>
<test inputfile="patient-example.xml"><expression>name.take(2) != name.take(2).first() | name.take(2).last()</expression><output type="boolean">false</output></test>

View File

@ -16,15 +16,31 @@ public class BaseDateTimeTypeTest {
* </ul>
*/
@Test
public void equalsUsingFhirPathRules() {
public void equalsUsingFhirPathRulesInSpec() {
// from the spec
assertTrue( compareDateTimes("2012", "2012"));
assertFalse(compareDateTimes("2012", "2013"));
assertNull( compareDateTimes("2012-01", "2012"));
assertNull( compareDateTimes("2012-01-01", "2012-01-01T00:00:00"));
assertTrue( compareDateTimes("2012-01-01T10:30:00", "2012-01-01T10:30:00"));
assertFalse(compareDateTimes("2012-01-01T10:30:00", "2012-01-01T10:31:00"));
assertTrue( compareDateTimes("2012-01-01T10:30:31.0", "2012-01-01T10:30:31"));
assertFalse(compareDateTimes("2012-01-01T10:30:31.1", "2012-01-01T10:30:31"));
assertFalse(compareDateTimes("2017-11-05T01:30:00.0-04:00", "2017-11-05T01:15:00.0-05:00"));
assertTrue(compareDateTimes("2017-11-05T01:30:00.0-04:00", "2017-11-05T00:30:00.0-05:00"));
}
@Test
public void equalsUsingFhirPathRulesOther() {
// Exact same - Same timezone
assertTrue(compareDateTimes("2001-01-02T11:22:33.444Z", "2001-01-02T11:22:33.444Z"));
assertTrue( compareDateTimes("2001-01-02T11:22:33.444Z", "2001-01-02T11:22:33.444Z"));
// Exact same - Different timezone
assertTrue(compareDateTimes("2001-01-02T11:22:33.444-03:00", "2001-01-02T10:22:33.444-04:00"));
assertTrue( compareDateTimes("2001-01-02T11:22:33.444-03:00", "2001-01-02T10:22:33.444-04:00"));
// Exact same - Dates
assertTrue(compareDateTimes("2001", "2001"));
assertTrue(compareDateTimes("2001-01", "2001-01"));
assertTrue(compareDateTimes("2001-01-02", "2001-01-02"));
assertTrue( compareDateTimes("2001", "2001"));
assertTrue( compareDateTimes("2001-01", "2001-01"));
assertTrue( compareDateTimes("2001-01-02", "2001-01-02"));
// Same precision but different values - Dates
assertFalse(compareDateTimes("2001", "2002"));
assertFalse(compareDateTimes("2001-01", "2001-02"));
@ -34,12 +50,12 @@ public class BaseDateTimeTypeTest {
assertFalse(compareDateTimes("2001-01-02T11:22:33.445Z", "2001-01-02T11:22:33.444Z"));
// FHIRPath tests:
assertFalse(compareDateTimes("1974-12-25", "1974-12-25T12:34:00+10:00"));
assertFalse(compareDateTimes("1974-12-25T12:34:00+10:00", "1974-12-25"));
assertNull( compareDateTimes("1974-12-25", "1974-12-25T12:34:00+10:00"));
assertNull( compareDateTimes("1974-12-25T12:34:00+10:00", "1974-12-25"));
assertFalse(compareDateTimes("1974-12-25", "1974-12-25T12:34:00-10:00"));
assertFalse(compareDateTimes("1974-12-25T12:34:00-10:00", "1974-12-25"));
assertFalse(compareDateTimes("1974-12-25", "1974-12-25T12:34:00Z"));
assertFalse(compareDateTimes("1974-12-25T12:34:00Z", "1974-12-25"));
assertNull( compareDateTimes("1974-12-25", "1974-12-25T12:34:00Z"));
assertNull( compareDateTimes("1974-12-25T12:34:00Z", "1974-12-25"));
assertFalse(compareDateTimes("2012-04-15", "2012-04-16"));
assertFalse(compareDateTimes("2012-04-16", "2012-04-15"));
assertFalse(compareDateTimes("2012-04-15T15:00:00", "2012-04-15T10:00:00"));
@ -49,8 +65,8 @@ public class BaseDateTimeTypeTest {
assertNull(compareDateTimes("1974-12-25", "1974-12-25T12:34:00"));
assertNull(compareDateTimes("1974-12-25T12:34:00", "1974-12-25"));
assertNull(compareDateTimes("2012-04-15T10:00:00", "2012-04-15"));
assertNull(compareDateTimes("2012-04-15T15:00:00Z", "2012-04-15T10:00:00"));
assertNull(compareDateTimes("2012-04-15T10:00:00", "2012-04-15T15:00:00Z"));
assertFalse(compareDateTimes("2012-04-15T15:00:00Z", "2012-04-15T10:00:00"));
assertFalse(compareDateTimes("2012-04-15T10:00:00", "2012-04-15T15:00:00Z"));
assertTrue(compareDateTimes("1974-12-25", "1974-12-25"));
assertTrue(compareDateTimes("2012-04-15", "2012-04-15"));
assertTrue(compareDateTimes("2012-04-15T15:00:00+02:00", "2012-04-15T16:00:00+03:00"));
@ -59,7 +75,7 @@ public class BaseDateTimeTypeTest {
assertTrue(compareDateTimes("2017-11-05T00:30:00.0-05:00", "2017-11-05T01:30:00.0-04:00"));
assertFalse(compareDateTimes("2016-12-02T13:00:00Z", "2016-11-02T10:00:00")); // no timezone, but cannot be the same time
assertNull(compareDateTimes("2016-12-02T13:00:00Z", "2016-12-02T10:00:00")); // no timezone, might be the same time
assertFalse(compareDateTimes("2016-12-02T13:00:00Z", "2016-12-02T10:00:00")); // no timezone, might be the same time
}
private Boolean compareDateTimes(String theLeft, String theRight) {