Rework precision checking in datetime (#874)

This commit is contained in:
James Agnew 2022-07-25 16:40:55 -04:00 committed by GitHub
parent b213d00f6d
commit f010804e44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 190 additions and 240 deletions

View File

@ -110,33 +110,33 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
/**
* Constructor
*
*
* @throws IllegalArgumentException
* If the specified precision is not allowed for this type
*/
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision) {
setValue(theDate, thePrecision);
if (isPrecisionAllowed(thePrecision) == false) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + thePrecision + " precision): " + theDate);
}
validatePrecisionAndThrowIllegalArgumentException();
}
/**
* Constructor
*
*
* @throws IllegalArgumentException
* If the specified precision is not allowed for this type
*/
public BaseDateTimeType(String theString) {
setValueAsString(theString);
validatePrecisionAndThrowIllegalArgumentException();
}
/**
* Constructor
*/
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision, TimeZone theTimeZone) {
this(theDate, thePrecision);
setTimeZone(theTimeZone);
this(theDate, thePrecision);
setTimeZone(theTimeZone);
validatePrecisionAndThrowIllegalArgumentException();
}
private void clearTimeZone() {
@ -144,7 +144,13 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
myTimeZoneZulu = false;
}
@Override
private void validatePrecisionAndThrowIllegalArgumentException() {
if (!isPrecisionAllowed(getPrecision())) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + getValueAsString());
}
}
@Override
protected String encode(Date theValue) {
if (theValue == null) {
return null;
@ -204,7 +210,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
/**
* Gets the precision for this datatype (using the default for the given type if not set)
*
*
* @see #setPrecision(TemporalPrecisionEnum)
*/
public TemporalPrecisionEnum getPrecision() {
@ -251,7 +257,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
/**
* Returns <code>true</code> if this object represents a date that is today's date
*
*
* @throws NullPointerException
* if {@link #getValue()} returns <code>null</code>
*/
@ -264,46 +270,26 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
protected Date parse(String theValue) throws IllegalArgumentException {
try {
if (theValue.length() == 4 && ourYearPattern.matcher(theValue).matches()) {
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)) {
// 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)) {
// 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)) {
// 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)) {
// ourLog.debug("Invalid date/time string (datatype " + getClass().getSimpleName() +
// " does not support DAY precision): " + theValue);
}
setPrecision(DAY);
clearTimeZone();
return ((ourYearMonthDayFormat).parse(theValue));
@ -312,20 +298,12 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
if (firstColonIndex == -1) {
throw new IllegalArgumentException("Invalid date/time string: " + theValue);
}
boolean hasSeconds = theValue.length() > firstColonIndex+3 ? theValue.charAt(firstColonIndex+3) == ':' : false;
boolean hasSeconds = theValue.length() > firstColonIndex+3 ? theValue.charAt(firstColonIndex+3) == ':' : false;
int dotIndex = theValue.length() >= 18 ? theValue.indexOf('.', 18): -1;
boolean hasMillis = dotIndex > -1;
// if (!hasMillis && !isPrecisionAllowed(SECOND)) {
// ourLog.debug("Invalid date/time string (data type does not support SECONDS precision): " +
// theValue);
// } else if (hasMillis && !isPrecisionAllowed(MILLI)) {
// ourLog.debug("Invalid date/time string (data type " + getClass().getSimpleName() +
// " does not support MILLIS precision):" + theValue);
// }
Date retVal;
if (hasMillis) {
try {
@ -390,7 +368,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* <li>{@link Calendar#MONTH}
* <li>{@link Calendar#YEAR}
* </ul>
*
*
* @throws IllegalArgumentException
*/
public void setPrecision(TemporalPrecisionEnum thePrecision) throws IllegalArgumentException {
@ -429,7 +407,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* Sets the value of this date/time using the default level of precision
* for this datatype
* using the system local time zone
*
*
* @param theValue
* The date value
*/
@ -446,7 +424,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
/**
* Sets the value of this date/time using the specified level of precision
* using the system local time zone
*
*
* @param theValue
* The date value
* @param thePrecision
@ -465,9 +443,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public void setValueAsString(String theString) throws IllegalArgumentException {
clearTimeZone();
super.setValueAsString(theString);
if (isPrecisionAllowed(getPrecision()) == false) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + theString);
}
}
/**
@ -545,7 +520,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
/**
* Adds the given amount to the field specified by theField
*
*
* @param theField
* The field, uses constants from {@link Calendar} such as {@link Calendar#YEAR}
* @param theValue
@ -591,7 +566,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
timeZone = (theV3String.substring(i));
break;
}
// assertEquals("2013-02-02T20:13:03-05:00", DateAndTime.parseV3("20130202201303-0500").toString());
if (i == 4 || i == 6) {
b.append('-');
@ -600,7 +575,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
} else if (i == 10 || i == 12) {
b.append(':');
}
b.append(nextChar);
}
@ -615,7 +590,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
b.append(timeZone);
}
}
setValueAsString(b.toString());
}
}

View File

@ -26,11 +26,9 @@ public class BaseDateTimeTypeTests {
@ParameterizedTest
@MethodSource("getInvalidStringParams")
public <K extends BaseDateTimeType> void testInvalidString(Class<K> clazz, String param) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
public <K extends BaseDateTimeType> void testInvalidString(Class<K> clazz, String param) {
InvocationTargetException exceptionWrapper = Assertions.assertThrows(InvocationTargetException.class, () -> clazz.getConstructor(String.class).newInstance(param));
assertEquals(IllegalArgumentException.class, exceptionWrapper.getTargetException().getClass());
K srcInstance = clazz.getDeclaredConstructor().newInstance();
Assertions.assertThrows(IllegalArgumentException.class, () -> srcInstance.setValueAsString(param));
}
private static Stream<Arguments> getValidStringParams() {

View File

@ -30,7 +30,12 @@ package org.hl7.fhir.dstu2016may.model;
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.parser.DataFormatException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.utilities.DateTimeUtil;
import java.util.Calendar;
import java.util.Date;
@ -39,26 +44,15 @@ import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
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 ca.uhn.fhir.parser.DataFormatException;
import org.hl7.fhir.utilities.DateTimeUtil;
import static org.apache.commons.lang3.StringUtils.isBlank;
public abstract class BaseDateTimeType extends PrimitiveType<Date> {
private static final long serialVersionUID = 1L;
static final long NANOS_PER_MILLIS = 1000000L;
static final long NANOS_PER_SECOND = 1000000000L;
private static final long serialVersionUID = 1L;
private static final Map<String, TimeZone> timezoneCache = new ConcurrentHashMap<>();
private static final FastDateFormat ourHumanDateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM);
private static final FastDateFormat ourHumanDateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM);
private String myFractionalSeconds;
private TemporalPrecisionEnum myPrecision = null;
private TimeZone myTimeZone;
@ -74,14 +68,11 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
/**
* Constructor
*
* @throws DataFormatException
* If the specified precision is not allowed for this type
* @throws DataFormatException If the specified precision is not allowed for this type
*/
public BaseDateTimeType(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);
}
validatePrecisionAndThrowIllegalArgumentException();
}
/**
@ -90,16 +81,23 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision, TimeZone theTimeZone) {
this(theDate, thePrecision);
setTimeZone(theTimeZone);
validatePrecisionAndThrowIllegalArgumentException();
}
/**
* Constructor
*
* @throws DataFormatException
* If the specified precision is not allowed for this type
* @throws DataFormatException If the specified precision is not allowed for this type
*/
public BaseDateTimeType(String theString) {
setValueAsString(theString);
validatePrecisionAndThrowIllegalArgumentException();
}
private void validatePrecisionAndThrowIllegalArgumentException() {
if (!isPrecisionAllowed(getPrecision())) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + getValueAsString());
}
}
private void clearTimeZone() {
@ -203,6 +201,19 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return myPrecision;
}
/**
* Sets the precision for this datatype
*
* @throws DataFormatException
*/
public void setPrecision(TemporalPrecisionEnum thePrecision) throws DataFormatException {
if (thePrecision == null) {
throw new NullPointerException("Precision may not be null");
}
myPrecision = thePrecision;
updateStringValue();
}
/**
* Returns the TimeZone associated with this dateTime's value. May return <code>null</code> if no timezone was
* supplied.
@ -214,6 +225,13 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return myTimeZone;
}
public BaseDateTimeType setTimeZone(TimeZone theTimeZone) {
myTimeZone = theTimeZone;
myTimeZoneZulu = false;
updateStringValue();
return this;
}
/**
* Returns the value of this object as a {@link GregorianCalendar}
*/
@ -243,11 +261,17 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return myTimeZoneZulu;
}
public BaseDateTimeType setTimeZoneZulu(boolean theTimeZoneZulu) {
myTimeZoneZulu = theTimeZoneZulu;
myTimeZone = null;
updateStringValue();
return this;
}
/**
* Returns <code>true</code> if this object represents a date that is today's date
*
* @throws NullPointerException
* if {@link #getValue()} returns <code>null</code>
* @throws NullPointerException if {@link #getValue()} returns <code>null</code>
*/
public boolean isToday() {
Validate.notNull(getValue(), getClass().getSimpleName() + " contains null value");
@ -366,7 +390,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
myFractionalSeconds = "";
}
myPrecision = precision;
myPrecision = precision;
return cal.getTime();
}
@ -386,19 +410,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return retVal;
}
/**
* Sets the precision for this datatype
*
* @throws DataFormatException
*/
public void setPrecision(TemporalPrecisionEnum thePrecision) throws DataFormatException {
if (thePrecision == null) {
throw new NullPointerException("Precision may not be null");
}
myPrecision = thePrecision;
updateStringValue();
}
private BaseDateTimeType setTimeZone(String theWholeValue, String theValue) {
if (isBlank(theValue)) {
@ -420,20 +431,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return this;
}
public BaseDateTimeType setTimeZone(TimeZone theTimeZone) {
myTimeZone = theTimeZone;
myTimeZoneZulu = false;
updateStringValue();
return this;
}
public BaseDateTimeType setTimeZoneZulu(boolean theTimeZoneZulu) {
myTimeZoneZulu = theTimeZoneZulu;
myTimeZone = null;
updateStringValue();
return this;
}
/**
* Sets the value for this type using the given Java Date object as the time, and using the default precision for
* this datatype (unless the precision is already set), as well as the local timezone as determined by the local operating
@ -450,10 +447,8 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* well as the local timezone as determined by the local operating system. Both of
* these properties may be modified in subsequent calls if neccesary.
*
* @param theValue
* The date value
* @param thePrecision
* The precision
* @param theValue The date value
* @param thePrecision The precision
* @throws DataFormatException
*/
public void setValue(Date theValue, TemporalPrecisionEnum thePrecision) throws DataFormatException {
@ -478,9 +473,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public void setValueAsString(String theString) throws DataFormatException {
clearTimeZone();
super.setValueAsString(theString);
if (isPrecisionAllowed(getPrecision()) == false) {
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + theString);
}
}
private void throwBadDateFormat(String theValue) {
@ -534,6 +526,14 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getFieldValue(Calendar.YEAR);
}
/**
* Sets the year, e.g. 2015
*/
public BaseDateTimeType setYear(int theYear) {
setFieldValue(Calendar.YEAR, theYear, null, 0, 9999);
return this;
}
/**
* Returns the month with 0-index, e.g. 0=January
*/
@ -541,6 +541,14 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getFieldValue(Calendar.MONTH);
}
/**
* Sets the month with 0-index, e.g. 0=January
*/
public BaseDateTimeType setMonth(int theMonth) {
setFieldValue(Calendar.MONTH, theMonth, null, 0, 11);
return this;
}
/**
* Returns the month with 1-index, e.g. 1=the first day of the month
*/
@ -548,6 +556,14 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getFieldValue(Calendar.DAY_OF_MONTH);
}
/**
* Sets the month with 1-index, e.g. 1=the first day of the month
*/
public BaseDateTimeType setDay(int theDay) {
setFieldValue(Calendar.DAY_OF_MONTH, theDay, null, 0, 31);
return this;
}
/**
* Returns the hour of the day in a 24h clock, e.g. 13=1pm
*/
@ -555,6 +571,14 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getFieldValue(Calendar.HOUR_OF_DAY);
}
/**
* Sets the hour of the day in a 24h clock, e.g. 13=1pm
*/
public BaseDateTimeType setHour(int theHour) {
setFieldValue(Calendar.HOUR_OF_DAY, theHour, null, 0, 23);
return this;
}
/**
* Returns the minute of the hour in the range 0-59
*/
@ -562,6 +586,14 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getFieldValue(Calendar.MINUTE);
}
/**
* Sets the minute of the hour in the range 0-59
*/
public BaseDateTimeType setMinute(int theMinute) {
setFieldValue(Calendar.MINUTE, theMinute, null, 0, 59);
return this;
}
/**
* Returns the second of the minute in the range 0-59
*/
@ -569,6 +601,14 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getFieldValue(Calendar.SECOND);
}
/**
* Sets the second of the minute in the range 0-59
*/
public BaseDateTimeType setSecond(int theSecond) {
setFieldValue(Calendar.SECOND, theSecond, null, 0, 59);
return this;
}
/**
* Returns the milliseconds within the current second.
* <p>
@ -580,6 +620,18 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return getFieldValue(Calendar.MILLISECOND);
}
/**
* Sets the milliseconds within the current second.
* <p>
* Note that this method sets the
* same value as {@link #setNanos(long)} but with less precision.
* </p>
*/
public BaseDateTimeType setMillis(int theMillis) {
setFieldValue(Calendar.MILLISECOND, theMillis, null, 0, 999);
return this;
}
/**
* Returns the nanoseconds within the current second
* <p>
@ -596,66 +648,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
return Long.parseLong(retVal);
}
/**
* Sets the year, e.g. 2015
*/
public BaseDateTimeType setYear(int theYear) {
setFieldValue(Calendar.YEAR, theYear, null, 0, 9999);
return this;
}
/**
* Sets the month with 0-index, e.g. 0=January
*/
public BaseDateTimeType setMonth(int theMonth) {
setFieldValue(Calendar.MONTH, theMonth, null, 0, 11);
return this;
}
/**
* Sets the month with 1-index, e.g. 1=the first day of the month
*/
public BaseDateTimeType setDay(int theDay) {
setFieldValue(Calendar.DAY_OF_MONTH, theDay, null, 0, 31);
return this;
}
/**
* Sets the hour of the day in a 24h clock, e.g. 13=1pm
*/
public BaseDateTimeType setHour(int theHour) {
setFieldValue(Calendar.HOUR_OF_DAY, theHour, null, 0, 23);
return this;
}
/**
* Sets the minute of the hour in the range 0-59
*/
public BaseDateTimeType setMinute(int theMinute) {
setFieldValue(Calendar.MINUTE, theMinute, null, 0, 59);
return this;
}
/**
* Sets the second of the minute in the range 0-59
*/
public BaseDateTimeType setSecond(int theSecond) {
setFieldValue(Calendar.SECOND, theSecond, null, 0, 59);
return this;
}
/**
* Sets the milliseconds within the current second.
* <p>
* Note that this method sets the
* same value as {@link #setNanos(long)} but with less precision.
* </p>
*/
public BaseDateTimeType setMillis(int theMillis) {
setFieldValue(Calendar.MILLISECOND, theMillis, null, 0, 999);
return this;
}
/**
* Sets the nanoseconds within the current second
* <p>
@ -664,17 +656,17 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
* </p>
*/
public BaseDateTimeType setNanos(long theNanos) {
validateValueInRange(theNanos, 0, NANOS_PER_SECOND-1);
validateValueInRange(theNanos, 0, NANOS_PER_SECOND - 1);
String fractionalSeconds = StringUtils.leftPad(Long.toString(theNanos), 9, '0');
// Strip trailing 0s
for (int i = fractionalSeconds.length(); i > 0; i--) {
if (fractionalSeconds.charAt(i-1) != '0') {
if (fractionalSeconds.charAt(i - 1) != '0') {
fractionalSeconds = fractionalSeconds.substring(0, i);
break;
}
}
int millis = (int)(theNanos / NANOS_PER_MILLIS);
int millis = (int) (theNanos / NANOS_PER_MILLIS);
setFieldValue(Calendar.MILLISECOND, millis, fractionalSeconds, 0, 999);
return this;
}
@ -740,11 +732,11 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
if (b.length() == 16)
b.append(":00"); // schema rule, must have seconds
if (timeZone != null && b.length() > 10) {
if (timeZone.length() ==5) {
if (timeZone.length() == 5) {
b.append(timeZone.substring(0, 3));
b.append(':');
b.append(timeZone.substring(3));
}else {
} else {
b.append(timeZone);
}
}

View File

@ -27,11 +27,9 @@ public class BaseDateTimeTypeTests {
@ParameterizedTest
@MethodSource("getInvalidStringParams")
public <K extends BaseDateTimeType> void testInvalidString(Class<K> clazz, String param) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
public <K extends BaseDateTimeType> void testInvalidString(Class<K> clazz, String param) {
InvocationTargetException exceptionWrapper = Assertions.assertThrows(InvocationTargetException.class, () -> clazz.getConstructor(String.class).newInstance(param));
assertEquals(DataFormatException.class, exceptionWrapper.getTargetException().getClass());
K srcInstance = clazz.getDeclaredConstructor().newInstance();
Assertions.assertThrows(DataFormatException.class, () -> srcInstance.setValueAsString(param));
assertEquals(IllegalArgumentException.class, exceptionWrapper.getTargetException().getClass());
}
private static Stream<Arguments> getValidStringParams() {

View File

@ -79,7 +79,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
*/
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision) {
setValue(theDate, thePrecision);
validatePrecisionAndThrowDataFormatException(getValueAsString(), getPrecision());
validatePrecisionAndThrowIllegalArgumentException();
}
/**
@ -88,7 +88,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision, TimeZone theTimeZone) {
this(theDate, thePrecision);
setTimeZone(theTimeZone);
validatePrecisionAndThrowDataFormatException(getValueAsString(), getPrecision());
validatePrecisionAndThrowIllegalArgumentException();
}
/**
@ -99,7 +99,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
*/
public BaseDateTimeType(String theString) {
setValueAsString(theString);
validatePrecisionAndThrowIllegalArgumentException();
}
/**
@ -504,7 +504,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
}
if (precision == TemporalPrecisionEnum.MINUTE) {
validatePrecisionAndThrowDataFormatException(value, precision);
validatePrecisionAndThrowIllegalArgumentException();
}
myPrecision = precision;
@ -713,7 +713,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public void setValueAsString(String theString) throws DataFormatException {
clearTimeZone();
super.setValueAsString(theString);
validatePrecisionAndThrowDataFormatException(theString, getPrecision());
}
protected void setValueAsV3String(String theV3String) {
@ -833,9 +832,9 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
}
}
private void validatePrecisionAndThrowDataFormatException(String theValue, TemporalPrecisionEnum thePrecision) {
if (isPrecisionAllowed(thePrecision) == false) {
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + thePrecision + " precision): " + theValue);
private void validatePrecisionAndThrowIllegalArgumentException() {
if (!isPrecisionAllowed(getPrecision())) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + getValueAsString());
}
}

View File

@ -29,9 +29,7 @@ public class BaseDateTimeTypeTests {
@MethodSource("getInvalidStringParams")
public <K extends BaseDateTimeType> void testInvalidString(Class<K> clazz, String param) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
InvocationTargetException exceptionWrapper = Assertions.assertThrows(InvocationTargetException.class, () -> clazz.getConstructor(String.class).newInstance(param));
assertEquals(DataFormatException.class, exceptionWrapper.getTargetException().getClass());
K srcInstance = clazz.getDeclaredConstructor().newInstance();
Assertions.assertThrows(DataFormatException.class, () -> srcInstance.setValueAsString(param));
assertEquals(IllegalArgumentException.class, exceptionWrapper.getTargetException().getClass());
}
private static Stream<Arguments> getValidStringParams() {

View File

@ -43,7 +43,6 @@ import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
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 ca.uhn.fhir.parser.DataFormatException;
import org.hl7.fhir.utilities.DateTimeUtil;
@ -54,9 +53,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
static final long NANOS_PER_SECOND = 1000000000L;
private static final Map<String, TimeZone> timezoneCache = new ConcurrentHashMap<>();
private static final FastDateFormat ourHumanDateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM);
private static final FastDateFormat ourHumanDateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM);
private static final long serialVersionUID = 1L;
private String myFractionalSeconds;
@ -79,9 +75,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
*/
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision) {
setValue(theDate, thePrecision);
if (isPrecisionAllowed(thePrecision) == false) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + thePrecision + " precision): " + theDate);
}
validatePrecisionAndThrowIllegalArgumentException();
}
/**
@ -90,6 +84,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision, TimeZone theTimeZone) {
this(theDate, thePrecision);
setTimeZone(theTimeZone);
validatePrecisionAndThrowIllegalArgumentException();
}
/**
@ -100,9 +95,16 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
*/
public BaseDateTimeType(String theString) {
setValueAsString(theString);
validatePrecisionAndThrowIllegalArgumentException();
}
/**
private void validatePrecisionAndThrowIllegalArgumentException() {
if (!isPrecisionAllowed(getPrecision())) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + getValueAsString());
}
}
/**
* Adds the given amount to the field specified by theField
*
* @param theField
@ -709,9 +711,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public void setValueAsString(String theString) throws DataFormatException {
clearTimeZone();
super.setValueAsString(theString);
if (isPrecisionAllowed(getPrecision()) == false) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + theString);
}
}
protected void setValueAsV3String(String theV3String) {

View File

@ -1,6 +1,5 @@
package org.hl7.fhir.r4.model;
import ca.uhn.fhir.parser.DataFormatException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@ -96,8 +95,6 @@ public class BaseDateTimeTypeTest {
public <K extends BaseDateTimeType> void testInvalidString(Class<K> clazz, String param) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
InvocationTargetException exceptionWrapper = Assertions.assertThrows(InvocationTargetException.class, () -> clazz.getConstructor(String.class).newInstance(param));
assertEquals(IllegalArgumentException.class, exceptionWrapper.getTargetException().getClass());
K srcInstance = clazz.getDeclaredConstructor().newInstance();
Assertions.assertThrows(IllegalArgumentException.class, () -> srcInstance.setValueAsString(param));
}
private static Stream<Arguments> getValidStringParams() {

View File

@ -35,7 +35,6 @@ import ca.uhn.fhir.parser.DataFormatException;
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 org.hl7.fhir.utilities.Utilities;
@ -54,9 +53,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
static final long NANOS_PER_SECOND = 1000000000L;
private static final Map<String, TimeZone> timezoneCache = new ConcurrentHashMap<>();
private static final FastDateFormat ourHumanDateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM);
private static final FastDateFormat ourHumanDateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM);
private static final long serialVersionUID = 1L;
private String myFractionalSeconds;
@ -79,9 +75,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
*/
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision) {
setValue(theDate, thePrecision);
if (isPrecisionAllowed(thePrecision) == false) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + thePrecision + " precision): " + theDate);
}
validatePrecisionAndThrowIllegalArgumentException();
}
/**
@ -90,6 +84,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision, TimeZone theTimeZone) {
this(theDate, thePrecision);
setTimeZone(theTimeZone);
validatePrecisionAndThrowIllegalArgumentException();
}
/**
@ -100,8 +95,15 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
*/
public BaseDateTimeType(String theString) {
setValueAsString(theString);
validatePrecisionAndThrowIllegalArgumentException();
}
private void validatePrecisionAndThrowIllegalArgumentException() {
if (!isPrecisionAllowed(getPrecision())) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + getValueAsString());
}
}
/**
* Adds the given amount to the field specified by theField
*
@ -716,9 +718,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public void setValueAsString(String theString) throws DataFormatException {
clearTimeZone();
super.setValueAsString(theString);
if (isPrecisionAllowed(getPrecision()) == false) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + theString);
}
}
protected void setValueAsV3String(String theV3String) {

View File

@ -1,15 +1,15 @@
package org.hl7.fhir.r4b.model;
import java.lang.reflect.InvocationTargetException;
import java.util.TimeZone;
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.lang.reflect.InvocationTargetException;
import java.util.TimeZone;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class BaseDateTimeTypeTest {
@ -128,8 +128,6 @@ public class BaseDateTimeTypeTest {
public <K extends BaseDateTimeType> void testInvalidString(Class<K> clazz, String param) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
InvocationTargetException exceptionWrapper = Assertions.assertThrows(InvocationTargetException.class, () -> clazz.getConstructor(String.class).newInstance(param));
assertEquals(IllegalArgumentException.class, exceptionWrapper.getTargetException().getClass());
K srcInstance = clazz.getDeclaredConstructor().newInstance();
Assertions.assertThrows(IllegalArgumentException.class, () -> srcInstance.setValueAsString(param));
}
private static Stream<Arguments> getValidStringParams() {

View File

@ -35,7 +35,6 @@ import ca.uhn.fhir.parser.DataFormatException;
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 org.hl7.fhir.utilities.Utilities;
@ -54,9 +53,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
static final long NANOS_PER_SECOND = 1000000000L;
private static final Map<String, TimeZone> timezoneCache = new ConcurrentHashMap<>();
private static final FastDateFormat ourHumanDateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM);
private static final FastDateFormat ourHumanDateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM);
private static final long serialVersionUID = 1L;
private String myFractionalSeconds;
@ -79,17 +75,16 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
*/
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision) {
setValue(theDate, thePrecision);
if (isPrecisionAllowed(thePrecision) == false) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + thePrecision + " precision): " + theDate);
}
}
validatePrecisionAndThrowIllegalArgumentException();
}
/**
/**
* Constructor
*/
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision, TimeZone theTimeZone) {
this(theDate, thePrecision);
setTimeZone(theTimeZone);
validatePrecisionAndThrowIllegalArgumentException();
}
/**
@ -100,8 +95,15 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
*/
public BaseDateTimeType(String theString) {
setValueAsString(theString);
validatePrecisionAndThrowIllegalArgumentException();
}
private void validatePrecisionAndThrowIllegalArgumentException() {
if (!isPrecisionAllowed(getPrecision())) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + getValueAsString());
}
}
/**
* Adds the given amount to the field specified by theField
*
@ -716,9 +718,6 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
public void setValueAsString(String theString) throws DataFormatException {
clearTimeZone();
super.setValueAsString(theString);
if (isPrecisionAllowed(getPrecision()) == false) {
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + theString);
}
}
protected void setValueAsV3String(String theV3String) {

View File

@ -1,15 +1,15 @@
package org.hl7.fhir.r5.model;
import java.lang.reflect.InvocationTargetException;
import java.util.TimeZone;
import java.util.stream.Stream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.lang.reflect.InvocationTargetException;
import java.util.TimeZone;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class BaseDateTimeTypeTest {
@ -128,8 +128,6 @@ public class BaseDateTimeTypeTest {
public <K extends BaseDateTimeType> void testInvalidString(Class<K> clazz, String param) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
InvocationTargetException exceptionWrapper = Assertions.assertThrows(InvocationTargetException.class, () -> clazz.getConstructor(String.class).newInstance(param));
assertEquals(IllegalArgumentException.class, exceptionWrapper.getTargetException().getClass());
K srcInstance = clazz.getDeclaredConstructor().newInstance();
Assertions.assertThrows(IllegalArgumentException.class, () -> srcInstance.setValueAsString(param));
}
private static Stream<Arguments> getValidStringParams() {