Port in changes from RI
This commit is contained in:
parent
826c91087b
commit
88117f8d6e
|
@ -16,13 +16,13 @@ import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
|
||||||
public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
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_MILLIS = 1000000L;
|
||||||
static final long NANOS_PER_SECOND = 1000000000L;
|
|
||||||
|
|
||||||
|
static final long NANOS_PER_SECOND = 1000000000L;
|
||||||
private static final FastDateFormat ourHumanDateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM);
|
private static final FastDateFormat ourHumanDateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM);
|
||||||
|
|
||||||
private static final FastDateFormat ourHumanDateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM);
|
private static final FastDateFormat ourHumanDateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM);
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private String myFractionalSeconds;
|
private String myFractionalSeconds;
|
||||||
private TemporalPrecisionEnum myPrecision = null;
|
private TemporalPrecisionEnum myPrecision = null;
|
||||||
|
@ -39,13 +39,13 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @throws DataFormatException
|
* @throws IllegalArgumentException
|
||||||
* If the specified precision is not allowed for this type
|
* If the specified precision is not allowed for this type
|
||||||
*/
|
*/
|
||||||
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision) {
|
public BaseDateTimeType(Date theDate, TemporalPrecisionEnum thePrecision) {
|
||||||
setValue(theDate, thePrecision);
|
setValue(theDate, thePrecision);
|
||||||
if (isPrecisionAllowed(thePrecision) == false) {
|
if (isPrecisionAllowed(thePrecision) == false) {
|
||||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + thePrecision + " precision): " + theDate);
|
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + thePrecision + " precision): " + theDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,16 +60,76 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
* @throws DataFormatException
|
* @throws IllegalArgumentException
|
||||||
* If the specified precision is not allowed for this type
|
* If the specified precision is not allowed for this type
|
||||||
*/
|
*/
|
||||||
public BaseDateTimeType(String theString) {
|
public BaseDateTimeType(String theString) {
|
||||||
setValueAsString(theString);
|
setValueAsString(theString);
|
||||||
if (isPrecisionAllowed(getPrecision()) == false) {
|
if (isPrecisionAllowed(getPrecision()) == false) {
|
||||||
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + theString);
|
throw new IllegalArgumentException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support " + getPrecision() + " precision): " + theString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* The number to add (or subtract for a negative number)
|
||||||
|
*/
|
||||||
|
public void add(int theField, int theValue) {
|
||||||
|
switch (theField) {
|
||||||
|
case Calendar.YEAR:
|
||||||
|
setValue(DateUtils.addYears(getValue(), theValue), getPrecision());
|
||||||
|
break;
|
||||||
|
case Calendar.MONTH:
|
||||||
|
setValue(DateUtils.addMonths(getValue(), theValue), getPrecision());
|
||||||
|
break;
|
||||||
|
case Calendar.DATE:
|
||||||
|
setValue(DateUtils.addDays(getValue(), theValue), getPrecision());
|
||||||
|
break;
|
||||||
|
case Calendar.HOUR:
|
||||||
|
setValue(DateUtils.addHours(getValue(), theValue), getPrecision());
|
||||||
|
break;
|
||||||
|
case Calendar.MINUTE:
|
||||||
|
setValue(DateUtils.addMinutes(getValue(), theValue), getPrecision());
|
||||||
|
break;
|
||||||
|
case Calendar.SECOND:
|
||||||
|
setValue(DateUtils.addSeconds(getValue(), theValue), getPrecision());
|
||||||
|
break;
|
||||||
|
case Calendar.MILLISECOND:
|
||||||
|
setValue(DateUtils.addMilliseconds(getValue(), theValue), getPrecision());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new DataFormatException("Unknown field constant: " + theField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if the given object represents a date/time before <code>this</code> object
|
||||||
|
*
|
||||||
|
* @throws NullPointerException
|
||||||
|
* If <code>this.getValue()</code> or <code>theDateTimeType.getValue()</code>
|
||||||
|
* return <code>null</code>
|
||||||
|
*/
|
||||||
|
public boolean after(DateTimeType theDateTimeType) {
|
||||||
|
validateBeforeOrAfter(theDateTimeType);
|
||||||
|
return getValue().after(theDateTimeType.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns <code>true</code> if the given object represents a date/time before <code>this</code> object
|
||||||
|
*
|
||||||
|
* @throws NullPointerException
|
||||||
|
* If <code>this.getValue()</code> or <code>theDateTimeType.getValue()</code>
|
||||||
|
* return <code>null</code>
|
||||||
|
*/
|
||||||
|
public boolean before(DateTimeType theDateTimeType) {
|
||||||
|
validateBeforeOrAfter(theDateTimeType);
|
||||||
|
return getValue().before(theDateTimeType.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
private void clearTimeZone() {
|
private void clearTimeZone() {
|
||||||
myTimeZone = null;
|
myTimeZone = null;
|
||||||
myTimeZoneZulu = false;
|
myTimeZoneZulu = false;
|
||||||
|
@ -140,11 +200,74 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the month with 1-index, e.g. 1=the first day of the month
|
||||||
|
*/
|
||||||
|
public Integer getDay() {
|
||||||
|
return getFieldValue(Calendar.DAY_OF_MONTH);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the default precision for the given datatype
|
* Returns the default precision for the given datatype
|
||||||
*/
|
*/
|
||||||
protected abstract TemporalPrecisionEnum getDefaultPrecisionForDatatype();
|
protected abstract TemporalPrecisionEnum getDefaultPrecisionForDatatype();
|
||||||
|
|
||||||
|
private Integer getFieldValue(int theField) {
|
||||||
|
if (getValue() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Calendar cal = getValueAsCalendar();
|
||||||
|
return cal.get(theField);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hour of the day in a 24h clock, e.g. 13=1pm
|
||||||
|
*/
|
||||||
|
public Integer getHour() {
|
||||||
|
return getFieldValue(Calendar.HOUR_OF_DAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the milliseconds within the current second.
|
||||||
|
* <p>
|
||||||
|
* Note that this method returns the
|
||||||
|
* same value as {@link #getNanos()} but with less precision.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public Integer getMillis() {
|
||||||
|
return getFieldValue(Calendar.MILLISECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the minute of the hour in the range 0-59
|
||||||
|
*/
|
||||||
|
public Integer getMinute() {
|
||||||
|
return getFieldValue(Calendar.MINUTE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the month with 0-index, e.g. 0=January
|
||||||
|
*/
|
||||||
|
public Integer getMonth() {
|
||||||
|
return getFieldValue(Calendar.MONTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the nanoseconds within the current second
|
||||||
|
* <p>
|
||||||
|
* Note that this method returns the
|
||||||
|
* same value as {@link #getMillis()} but with more precision.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public Long getNanos() {
|
||||||
|
if (isBlank(myFractionalSeconds)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String retVal = StringUtils.rightPad(myFractionalSeconds, 9, '0');
|
||||||
|
retVal = retVal.substring(0, 9);
|
||||||
|
return Long.parseLong(retVal);
|
||||||
|
}
|
||||||
|
|
||||||
private int getOffsetIndex(String theValueString) {
|
private int getOffsetIndex(String theValueString) {
|
||||||
int plusIndex = theValueString.indexOf('+', 16);
|
int plusIndex = theValueString.indexOf('+', 16);
|
||||||
int minusIndex = theValueString.indexOf('-', 16);
|
int minusIndex = theValueString.indexOf('-', 16);
|
||||||
|
@ -171,6 +294,13 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
return myPrecision;
|
return myPrecision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the second of the minute in the range 0-59
|
||||||
|
*/
|
||||||
|
public Integer getSecond() {
|
||||||
|
return getFieldValue(Calendar.SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the TimeZone associated with this dateTime's value. May return <code>null</code> if no timezone was
|
* Returns the TimeZone associated with this dateTime's value. May return <code>null</code> if no timezone was
|
||||||
* supplied.
|
* supplied.
|
||||||
|
@ -199,6 +329,13 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
return cal;
|
return cal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the year, e.g. 2015
|
||||||
|
*/
|
||||||
|
public Integer getYear() {
|
||||||
|
return getFieldValue(Calendar.YEAR);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be implemented by subclasses to indicate whether the given precision is allowed by this type
|
* To be implemented by subclasses to indicate whether the given precision is allowed by this type
|
||||||
*/
|
*/
|
||||||
|
@ -272,7 +409,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
int offsetIdx = getOffsetIndex(value);
|
int offsetIdx = getOffsetIndex(value);
|
||||||
String time;
|
String time;
|
||||||
if (offsetIdx == -1) {
|
if (offsetIdx == -1) {
|
||||||
//throwBadDateFormat(theValue);
|
// throwBadDateFormat(theValue);
|
||||||
// No offset - should this be an error?
|
// No offset - should this be an error?
|
||||||
time = value.substring(11);
|
time = value.substring(11);
|
||||||
} else {
|
} else {
|
||||||
|
@ -334,7 +471,7 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
myFractionalSeconds = "";
|
myFractionalSeconds = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
myPrecision = precision;
|
myPrecision = precision;
|
||||||
return cal.getTime();
|
return cal.getTime();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -354,6 +491,92 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFieldValue(int theField, int theValue, String theFractionalSeconds, int theMinimum, int theMaximum) {
|
||||||
|
validateValueInRange(theValue, theMinimum, theMaximum);
|
||||||
|
Calendar cal;
|
||||||
|
if (getValue() == null) {
|
||||||
|
cal = new GregorianCalendar(0, 0, 0);
|
||||||
|
} else {
|
||||||
|
cal = getValueAsCalendar();
|
||||||
|
}
|
||||||
|
if (theField != -1) {
|
||||||
|
cal.set(theField, theValue);
|
||||||
|
}
|
||||||
|
if (theFractionalSeconds != null) {
|
||||||
|
myFractionalSeconds = theFractionalSeconds;
|
||||||
|
} else if (theField == Calendar.MILLISECOND) {
|
||||||
|
myFractionalSeconds = StringUtils.leftPad(Integer.toString(theValue), 3, '0');
|
||||||
|
}
|
||||||
|
super.setValue(cal.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 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 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 month with 0-index, e.g. 0=January
|
||||||
|
*/
|
||||||
|
public BaseDateTimeType setMonth(int theMonth) {
|
||||||
|
setFieldValue(Calendar.MONTH, theMonth, null, 0, 11);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the nanoseconds within the current second
|
||||||
|
* <p>
|
||||||
|
* Note that this method sets the
|
||||||
|
* same value as {@link #setMillis(int)} but with more precision.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public BaseDateTimeType setNanos(long theNanos) {
|
||||||
|
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') {
|
||||||
|
fractionalSeconds = fractionalSeconds.substring(0, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int millis = (int) (theNanos / NANOS_PER_MILLIS);
|
||||||
|
setFieldValue(Calendar.MILLISECOND, millis, fractionalSeconds, 0, 999);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the precision for this datatype
|
* Sets the precision for this datatype
|
||||||
*
|
*
|
||||||
|
@ -367,6 +590,14 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
updateStringValue();
|
updateStringValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
private BaseDateTimeType setTimeZone(String theWholeValue, String theValue) {
|
private BaseDateTimeType setTimeZone(String theWholeValue, String theValue) {
|
||||||
|
|
||||||
if (isBlank(theValue)) {
|
if (isBlank(theValue)) {
|
||||||
|
@ -448,6 +679,55 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
super.setValueAsString(theValue);
|
super.setValueAsString(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setValueAsV3String(String theV3String) {
|
||||||
|
if (StringUtils.isBlank(theV3String)) {
|
||||||
|
setValue(null);
|
||||||
|
} else {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
String timeZone = null;
|
||||||
|
for (int i = 0; i < theV3String.length(); i++) {
|
||||||
|
char nextChar = theV3String.charAt(i);
|
||||||
|
if (nextChar == '+' || nextChar == '-' || nextChar == 'Z') {
|
||||||
|
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('-');
|
||||||
|
} else if (i == 8) {
|
||||||
|
b.append('T');
|
||||||
|
} else if (i == 10 || i == 12) {
|
||||||
|
b.append(':');
|
||||||
|
}
|
||||||
|
|
||||||
|
b.append(nextChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b.length() == 16)
|
||||||
|
b.append(":00"); // schema rule, must have seconds
|
||||||
|
if (timeZone != null && b.length() > 10) {
|
||||||
|
if (timeZone.length() == 5) {
|
||||||
|
b.append(timeZone.substring(0, 3));
|
||||||
|
b.append(':');
|
||||||
|
b.append(timeZone.substring(3));
|
||||||
|
} else {
|
||||||
|
b.append(timeZone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setValueAsString(b.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the year, e.g. 2015
|
||||||
|
*/
|
||||||
|
public BaseDateTimeType setYear(int theYear) {
|
||||||
|
setFieldValue(Calendar.YEAR, theYear, null, 0, 9999);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private void throwBadDateFormat(String theValue) {
|
private void throwBadDateFormat(String theValue) {
|
||||||
throw new DataFormatException("Invalid date/time format: \"" + theValue + "\"");
|
throw new DataFormatException("Invalid date/time format: \"" + theValue + "\"");
|
||||||
}
|
}
|
||||||
|
@ -456,6 +736,18 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
throw new DataFormatException("Invalid date/time format: \"" + theValue + "\": " + theMesssage);
|
throw new DataFormatException("Invalid date/time format: \"" + theValue + "\": " + theMesssage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a view of this date/time as a Calendar object. Note that the returned
|
||||||
|
* Calendar object is entirely independent from <code>this</code> object. Changes to the
|
||||||
|
* calendar will not affect <code>this</code>.
|
||||||
|
*/
|
||||||
|
public Calendar toCalendar() {
|
||||||
|
Calendar retVal = Calendar.getInstance();
|
||||||
|
retVal.setTime(getValue());
|
||||||
|
retVal.setTimeZone(getTimeZone());
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a human readable version of this date/time using the system local format.
|
* Returns a human readable version of this date/time using the system local format.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -502,6 +794,18 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateBeforeOrAfter(DateTimeType theDateTimeType) {
|
||||||
|
if (getValue() == null) {
|
||||||
|
throw new NullPointerException("This BaseDateTimeType does not contain a value (getValue() returns null)");
|
||||||
|
}
|
||||||
|
if (theDateTimeType == null) {
|
||||||
|
throw new NullPointerException("theDateTimeType must not be null");
|
||||||
|
}
|
||||||
|
if (theDateTimeType.getValue() == null) {
|
||||||
|
throw new NullPointerException("The given BaseDateTimeType does not contain a value (theDateTimeType.getValue() returns null)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void validateCharAtIndexIs(String theValue, int theIndex, char theChar) {
|
private void validateCharAtIndexIs(String theValue, int theIndex, char theChar) {
|
||||||
if (theValue.charAt(theIndex) != theChar) {
|
if (theValue.charAt(theIndex) != theChar) {
|
||||||
throwBadDateFormat(theValue, "Expected character '" + theChar + "' at index " + theIndex + " but found " + theValue.charAt(theIndex));
|
throwBadDateFormat(theValue, "Expected character '" + theChar + "' at index " + theIndex + " but found " + theValue.charAt(theIndex));
|
||||||
|
@ -514,230 +818,10 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the year, e.g. 2015
|
|
||||||
*/
|
|
||||||
public Integer getYear() {
|
|
||||||
return getFieldValue(Calendar.YEAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the month with 0-index, e.g. 0=January
|
|
||||||
*/
|
|
||||||
public Integer getMonth() {
|
|
||||||
return getFieldValue(Calendar.MONTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the month with 1-index, e.g. 1=the first day of the month
|
|
||||||
*/
|
|
||||||
public Integer getDay() {
|
|
||||||
return getFieldValue(Calendar.DAY_OF_MONTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the hour of the day in a 24h clock, e.g. 13=1pm
|
|
||||||
*/
|
|
||||||
public Integer getHour() {
|
|
||||||
return getFieldValue(Calendar.HOUR_OF_DAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the minute of the hour in the range 0-59
|
|
||||||
*/
|
|
||||||
public Integer getMinute() {
|
|
||||||
return getFieldValue(Calendar.MINUTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the second of the minute in the range 0-59
|
|
||||||
*/
|
|
||||||
public Integer getSecond() {
|
|
||||||
return getFieldValue(Calendar.SECOND);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the milliseconds within the current second.
|
|
||||||
* <p>
|
|
||||||
* Note that this method returns the
|
|
||||||
* same value as {@link #getNanos()} but with less precision.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public Integer getMillis() {
|
|
||||||
return getFieldValue(Calendar.MILLISECOND);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the nanoseconds within the current second
|
|
||||||
* <p>
|
|
||||||
* Note that this method returns the
|
|
||||||
* same value as {@link #getMillis()} but with more precision.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public Long getNanos() {
|
|
||||||
if (isBlank(myFractionalSeconds)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String retVal = StringUtils.rightPad(myFractionalSeconds, 9, '0');
|
|
||||||
retVal = retVal.substring(0, 9);
|
|
||||||
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>
|
|
||||||
* Note that this method sets the
|
|
||||||
* same value as {@link #setMillis(int)} but with more precision.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public BaseDateTimeType setNanos(long theNanos) {
|
|
||||||
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') {
|
|
||||||
fractionalSeconds = fractionalSeconds.substring(0, i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int millis = (int)(theNanos / NANOS_PER_MILLIS);
|
|
||||||
setFieldValue(Calendar.MILLISECOND, millis, fractionalSeconds, 0, 999);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setFieldValue(int theField, int theValue, String theFractionalSeconds, int theMinimum, int theMaximum) {
|
|
||||||
validateValueInRange(theValue, theMinimum, theMaximum);
|
|
||||||
Calendar cal;
|
|
||||||
if (getValue() == null) {
|
|
||||||
cal = new GregorianCalendar(0, 0, 0);
|
|
||||||
} else {
|
|
||||||
cal = getValueAsCalendar();
|
|
||||||
}
|
|
||||||
if (theField != -1) {
|
|
||||||
cal.set(theField, theValue);
|
|
||||||
}
|
|
||||||
if (theFractionalSeconds != null) {
|
|
||||||
myFractionalSeconds = theFractionalSeconds;
|
|
||||||
} else if (theField == Calendar.MILLISECOND) {
|
|
||||||
myFractionalSeconds = StringUtils.leftPad(Integer.toString(theValue), 3, '0');
|
|
||||||
}
|
|
||||||
super.setValue(cal.getTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateValueInRange(long theValue, long theMinimum, long theMaximum) {
|
private void validateValueInRange(long theValue, long theMinimum, long theMaximum) {
|
||||||
if (theValue < theMinimum || theValue > theMaximum) {
|
if (theValue < theMinimum || theValue > theMaximum) {
|
||||||
throw new IllegalArgumentException("Value " + theValue + " is not between allowable range: " + theMinimum + " - " + theMaximum);
|
throw new IllegalArgumentException("Value " + theValue + " is not between allowable range: " + theMinimum + " - " + theMaximum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Integer getFieldValue(int theField) {
|
|
||||||
if (getValue() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Calendar cal = getValueAsCalendar();
|
|
||||||
return cal.get(theField);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setValueAsV3String(String theV3String) {
|
|
||||||
if (StringUtils.isBlank(theV3String)) {
|
|
||||||
setValue(null);
|
|
||||||
} else {
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
String timeZone = null;
|
|
||||||
for (int i = 0; i < theV3String.length(); i++) {
|
|
||||||
char nextChar = theV3String.charAt(i);
|
|
||||||
if (nextChar == '+' || nextChar == '-' || nextChar == 'Z') {
|
|
||||||
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('-');
|
|
||||||
} else if (i == 8) {
|
|
||||||
b.append('T');
|
|
||||||
} else if (i == 10 || i == 12) {
|
|
||||||
b.append(':');
|
|
||||||
}
|
|
||||||
|
|
||||||
b.append(nextChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (b.length() == 16)
|
|
||||||
b.append(":00"); // schema rule, must have seconds
|
|
||||||
if (timeZone != null && b.length() > 10) {
|
|
||||||
if (timeZone.length() ==5) {
|
|
||||||
b.append(timeZone.substring(0, 3));
|
|
||||||
b.append(':');
|
|
||||||
b.append(timeZone.substring(3));
|
|
||||||
}else {
|
|
||||||
b.append(timeZone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setValueAsString(b.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,7 @@ package org.hl7.fhir.dstu3.model;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.hamcrest.Matchers.either;
|
import static org.hamcrest.Matchers.either;
|
||||||
import static org.hamcrest.Matchers.endsWith;
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
@ -55,6 +50,65 @@ public class BaseDateTimeTypeDstu3Test {
|
||||||
assertEquals("1995-11-15T04:58:08Z", dt.getValueAsString());
|
assertEquals("1995-11-15T04:58:08Z", dt.getValueAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAfter() {
|
||||||
|
assertTrue(new DateTimeType("2011-01-01T12:12:12Z").after(new DateTimeType("2011-01-01T12:12:11Z")));
|
||||||
|
assertFalse(new DateTimeType("2011-01-01T12:12:11Z").after(new DateTimeType("2011-01-01T12:12:12Z")));
|
||||||
|
assertFalse(new DateTimeType("2011-01-01T12:12:12Z").after(new DateTimeType("2011-01-01T12:12:12Z")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBefore() {
|
||||||
|
assertFalse(new DateTimeType("2011-01-01T12:12:12Z").before(new DateTimeType("2011-01-01T12:12:11Z")));
|
||||||
|
assertTrue(new DateTimeType("2011-01-01T12:12:11Z").before(new DateTimeType("2011-01-01T12:12:12Z")));
|
||||||
|
assertFalse(new DateTimeType("2011-01-01T12:12:12Z").before(new DateTimeType("2011-01-01T12:12:12Z")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test()
|
||||||
|
public void testAfterNull() {
|
||||||
|
try {
|
||||||
|
assertTrue(new DateTimeType().after(new DateTimeType("2011-01-01T12:12:11Z")));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("This BaseDateTimeType does not contain a value (getValue() returns null)", e.getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertTrue(new DateTimeType("2011-01-01T12:12:11Z").after(new DateTimeType()));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("The given BaseDateTimeType does not contain a value (theDateTimeType.getValue() returns null)", e.getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertTrue(new DateTimeType("2011-01-01T12:12:11Z").after(null));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("theDateTimeType must not be null", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test()
|
||||||
|
public void testBeforeNull1() {
|
||||||
|
try {
|
||||||
|
assertTrue(new DateTimeType().before(new DateTimeType("2011-01-01T12:12:11Z")));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("This BaseDateTimeType does not contain a value (getValue() returns null)", e.getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertTrue(new DateTimeType("2011-01-01T12:12:11Z").before(new DateTimeType()));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("The given BaseDateTimeType does not contain a value (theDateTimeType.getValue() returns null)", e.getMessage());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
assertTrue(new DateTimeType("2011-01-01T12:12:11Z").before(null));
|
||||||
|
fail();
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
assertEquals("theDateTimeType must not be null", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test for #57
|
* Test for #57
|
||||||
*/
|
*/
|
||||||
|
@ -64,13 +118,13 @@ public class BaseDateTimeTypeDstu3Test {
|
||||||
try {
|
try {
|
||||||
new DateType("2001-01-02T11:13:33");
|
new DateType("2001-01-02T11:13:33");
|
||||||
fail();
|
fail();
|
||||||
} catch (DataFormatException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
assertThat(e.getMessage(), containsString("precision"));
|
assertThat(e.getMessage(), containsString("precision"));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
new InstantType("2001-01-02");
|
new InstantType("2001-01-02");
|
||||||
fail();
|
fail();
|
||||||
} catch (DataFormatException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
assertThat(e.getMessage(), containsString("precision"));
|
assertThat(e.getMessage(), containsString("precision"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue