Merge pull request #92 from hapifhir/ja_20191018_91_honour_dt_precision

Fix precision and validator issues
This commit is contained in:
Grahame Grieve 2019-10-18 12:25:03 -05:00 committed by GitHub
commit 0b196e5188
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 160 additions and 164 deletions

View File

@ -25,6 +25,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.hl7.fhir.utilities.DateTimeUtil;
import java.text.ParseException;
import java.util.*;
@ -472,53 +473,29 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getValue().after(theDateTimeType.getValue());
}
/**
* Returns a human readable version of this date/time using the system local format.
* <p>
* <b>Note on time zones:</b> This method renders the value using the time zone
* that is contained within the value. For example, if this date object contains the
* value "2012-01-05T12:00:00-08:00", the human display will be rendered as "12:00:00"
* even if the application is being executed on a system in a different time zone. If
* this behaviour is not what you want, use {@link #toHumanDisplayLocalTimezone()}
* instead.
* </p>
*/
public String toHumanDisplay() {
TimeZone tz = getTimeZone();
Calendar value = tz != null ? Calendar.getInstance(tz) : Calendar.getInstance();
value.setTime(getValue());
/**
* Returns a human readable version of this date/time using the system local format.
* <p>
* <b>Note on time zones:</b> This method renders the value using the time zone that is contained within the value.
* For example, if this date object contains the value "2012-01-05T12:00:00-08:00",
* the human display will be rendered as "12:00:00" even if the application is being executed on a system in a
* different time zone. If this behaviour is not what you want, use
* {@link #toHumanDisplayLocalTimezone()} instead.
* </p>
*/
public String toHumanDisplay() {
return DateTimeUtil.toHumanDisplay(getTimeZone(), getPrecision(), getValue(), getValueAsString());
}
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(value);
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(value);
}
}
/**
* Returns a human readable version of this date/time using the system local format,
* converted to the local timezone if neccesary.
*
* @see #toHumanDisplay() for a method which does not convert the time to the local
* timezone before rendering it.
*/
public String toHumanDisplayLocalTimezone() {
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(getValue());
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(getValue());
}
}
/**
* Returns a human readable version of this date/time using the system local format, converted to the local timezone
* if neccesary.
*
* @see #toHumanDisplay() for a method which does not convert the time to the local timezone before rendering it.
*/
public String toHumanDisplayLocalTimezone() {
return DateTimeUtil.toHumanDisplayLocalTimezone(getPrecision(), getValue(), getValueAsString());
}
/**

View File

@ -34,6 +34,7 @@ import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import ca.uhn.fhir.parser.DataFormatException;
import org.hl7.fhir.utilities.DateTimeUtil;
public abstract class BaseDateTimeType extends PrimitiveType<Date> {
@ -488,20 +489,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* </p>
*/
public String toHumanDisplay() {
TimeZone tz = getTimeZone();
Calendar value = tz != null ? Calendar.getInstance(tz) : Calendar.getInstance();
value.setTime(getValue());
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(value);
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(value);
}
return DateTimeUtil.toHumanDisplay(getTimeZone(), getPrecision(), getValue(), getValueAsString());
}
/**
@ -511,16 +499,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* @see #toHumanDisplay() for a method which does not convert the time to the local timezone before rendering it.
*/
public String toHumanDisplayLocalTimezone() {
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(getValue());
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(getValue());
}
return DateTimeUtil.toHumanDisplayLocalTimezone(getPrecision(), getValue(), getValueAsString());
}
private void validateCharAtIndexIs(String theValue, int theIndex, char theChar) {

View File

@ -34,6 +34,7 @@ import org.apache.commons.lang3.time.FastDateFormat;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.parser.DataFormatException;
import org.hl7.fhir.utilities.DateTimeUtil;
public abstract class BaseDateTimeType extends PrimitiveType<Date> {
@ -781,20 +782,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* </p>
*/
public String toHumanDisplay() {
TimeZone tz = getTimeZone();
Calendar value = tz != null ? Calendar.getInstance(tz) : Calendar.getInstance();
value.setTime(getValue());
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(value);
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(value);
}
return DateTimeUtil.toHumanDisplay(getTimeZone(), getPrecision(), getValue(), getValueAsString());
}
/**
@ -804,16 +792,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* @see #toHumanDisplay() for a method which does not convert the time to the local timezone before rendering it.
*/
public String toHumanDisplayLocalTimezone() {
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(getValue());
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(getValue());
}
return DateTimeUtil.toHumanDisplayLocalTimezone(getPrecision(), getValue(), getValueAsString());
}
private void validateBeforeOrAfter(DateTimeType theDateTimeType) {

View File

@ -34,6 +34,7 @@ import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import ca.uhn.fhir.parser.DataFormatException;
import org.hl7.fhir.utilities.DateTimeUtil;
public abstract class BaseDateTimeType extends PrimitiveType<Date> {
@ -771,51 +772,29 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return retVal;
}
/**
* Returns a human readable version of this date/time using the system local format.
* <p>
* <b>Note on time zones:</b> This method renders the value using the time zone that is contained within the value.
* For example, if this date object contains the value "2012-01-05T12:00:00-08:00",
* the human display will be rendered as "12:00:00" even if the application is being executed on a system in a
* different time zone. If this behaviour is not what you want, use
* {@link #toHumanDisplayLocalTimezone()} instead.
* </p>
*/
public String toHumanDisplay() {
TimeZone tz = getTimeZone();
Calendar value = tz != null ? Calendar.getInstance(tz) : Calendar.getInstance();
value.setTime(getValue());
/**
* Returns a human readable version of this date/time using the system local format.
* <p>
* <b>Note on time zones:</b> This method renders the value using the time zone that is contained within the value.
* For example, if this date object contains the value "2012-01-05T12:00:00-08:00",
* the human display will be rendered as "12:00:00" even if the application is being executed on a system in a
* different time zone. If this behaviour is not what you want, use
* {@link #toHumanDisplayLocalTimezone()} instead.
* </p>
*/
public String toHumanDisplay() {
return DateTimeUtil.toHumanDisplay(getTimeZone(), getPrecision(), getValue(), getValueAsString());
}
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(value);
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(value);
}
}
/**
* Returns a human readable version of this date/time using the system local format, converted to the local timezone
* if neccesary.
*
* @see #toHumanDisplay() for a method which does not convert the time to the local timezone before rendering it.
*/
public String toHumanDisplayLocalTimezone() {
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(getValue());
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(getValue());
}
}
/**
* Returns a human readable version of this date/time using the system local format, converted to the local timezone
* if neccesary.
*
* @see #toHumanDisplay() for a method which does not convert the time to the local timezone before rendering it.
*/
public String toHumanDisplayLocalTimezone() {
return DateTimeUtil.toHumanDisplayLocalTimezone(getPrecision(), getValue(), getValueAsString());
}
private void validateBeforeOrAfter(DateTimeType theDateTimeType) {
if (getValue() == null) {

View File

@ -273,7 +273,12 @@ public class JsonParser extends ParserBase {
context.getChildren().add(n);
if (main != null) {
JsonPrimitive p = (JsonPrimitive) main;
n.setValue(p.getAsString());
if (p.isNumber() && p.getAsNumber() instanceof JsonTrackingParser.PresentedBigDecimal) {
String rawValue = ((JsonTrackingParser.PresentedBigDecimal) p.getAsNumber()).getPresentation();
n.setValue(rawValue);
} else {
n.setValue(p.getAsString());
}
if (!n.getProperty().isChoice() && n.getType().equals("xhtml")) {
try {
n.setXhtml(new XhtmlParser().setValidatorMode(policy == ValidationPolicy.EVERYTHING).parse(n.getValue(), null).getDocumentElement());

View File

@ -26,6 +26,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.commons.lang3.time.FastDateFormat;
import org.hl7.fhir.utilities.DateTimeUtil;
import java.util.Calendar;
import java.util.Date;
@ -781,20 +782,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* </p>
*/
public String toHumanDisplay() {
TimeZone tz = getTimeZone();
Calendar value = tz != null ? Calendar.getInstance(tz) : Calendar.getInstance();
value.setTime(getValue());
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(value);
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(value);
}
return DateTimeUtil.toHumanDisplay(getTimeZone(), getPrecision(), getValue(), getValueAsString());
}
/**
@ -804,16 +792,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* @see #toHumanDisplay() for a method which does not convert the time to the local timezone before rendering it.
*/
public String toHumanDisplayLocalTimezone() {
switch (getPrecision()) {
case YEAR:
case MONTH:
case DAY:
return ourHumanDateFormat.format(getValue());
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(getValue());
}
return DateTimeUtil.toHumanDisplayLocalTimezone(getPrecision(), getValue(), getValueAsString());
}
private void validateBeforeOrAfter(DateTimeType theDateTimeType) {

View File

@ -78,6 +78,21 @@ public class BaseDateTimeTypeTest {
assertFalse(compareDateTimes("2016-12-02T13:00:00Z", "2016-12-02T10:00:00")); // no timezone, might be the same time
}
@Test
public void testToHumanDisplayForDateOnlyPrecisions() {
assertEquals("2019-01-02", new DateTimeType("2019-01-02").toHumanDisplay());
assertEquals("2019-01", new DateTimeType("2019-01").toHumanDisplay());
assertEquals("2019", new DateTimeType("2019").toHumanDisplay());
}
@Test
public void testToHumanDisplayLocalTimezoneForDateOnlyPrecisions() {
assertEquals("2019-01-02", new DateTimeType("2019-01-02").toHumanDisplayLocalTimezone());
assertEquals("2019-01", new DateTimeType("2019-01").toHumanDisplayLocalTimezone());
assertEquals("2019", new DateTimeType("2019").toHumanDisplayLocalTimezone());
}
private Boolean compareDateTimes(String theLeft, String theRight) {
System.out.println("Compare "+theLeft+" to "+theRight);
DateTimeType leftDt = new DateTimeType(theLeft);

View File

@ -0,0 +1,44 @@
package org.hl7.fhir.utilities;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import org.apache.commons.lang3.time.FastDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class DateTimeUtil {
private static final FastDateFormat ourHumanDateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM);
private static final FastDateFormat ourHumanDateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM);
public static String toHumanDisplay(TimeZone theTimeZone, TemporalPrecisionEnum thePrecision, Date theValue, String theValueAsString) {
Calendar value = theTimeZone != null ? Calendar.getInstance(theTimeZone) : Calendar.getInstance();
value.setTime(theValue);
switch (thePrecision) {
case YEAR:
case MONTH:
case DAY:
return theValueAsString;
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(value);
}
}
public static String toHumanDisplayLocalTimezone(TemporalPrecisionEnum thePrecision, Date theValue, String theValueAsString) {
switch (thePrecision) {
case YEAR:
case MONTH:
case DAY:
return theValueAsString;
case MILLI:
case SECOND:
default:
return ourHumanDateTimeFormat.format(theValue);
}
}
}

View File

@ -179,6 +179,11 @@ public class Utilities {
if (value.startsWith("+0") && !"+0".equals(value) && !value.startsWith("+0."))
return DecimalStatus.SYNTAX;
}
// check for trailing dot
if (value.endsWith(".")) {
return DecimalStatus.SYNTAX;
}
boolean havePeriod = false;
boolean haveExponent = false;

View File

@ -67,7 +67,7 @@ import com.google.gson.JsonObject;
public class ValidationTestSuite implements IEvaluationContext, IValidatorResourceFetcher {
@Parameters(name = "{index}: id {0}")
public static Iterable<Object[]> data() throws ParserConfigurationException, SAXException, IOException {
public static Iterable<Object[]> data() throws IOException {
Map<String, JsonObject> examples = new HashMap<String, JsonObject>();
JsonObject json = (JsonObject) new com.google.gson.JsonParser().parse(TextFile.fileToString(TestUtilities.resourceNameToFile("validation-examples", "manifest.json")));

View File

@ -148,6 +148,12 @@
"/List/text/div (line 7, col4) Error parsing XHTML: Malformed XHTML: Found a DocType declaration, and these are not allowed (XXE security vulnerability protection) "
]
},
"observation-with-trailing-dot.json": {
"errorCount": 1,
"errors": [
"ERROR: Observation.referenceRange[0].high.value: The value '925.' is not a valid decimal"
]
},
"synthea.json": {
"version": "4.0",
"errorCount": 2,

View File

@ -0,0 +1,28 @@
{
"resourceType": "Observation",
"status": "final",
"subject": {
"reference": "Patient/123"
},
"code": {
"coding": [
{
"system": "http://foo",
"code": "123"
}
]
},
"referenceRange": [
{
"low": {
"value": 210.0,
"unit": "pg/mL"
},
"high": {
"value": 925.,
"unit": "pg/mL"
},
"text": "The 'high' value above has an invalid number with a trailing dot"
}
]
}