Add unit test for BaseDateTimeDt

This commit is contained in:
James Agnew 2014-11-14 14:36:26 -05:00
parent df2011388c
commit e2d59ef5ba
2 changed files with 113 additions and 44 deletions

View File

@ -23,9 +23,12 @@ package ca.uhn.fhir.model.primitive;
import static ca.uhn.fhir.model.api.TemporalPrecisionEnum.*; import static ca.uhn.fhir.model.api.TemporalPrecisionEnum.*;
import java.text.ParseException; import java.text.ParseException;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -39,6 +42,10 @@ import ca.uhn.fhir.parser.DataFormatException;
public abstract class BaseDateTimeDt extends BasePrimitive<Date> { public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
/*
* 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 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 Pattern ourYearDashMonthPattern = Pattern.compile("[0-9]{4}-[0-9]{2}");
private static final FastDateFormat ourYearFormat = FastDateFormat.getInstance("yyyy"); private static final FastDateFormat ourYearFormat = FastDateFormat.getInstance("yyyy");
@ -47,23 +54,43 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
private static final Pattern ourYearMonthDayPattern = Pattern.compile("[0-9]{4}[0-9]{2}[0-9]{2}"); private static final Pattern ourYearMonthDayPattern = Pattern.compile("[0-9]{4}[0-9]{2}[0-9]{2}");
private static final FastDateFormat ourYearMonthDayTimeFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss"); private static final FastDateFormat ourYearMonthDayTimeFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss");
private static final FastDateFormat ourYearMonthDayTimeMilliFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS"); private static final FastDateFormat ourYearMonthDayTimeMilliFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS");
private static final FastDateFormat ourYearMonthDayTimeUTCZFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"));
private static final FastDateFormat ourYearMonthDayTimeMilliUTCZFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("UTC")); private static final FastDateFormat ourYearMonthDayTimeMilliUTCZFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", TimeZone.getTimeZone("UTC"));
private static final FastDateFormat ourYearMonthDayTimeMilliZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ"); private static final FastDateFormat ourYearMonthDayTimeMilliZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");
private static final FastDateFormat ourYearMonthDayTimeUTCZFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("UTC"));
private static final FastDateFormat ourYearMonthDayTimeZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ"); private static final FastDateFormat ourYearMonthDayTimeZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ");
private static final FastDateFormat ourYearMonthFormat = FastDateFormat.getInstance("yyyy-MM"); private static final FastDateFormat ourYearMonthFormat = FastDateFormat.getInstance("yyyy-MM");
private static final FastDateFormat ourYearMonthNoDashesFormat = FastDateFormat.getInstance("yyyyMM"); 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 ourYearMonthPattern = Pattern.compile("[0-9]{4}[0-9]{2}");
private static final Pattern ourYearPattern = Pattern.compile("[0-9]{4}"); private static final Pattern ourYearPattern = Pattern.compile("[0-9]{4}");
static {
ArrayList<FastDateFormat> formatters = new ArrayList<FastDateFormat>();
formatters.add(ourYearFormat);
formatters.add(ourYearMonthDayFormat);
formatters.add(ourYearMonthDayNoDashesFormat);
formatters.add(ourYearMonthDayTimeFormat);
formatters.add(ourYearMonthDayTimeMilliFormat);
formatters.add(ourYearMonthDayTimeUTCZFormat);
formatters.add(ourYearMonthDayTimeMilliUTCZFormat);
formatters.add(ourYearMonthDayTimeMilliZoneFormat);
formatters.add(ourYearMonthDayTimeZoneFormat);
formatters.add(ourYearMonthFormat);
formatters.add(ourYearMonthNoDashesFormat);
ourFormatters = Collections.unmodifiableList(formatters);
}
private TemporalPrecisionEnum myPrecision = TemporalPrecisionEnum.SECOND; private TemporalPrecisionEnum myPrecision = TemporalPrecisionEnum.SECOND;
private TimeZone myTimeZone; private TimeZone myTimeZone;
private boolean myTimeZoneZulu = false; private boolean myTimeZoneZulu = false;
private Date myValue; private Date myValue;
private void clearTimeZone() {
myTimeZone = null;
myTimeZoneZulu = false;
}
/** /**
* Gets the precision for this datatype using field values from {@link Calendar}, such as {@link Calendar#MONTH}. * Gets the precision for this datatype using field values from {@link Calendar}, such as {@link Calendar#MONTH}. Default is {@link Calendar#DAY_OF_MONTH}
* Default is {@link Calendar#DAY_OF_MONTH}
* *
* @see #setPrecision(int) * @see #setPrecision(int)
*/ */
@ -121,6 +148,11 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
} }
} }
/**
* To be implemented by subclasses to indicate whether the given precision is allowed by this type
*/
abstract boolean isPrecisionAllowed(TemporalPrecisionEnum thePrecision);
public boolean isTimeZoneZulu() { public boolean isTimeZoneZulu() {
return myTimeZoneZulu; return myTimeZoneZulu;
} }
@ -154,6 +186,20 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
myPrecision = thePrecision; myPrecision = thePrecision;
} }
private void setTimeZone(String theValueString, boolean hasMillis) {
clearTimeZone();
int timeZoneStart = 19;
if (hasMillis)
timeZoneStart += 4;
if (theValueString.endsWith("Z")) {
setTimeZoneZulu(true);
} else if (theValueString.indexOf("GMT", timeZoneStart) != -1) {
setTimeZone(TimeZone.getTimeZone(theValueString.substring(timeZoneStart)));
} else if (theValueString.indexOf('+', timeZoneStart) != -1 || theValueString.indexOf('-', timeZoneStart) != -1) {
setTimeZone(TimeZone.getTimeZone("GMT" + theValueString.substring(timeZoneStart)));
}
}
public void setTimeZone(TimeZone theTimeZone) { public void setTimeZone(TimeZone theTimeZone) {
myTimeZone = theTimeZone; myTimeZone = theTimeZone;
} }
@ -217,7 +263,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
} else { } else {
throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue); throw new DataFormatException("Invalid date/time string (datatype " + getClass().getSimpleName() + " does not support DAY precision): " + theValue);
} }
} else if (theValue.length() >= 18) { //date and time with possible time zone } else if (theValue.length() >= 18) { // date and time with possible time zone
int dotIndex = theValue.indexOf('.', 18); int dotIndex = theValue.indexOf('.', 18);
boolean hasMillis = dotIndex > -1; boolean hasMillis = dotIndex > -1;
@ -227,34 +273,32 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
throw new DataFormatException("Invalid date/time string (data type " + getClass().getSimpleName() + " does not support MILLIS precision):" + theValue); throw new DataFormatException("Invalid date/time string (data type " + getClass().getSimpleName() + " does not support MILLIS precision):" + theValue);
} }
if(hasMillis){ if (hasMillis) {
try { try {
myValue = ourYearMonthDayTimeMilliZoneFormat.parse(theValue); if (hasOffset(theValue)) {
} catch (ParseException p){ myValue = ourYearMonthDayTimeMilliZoneFormat.parse(theValue);
try{ } else if (theValue.endsWith("Z"))
if(theValue.endsWith("Z")) myValue = ourYearMonthDayTimeMilliUTCZFormat.parse(theValue);
myValue = ourYearMonthDayTimeMilliUTCZFormat.parse(theValue); else
else myValue = ourYearMonthDayTimeMilliFormat.parse(theValue);
myValue = ourYearMonthDayTimeMilliFormat.parse(theValue); } catch (ParseException p2) {
}catch(ParseException p2){ throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
}
} }
setTimeZone(theValue, hasMillis); setTimeZone(theValue, hasMillis);
setPrecision(TemporalPrecisionEnum.MILLI); setPrecision(TemporalPrecisionEnum.MILLI);
}else{ } else {
try{ try {
myValue = ourYearMonthDayTimeZoneFormat.parse(theValue); if (hasOffset(theValue)) {
}catch(ParseException p){ myValue = ourYearMonthDayTimeZoneFormat.parse(theValue);
try{ } else if (theValue.endsWith("Z")) {
if(theValue.endsWith("Z")) myValue = ourYearMonthDayTimeUTCZFormat.parse(theValue);
myValue = ourYearMonthDayTimeUTCZFormat.parse(theValue); } else {
else myValue = ourYearMonthDayTimeFormat.parse(theValue);
myValue = ourYearMonthDayTimeFormat.parse(theValue);
}catch(ParseException p2){
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
} }
} catch (ParseException p2) {
throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue);
} }
setTimeZone(theValue, hasMillis); setTimeZone(theValue, hasMillis);
setPrecision(TemporalPrecisionEnum.SECOND); setPrecision(TemporalPrecisionEnum.SECOND);
} }
@ -266,27 +310,29 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
} }
} }
private void setTimeZone(String theValueString, boolean hasMillis) { private boolean hasOffset(String theValue) {
clearTimeZone(); boolean inTime = false;
int timeZoneStart = 19; for (int i = 0; i < theValue.length(); i++) {
if(hasMillis) timeZoneStart += 4; switch (theValue.charAt(i)) {
if (theValueString.endsWith("Z")) { case 'T':
setTimeZoneZulu(true); inTime = true;
} else if (theValueString.indexOf("GMT", timeZoneStart) != -1) { break;
setTimeZone(TimeZone.getTimeZone(theValueString.substring(timeZoneStart))); case '+':
} else if (theValueString.indexOf('+', timeZoneStart) != -1 || theValueString.indexOf('-', timeZoneStart) != -1) { case '-':
setTimeZone(TimeZone.getTimeZone("GMT"+theValueString.substring(timeZoneStart))); if (inTime) {
return true;
}
break;
}
} }
} return false;
private void clearTimeZone() {
myTimeZone = null;
myTimeZoneZulu = false;
} }
/** /**
* To be implemented by subclasses to indicate whether the given precision is allowed by this type * For unit tests only
*/ */
abstract boolean isPrecisionAllowed(TemporalPrecisionEnum thePrecision); static List<FastDateFormat> getFormatters() {
return ourFormatters;
}
} }

View File

@ -2,7 +2,11 @@ package ca.uhn.fhir.model.primitive;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone; import java.util.TimeZone;
import org.apache.commons.lang3.time.FastDateFormat; import org.apache.commons.lang3.time.FastDateFormat;
@ -15,13 +19,32 @@ import ca.uhn.fhir.parser.DataFormatException;
public class BaseDateTimeDtTest { public class BaseDateTimeDtTest {
private SimpleDateFormat myDateInstantParser; private SimpleDateFormat myDateInstantParser;
private FastDateFormat myDateInstantZoneParser; private FastDateFormat myDateInstantZoneParser;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseDateTimeDtTest.class);
@Before @Before
public void before() { public void before() {
myDateInstantParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 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")); myDateInstantZoneParser = FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss.SSSZ", TimeZone.getTimeZone("GMT-02:00"));
} }
@Test
public void testFormats() throws Exception {
Date instant = myDateInstantParser.parse("2001-02-03 13:01:02.555");
for (FastDateFormat next : BaseDateTimeDt.getFormatters()) {
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("EST"));
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 @Test
public void testParseYear() throws DataFormatException { public void testParseYear() throws DataFormatException {
DateTimeDt dt = new DateTimeDt(); DateTimeDt dt = new DateTimeDt();