mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-03-25 01:18:37 +00:00
Fix #57: Date/Time types should not throw exceptions for bad precision on setValue(String) but should for constructors
This commit is contained in:
parent
f9e19f759f
commit
383d4929c8
@ -42,16 +42,11 @@ import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
|
||||
/**
|
||||
* For unit tests only
|
||||
*/
|
||||
static List<FastDateFormat> getFormatters() {
|
||||
return ourFormatters;
|
||||
}
|
||||
/*
|
||||
* Add any new formatters to the static block below!!
|
||||
*/
|
||||
private static final List<FastDateFormat> ourFormatters;
|
||||
|
||||
private static final Pattern ourYearDashMonthDashDayPattern = Pattern.compile("[0-9]{4}-[0-9]{2}-[0-9]{2}");
|
||||
private static final Pattern ourYearDashMonthPattern = Pattern.compile("[0-9]{4}-[0-9]{2}");
|
||||
private static final FastDateFormat ourYearFormat = FastDateFormat.getInstance("yyyy");
|
||||
@ -67,8 +62,8 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
private static final FastDateFormat ourYearMonthFormat = FastDateFormat.getInstance("yyyy-MM");
|
||||
private static final FastDateFormat ourYearMonthNoDashesFormat = FastDateFormat.getInstance("yyyyMM");
|
||||
private static final Pattern ourYearMonthPattern = Pattern.compile("[0-9]{4}[0-9]{2}");
|
||||
|
||||
private static final Pattern ourYearPattern = Pattern.compile("[0-9]{4}");
|
||||
|
||||
static {
|
||||
ArrayList<FastDateFormat> formatters = new ArrayList<FastDateFormat>();
|
||||
formatters.add(ourYearFormat);
|
||||
@ -86,9 +81,51 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
}
|
||||
|
||||
private TemporalPrecisionEnum myPrecision = TemporalPrecisionEnum.SECOND;
|
||||
private TimeZone myTimeZone;
|
||||
|
||||
private TimeZone myTimeZone;
|
||||
private boolean myTimeZoneZulu = false;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseDateTimeDt.class);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public BaseDateTimeDt() {
|
||||
// nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @throws DataFormatException
|
||||
* If the specified precision is not allowed for this type
|
||||
*/
|
||||
public BaseDateTimeDt(Date theDate, TemporalPrecisionEnum thePrecision) {
|
||||
setValue(theDate, thePrecision);
|
||||
if (isPrecisionAllowed(thePrecision) == false) {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + thePrecision + " precision): " + theDate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @throws DataFormatException
|
||||
* If the specified precision is not allowed for this type
|
||||
*/
|
||||
public BaseDateTimeDt(String theString) {
|
||||
setValueAsString(theString);
|
||||
if (isPrecisionAllowed(getPrecision()) == false) {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + theString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public BaseDateTimeDt(Date theDate, TemporalPrecisionEnum thePrecision, TimeZone theTimeZone) {
|
||||
this(theDate, thePrecision);
|
||||
setTimeZone(theTimeZone);
|
||||
}
|
||||
|
||||
private void clearTimeZone() {
|
||||
myTimeZone = null;
|
||||
@ -136,6 +173,10 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default precision for the given datatype
|
||||
*/
|
||||
protected abstract TemporalPrecisionEnum getDefaultPrecisionForDatatype();
|
||||
|
||||
/**
|
||||
* Gets the precision for this datatype (using the default for the given type if not set)
|
||||
@ -150,13 +191,8 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default precision for the given datatype
|
||||
*/
|
||||
protected abstract TemporalPrecisionEnum getDefaultPrecisionForDatatype();
|
||||
|
||||
/**
|
||||
* Returns the TimeZone associated with this dateTime's value. May return
|
||||
* <code>null</code> if no timezone was supplied.
|
||||
* Returns the TimeZone associated with this dateTime's value. May return <code>null</code> if no timezone was
|
||||
* supplied.
|
||||
*/
|
||||
public TimeZone getTimeZone() {
|
||||
return myTimeZone;
|
||||
@ -204,57 +240,52 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
protected Date parse(String theValue) throws DataFormatException {
|
||||
try {
|
||||
if (theValue.length() == 4 && ourYearPattern.matcher(theValue).matches()) {
|
||||
if (isPrecisionAllowed(YEAR)) {
|
||||
setPrecision(YEAR);
|
||||
clearTimeZone();
|
||||
return ((ourYearFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support YEAR precision): " + theValue);
|
||||
if (!isPrecisionAllowed(YEAR)) {
|
||||
ourLog.debug("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support YEAR precision): " + theValue);
|
||||
}
|
||||
setPrecision(YEAR);
|
||||
clearTimeZone();
|
||||
return ((ourYearFormat).parse(theValue));
|
||||
} else if (theValue.length() == 6 && ourYearMonthPattern.matcher(theValue).matches()) {
|
||||
// Eg. 198401 (allow this just to be lenient)
|
||||
if (isPrecisionAllowed(MONTH)) {
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthNoDashesFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
if (!isPrecisionAllowed(MONTH)) {
|
||||
ourLog.debug("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthNoDashesFormat).parse(theValue));
|
||||
} else if (theValue.length() == 7 && ourYearDashMonthPattern.matcher(theValue).matches()) {
|
||||
// E.g. 1984-01 (this is valid according to the spec)
|
||||
if (isPrecisionAllowed(MONTH)) {
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support MONTH precision): " + theValue);
|
||||
if (!isPrecisionAllowed(MONTH)) {
|
||||
ourLog.debug("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support MONTH precision): " + theValue);
|
||||
}
|
||||
setPrecision(MONTH);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthFormat).parse(theValue));
|
||||
} else if (theValue.length() == 8 && ourYearMonthDayPattern.matcher(theValue).matches()) {
|
||||
// Eg. 19840101 (allow this just to be lenient)
|
||||
if (isPrecisionAllowed(DAY)) {
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthDayNoDashesFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
if (!isPrecisionAllowed(DAY)) {
|
||||
ourLog.debug("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthDayNoDashesFormat).parse(theValue));
|
||||
} else if (theValue.length() == 10 && ourYearDashMonthDashDayPattern.matcher(theValue).matches()) {
|
||||
// E.g. 1984-01-01 (this is valid according to the spec)
|
||||
if (isPrecisionAllowed(DAY)) {
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthDayFormat).parse(theValue));
|
||||
} else {
|
||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
if (!isPrecisionAllowed(DAY)) {
|
||||
ourLog.debug("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
|
||||
}
|
||||
setPrecision(DAY);
|
||||
clearTimeZone();
|
||||
return ((ourYearMonthDayFormat).parse(theValue));
|
||||
} else if (theValue.length() >= 18) { // date and time with possible time zone
|
||||
int dotIndex = theValue.indexOf('.', 18);
|
||||
boolean hasMillis = dotIndex > -1;
|
||||
|
||||
if (!hasMillis && !isPrecisionAllowed(SECOND)) {
|
||||
throw new DataFormatException("Invalid date/time string (data type does not support SECONDS precision): " + theValue);
|
||||
ourLog.debug("Invalid date/time string (data type does not support SECONDS precision): " + theValue);
|
||||
} else if (hasMillis && !isPrecisionAllowed(MILLI)) {
|
||||
throw new DataFormatException("Invalid date/time string (data type " + getClass().getSimpleName() + " does not support MILLIS precision):" + theValue);
|
||||
ourLog.debug("Invalid date/time string (data type " + getClass().getSimpleName() + " does not support MILLIS precision):" + theValue);
|
||||
}
|
||||
|
||||
Date retVal;
|
||||
@ -262,10 +293,11 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
try {
|
||||
if (hasOffset(theValue)) {
|
||||
retVal = ourYearMonthDayTimeMilliZoneFormat.parse(theValue);
|
||||
} else if (theValue.endsWith("Z"))
|
||||
} else if (theValue.endsWith("Z")) {
|
||||
retVal = ourYearMonthDayTimeMilliUTCZFormat.parse(theValue);
|
||||
else
|
||||
} else {
|
||||
retVal = ourYearMonthDayTimeMilliFormat.parse(theValue);
|
||||
}
|
||||
} catch (ParseException p2) {
|
||||
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
|
||||
}
|
||||
@ -316,8 +348,6 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
updateStringValue();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void setTimeZone(String theValueString, boolean hasMillis) {
|
||||
clearTimeZone();
|
||||
int timeZoneStart = 19;
|
||||
@ -332,13 +362,11 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setTimeZone(TimeZone theTimeZone) {
|
||||
myTimeZone = theTimeZone;
|
||||
updateStringValue();
|
||||
}
|
||||
|
||||
|
||||
public void setTimeZoneZulu(boolean theTimeZoneZulu) {
|
||||
myTimeZoneZulu = theTimeZoneZulu;
|
||||
updateStringValue();
|
||||
@ -353,9 +381,11 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
/**
|
||||
* Sets the value of this date/time using the specified level of precision
|
||||
*
|
||||
* @param theValue The date value
|
||||
* @param thePrecision The precision
|
||||
* @throws DataFormatException
|
||||
* @param theValue
|
||||
* The date value
|
||||
* @param thePrecision
|
||||
* The precision
|
||||
* @throws DataFormatException
|
||||
*/
|
||||
public void setValue(Date theValue, TemporalPrecisionEnum thePrecision) throws DataFormatException {
|
||||
clearTimeZone();
|
||||
@ -369,4 +399,11 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
||||
super.setValueAsString(theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* For unit tests only
|
||||
*/
|
||||
static List<FastDateFormat> getFormatters() {
|
||||
return ourFormatters;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,7 +25,16 @@ import java.util.Date;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
/**
|
||||
* Represents a FHIR date datatype. Valid precisions values for this type are:
|
||||
* <ul>
|
||||
* <li>{@link TemporalPrecisionEnum#YEAR}
|
||||
* <li>{@link TemporalPrecisionEnum#MONTH}
|
||||
* <li>{@link TemporalPrecisionEnum#DAY}
|
||||
* </ul>
|
||||
*/
|
||||
@DatatypeDef(name = "date")
|
||||
public class DateDt extends BaseDateTimeDt {
|
||||
|
||||
@ -44,10 +53,9 @@ public class DateDt extends BaseDateTimeDt {
|
||||
/**
|
||||
* Constructor which accepts a date value and uses the {@link #DEFAULT_PRECISION} for this type
|
||||
*/
|
||||
@SimpleSetter(suffix="WithDayPrecision")
|
||||
@SimpleSetter(suffix = "WithDayPrecision")
|
||||
public DateDt(@SimpleSetter.Parameter(name = "theDate") Date theDate) {
|
||||
setValue(theDate);
|
||||
setPrecision(DEFAULT_PRECISION);
|
||||
super(theDate, DEFAULT_PRECISION);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,11 +65,23 @@ public class DateDt extends BaseDateTimeDt {
|
||||
* <li>{@link TemporalPrecisionEnum#MONTH}
|
||||
* <li>{@link TemporalPrecisionEnum#DAY}
|
||||
* </ul>
|
||||
*
|
||||
* @throws DataFormatException
|
||||
* If the specified precision is not allowed for this type
|
||||
*/
|
||||
@SimpleSetter
|
||||
public DateDt(@SimpleSetter.Parameter(name = "theDate") Date theDate, @SimpleSetter.Parameter(name = "thePrecision") TemporalPrecisionEnum thePrecision) {
|
||||
setValue(theDate);
|
||||
setPrecision(thePrecision);
|
||||
super(theDate, thePrecision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which accepts a date as a string in FHIR format
|
||||
*
|
||||
* @throws DataFormatException
|
||||
* If the precision in the date string is not allowed for this type
|
||||
*/
|
||||
public DateDt(String theDate) {
|
||||
super(theDate);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -26,7 +26,18 @@ import java.util.TimeZone;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
/**
|
||||
* Represents a FHIR dateTime datatype. Valid precisions values for this type are:
|
||||
* <ul>
|
||||
* <li>{@link TemporalPrecisionEnum#YEAR}
|
||||
* <li>{@link TemporalPrecisionEnum#MONTH}
|
||||
* <li>{@link TemporalPrecisionEnum#DAY}
|
||||
* <li>{@link TemporalPrecisionEnum#SECOND}
|
||||
* <li>{@link TemporalPrecisionEnum#MILLI}
|
||||
* </ul>
|
||||
*/
|
||||
@DatatypeDef(name = "dateTime")
|
||||
public class DateTimeDt extends BaseDateTimeDt {
|
||||
|
||||
@ -43,18 +54,44 @@ public class DateTimeDt extends BaseDateTimeDt {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DateTimeDt
|
||||
* Create a new DateTimeDt with seconds precision and the local time zone
|
||||
*/
|
||||
@SimpleSetter(suffix = "WithSecondsPrecision")
|
||||
public DateTimeDt(@SimpleSetter.Parameter(name = "theDate") Date theDate) {
|
||||
setValue(theDate);
|
||||
setPrecision(DEFAULT_PRECISION);
|
||||
setTimeZone(TimeZone.getDefault());
|
||||
super(theDate, DEFAULT_PRECISION, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which accepts a date value and a precision value. Valid
|
||||
* precisions values for this type are:
|
||||
* Constructor which accepts a date value and a precision value. Valid precisions values for this type are:
|
||||
* <ul>
|
||||
* <li>{@link TemporalPrecisionEnum#YEAR}
|
||||
* <li>{@link TemporalPrecisionEnum#MONTH}
|
||||
* <li>{@link TemporalPrecisionEnum#DAY}
|
||||
* <li>{@link TemporalPrecisionEnum#SECOND}
|
||||
* <li>{@link TemporalPrecisionEnum#MILLI}
|
||||
* </ul>
|
||||
*
|
||||
* @throws DataFormatException
|
||||
* If the specified precision is not allowed for this type
|
||||
*/
|
||||
@SimpleSetter
|
||||
public DateTimeDt(@SimpleSetter.Parameter(name = "theDate") Date theDate, @SimpleSetter.Parameter(name = "thePrecision") TemporalPrecisionEnum thePrecision) {
|
||||
super(theDate, thePrecision, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using a string date/time
|
||||
*
|
||||
* @throws DataFormatException
|
||||
* If the specified precision is not allowed for this type
|
||||
*/
|
||||
public DateTimeDt(String theValue) {
|
||||
super(theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor which accepts a date value, precision value, and time zone. Valid precisions values for this type
|
||||
* are:
|
||||
* <ul>
|
||||
* <li>{@link TemporalPrecisionEnum#YEAR}
|
||||
* <li>{@link TemporalPrecisionEnum#MONTH}
|
||||
@ -63,18 +100,8 @@ public class DateTimeDt extends BaseDateTimeDt {
|
||||
* <li>{@link TemporalPrecisionEnum#MILLI}
|
||||
* </ul>
|
||||
*/
|
||||
@SimpleSetter
|
||||
public DateTimeDt(@SimpleSetter.Parameter(name = "theDate") Date theDate, @SimpleSetter.Parameter(name = "thePrecision") TemporalPrecisionEnum thePrecision) {
|
||||
setValue(theDate);
|
||||
setPrecision(thePrecision);
|
||||
setTimeZone(TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using a string date/time
|
||||
*/
|
||||
public DateTimeDt(String theValue) {
|
||||
setValueAsString(theValue);
|
||||
public DateTimeDt(Date theDate, TemporalPrecisionEnum thePrecision, TimeZone theTimezone) {
|
||||
super(theDate, thePrecision, theTimezone);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -92,10 +119,11 @@ public class DateTimeDt extends BaseDateTimeDt {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new instance of DateTimeDt with the current system time and SECOND precision
|
||||
* Returns a new instance of DateTimeDt with the current system time and SECOND precision and the system local time
|
||||
* zone
|
||||
*/
|
||||
public static DateTimeDt withCurrentTime() {
|
||||
return new DateTimeDt(new Date(), TemporalPrecisionEnum.SECOND);
|
||||
return new DateTimeDt(new Date(), TemporalPrecisionEnum.SECOND, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,5 +136,4 @@ public class DateTimeDt extends BaseDateTimeDt {
|
||||
return DEFAULT_PRECISION;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -29,6 +29,13 @@ import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
|
||||
/**
|
||||
* Represents a FHIR instant datatype. Valid precisions values for this type are:
|
||||
* <ul>
|
||||
* <li>{@link TemporalPrecisionEnum#SECOND}
|
||||
* <li>{@link TemporalPrecisionEnum#MILLI}
|
||||
* </ul>
|
||||
*/
|
||||
@DatatypeDef(name = "instant")
|
||||
public class InstantDt extends BaseDateTimeDt {
|
||||
|
||||
@ -54,31 +61,39 @@ public class InstantDt extends BaseDateTimeDt {
|
||||
* Create a new DateTimeDt
|
||||
*/
|
||||
public InstantDt(Calendar theCalendar) {
|
||||
setValue(theCalendar.getTime());
|
||||
setPrecision(DEFAULT_PRECISION);
|
||||
setTimeZone(theCalendar.getTimeZone());
|
||||
super(theCalendar.getTime(), DEFAULT_PRECISION, theCalendar.getTimeZone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance using the given date, precision level, and time zone
|
||||
*
|
||||
* @throws DataFormatException
|
||||
* If the specified precision is not allowed for this type
|
||||
*/
|
||||
public InstantDt(Date theDate, TemporalPrecisionEnum thePrecision, TimeZone theTimezone) {
|
||||
super(theDate, thePrecision, theTimezone);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new DateTimeDt using an existing value. <b>Use this constructor with caution</b>,
|
||||
* as it may create more precision than warranted (since for example it is possible to pass in
|
||||
* a DateTime with only a year, and this constructor will convert to an InstantDt with
|
||||
* milliseconds precision).
|
||||
* milliseconds precision).
|
||||
*/
|
||||
public InstantDt(BaseDateTimeDt theDateTime) {
|
||||
// Do not call super(foo) here, we don't want to trigger a DataFormatException
|
||||
setValue(theDateTime.getValue());
|
||||
setPrecision(DEFAULT_PRECISION);
|
||||
setTimeZone(theDateTime.getTimeZone());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new DateTimeDt
|
||||
* Create a new DateTimeDt with the given date/time and {@link TemporalPrecisionEnum#MILLI} precision
|
||||
*/
|
||||
@SimpleSetter(suffix = "WithMillisPrecision")
|
||||
public InstantDt(@SimpleSetter.Parameter(name = "theDate") Date theDate) {
|
||||
setValue(theDate);
|
||||
setPrecision(DEFAULT_PRECISION);
|
||||
setTimeZone(TimeZone.getDefault());
|
||||
super(theDate, DEFAULT_PRECISION, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,7 +120,7 @@ public class InstantDt extends BaseDateTimeDt {
|
||||
* @throws DataFormatException
|
||||
*/
|
||||
public InstantDt(String theString) {
|
||||
setValueAsString(theString);
|
||||
super(theString);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -154,10 +169,10 @@ public class InstantDt extends BaseDateTimeDt {
|
||||
|
||||
/**
|
||||
* Factory method which creates a new InstantDt with millisecond precision and initializes it with the
|
||||
* current time.
|
||||
* current time and the system local timezone.
|
||||
*/
|
||||
public static InstantDt withCurrentTime() {
|
||||
return new InstantDt(new Date(), TemporalPrecisionEnum.MILLI);
|
||||
return new InstantDt(new Date(), TemporalPrecisionEnum.MILLI, TimeZone.getDefault());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,58 +1,128 @@
|
||||
package ca.uhn.fhir.model.primitive;
|
||||
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.dstu.resource.Condition;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
|
||||
public class BaseDateTimeDtTest {
|
||||
private SimpleDateFormat myDateInstantParser;
|
||||
private FastDateFormat myDateInstantZoneParser;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseDateTimeDtTest.class);
|
||||
|
||||
|
||||
private static FhirContext ourCtx = new FhirContext();
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myDateInstantParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
myDateInstantZoneParser = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSSZ", TimeZone.getTimeZone("GMT-02:00"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void setTimezoneToZulu() {
|
||||
DateTimeDt dt = new DateTimeDt(new Date(816411488000L));
|
||||
// assertEquals("1995-11-14T23:58:08", dt.getValueAsString());
|
||||
// assertEquals("1995-11-14T23:58:08", dt.getValueAsString());
|
||||
dt.setTimeZoneZulu(true);
|
||||
assertEquals("1995-11-15T04:58:08Z", dt.getValueAsString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDateTimeInLocalTimezone() {
|
||||
DateTimeDt dt = DateTimeDt.withCurrentTime();
|
||||
String str = dt.getValueAsString();
|
||||
char offset = str.charAt(19);
|
||||
if (offset != '+' && offset != '-') {
|
||||
fail("No timezone provided: " + str);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstantInLocalTimezone() {
|
||||
InstantDt dt = InstantDt.withCurrentTime();
|
||||
String str = dt.getValueAsString();
|
||||
char offset = str.charAt(23);
|
||||
if (offset != '+' && offset != '-') {
|
||||
fail("No timezone provided: " + str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for #57
|
||||
*/
|
||||
@Test
|
||||
public void testDateParsesWithInvalidPrecision() {
|
||||
Condition c = new Condition();
|
||||
c.setDateAsserted(new DateDt());
|
||||
c.getDateAsserted().setValueAsString("2001-01-02T11:13:33");
|
||||
assertEquals(TemporalPrecisionEnum.SECOND, c.getDateAsserted().getPrecision());
|
||||
|
||||
String encoded = ourCtx.newXmlParser().encodeResourceToString(c);
|
||||
Assert.assertThat(encoded, Matchers.containsString("value=\"2001-01-02T11:13:33\""));
|
||||
|
||||
c = ourCtx.newXmlParser().parseResource(Condition.class, encoded);
|
||||
|
||||
assertEquals("2001-01-02T11:13:33", c.getDateAsserted().getValueAsString());
|
||||
assertEquals(TemporalPrecisionEnum.SECOND, c.getDateAsserted().getPrecision());
|
||||
|
||||
ValidationResult outcome = ourCtx.newValidator().validateWithResult(c);
|
||||
String outcomeStr = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome.getOperationOutcome());
|
||||
ourLog.info(outcomeStr);
|
||||
|
||||
assertThat(outcomeStr, containsString("date-primitive"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for #57
|
||||
*/
|
||||
@Test
|
||||
public void testConstructorRejectsInvalidPrecision() {
|
||||
try {
|
||||
new DateDt("2001-01-02T11:13:33");
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertThat(e.getMessage(), containsString("precision"));
|
||||
}
|
||||
try {
|
||||
new InstantDt("2001-01-02");
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertThat(e.getMessage(), containsString("precision"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFormats() throws Exception {
|
||||
Date instant = myDateInstantParser.parse("2001-02-03 13:01:02.555");
|
||||
for (FastDateFormat next : BaseDateTimeDt.getFormatters()) {
|
||||
|
||||
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(instant);
|
||||
String value = next.format(cal);
|
||||
ourLog.info("String: {}", value);
|
||||
|
||||
|
||||
DateTimeDt dt = new DateTimeDt(value);
|
||||
String reEncoded = next.format(dt.getValue());
|
||||
|
||||
|
||||
assertEquals(value, reEncoded);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParseDay() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt();
|
||||
@ -64,8 +134,7 @@ public class BaseDateTimeDtTest {
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.DAY, dt.getPrecision());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test()
|
||||
public void testParseMalformatted() throws DataFormatException {
|
||||
DateTimeDt dt = new DateTimeDt("20120102");
|
||||
@ -83,8 +152,8 @@ public class BaseDateTimeDtTest {
|
||||
assertEquals(false, dt.isTimeZoneZulu());
|
||||
assertNull(dt.getTimeZone());
|
||||
assertEquals(TemporalPrecisionEnum.MILLI, dt.getPrecision());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMilliZone() throws DataFormatException {
|
||||
InstantDt dt = new InstantDt();
|
||||
@ -183,11 +252,12 @@ public class BaseDateTimeDtTest {
|
||||
public void testSetValueByString() {
|
||||
InstantDt i = new InstantDt();
|
||||
i.setValueAsString("2014-06-20T20:22:09Z");
|
||||
|
||||
|
||||
assertNotNull(i.getValue());
|
||||
assertNotNull(i.getValueAsString());
|
||||
|
||||
|
||||
assertEquals(1403295729000L, i.getValue().getTime());
|
||||
assertEquals("2014-06-20T20:22:09Z",i.getValueAsString());
|
||||
assertEquals("2014-06-20T20:22:09Z", i.getValueAsString());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -156,6 +156,15 @@
|
||||
were applied to other client instances for the same client interface as well. (Issue
|
||||
did not affect generic/fluent clients)
|
||||
</action>
|
||||
<action type="fix" issue="57">
|
||||
DateDt, DateTimeDt and types InstantDt types now do not throw an exception
|
||||
if they are used to parse a value with the wrong level of precision for
|
||||
the given type but do throw an exception if the wrong level of precision
|
||||
is passed into their constructors.<![CDATA[<br/><br/>]]>
|
||||
This means that HAPI FHIR can now successfully parse resources from external
|
||||
sources that have the wrong level of precision, but will generate a validation
|
||||
error if the resource is validated. Thanks to Alexander Kley for the suggestion!
|
||||
</action>
|
||||
</release>
|
||||
<release version="0.7" date="2014-Oct-23">
|
||||
<action type="add" issue="30">
|
||||
|
Loading…
x
Reference in New Issue
Block a user