Merge pull request #92 from hapifhir/ja_20191018_91_honour_dt_precision
Fix precision and validator issues
This commit is contained in:
commit
0b196e5188
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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")));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue