From e2d59ef5ba05918502bc053f450bfc82bc3c4605 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 14 Nov 2014 14:36:26 -0500 Subject: [PATCH] Add unit test for BaseDateTimeDt --- .../fhir/model/primitive/BaseDateTimeDt.java | 132 ++++++++++++------ .../model/primitive/BaseDateTimeDtTest.java | 25 +++- 2 files changed, 113 insertions(+), 44 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java index d2a9b7bf701..0061be14205 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java @@ -23,9 +23,12 @@ package ca.uhn.fhir.model.primitive; import static ca.uhn.fhir.model.api.TemporalPrecisionEnum.*; import java.text.ParseException; +import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; +import java.util.List; import java.util.TimeZone; import java.util.regex.Pattern; @@ -39,6 +42,10 @@ import ca.uhn.fhir.parser.DataFormatException; public abstract class BaseDateTimeDt extends BasePrimitive { + /* + * Add any new formatters to the static block below!! + */ + private static final List 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"); @@ -47,23 +54,43 @@ public abstract class BaseDateTimeDt extends BasePrimitive { 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 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 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 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 formatters = new ArrayList(); + 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 TimeZone myTimeZone; private boolean myTimeZoneZulu = false; 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}. - * Default is {@link Calendar#DAY_OF_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} * * @see #setPrecision(int) */ @@ -121,6 +148,11 @@ public abstract class BaseDateTimeDt extends BasePrimitive { } } + /** + * To be implemented by subclasses to indicate whether the given precision is allowed by this type + */ + abstract boolean isPrecisionAllowed(TemporalPrecisionEnum thePrecision); + public boolean isTimeZoneZulu() { return myTimeZoneZulu; } @@ -154,6 +186,20 @@ public abstract class BaseDateTimeDt extends BasePrimitive { 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) { myTimeZone = theTimeZone; } @@ -217,7 +263,7 @@ public abstract class BaseDateTimeDt extends BasePrimitive { } else { 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); boolean hasMillis = dotIndex > -1; @@ -227,34 +273,32 @@ public abstract class BaseDateTimeDt extends BasePrimitive { throw new DataFormatException("Invalid date/time string (data type " + getClass().getSimpleName() + " does not support MILLIS precision):" + theValue); } - if(hasMillis){ + if (hasMillis) { try { - myValue = ourYearMonthDayTimeMilliZoneFormat.parse(theValue); - } catch (ParseException p){ - try{ - if(theValue.endsWith("Z")) - myValue = ourYearMonthDayTimeMilliUTCZFormat.parse(theValue); - else - myValue = ourYearMonthDayTimeMilliFormat.parse(theValue); - }catch(ParseException p2){ - throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue); - } + if (hasOffset(theValue)) { + myValue = ourYearMonthDayTimeMilliZoneFormat.parse(theValue); + } else if (theValue.endsWith("Z")) + myValue = ourYearMonthDayTimeMilliUTCZFormat.parse(theValue); + else + myValue = ourYearMonthDayTimeMilliFormat.parse(theValue); + } catch (ParseException p2) { + throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue); } setTimeZone(theValue, hasMillis); setPrecision(TemporalPrecisionEnum.MILLI); - }else{ - try{ - myValue = ourYearMonthDayTimeZoneFormat.parse(theValue); - }catch(ParseException p){ - try{ - if(theValue.endsWith("Z")) - myValue = ourYearMonthDayTimeUTCZFormat.parse(theValue); - else - myValue = ourYearMonthDayTimeFormat.parse(theValue); - }catch(ParseException p2){ - throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue); + } else { + try { + if (hasOffset(theValue)) { + myValue = ourYearMonthDayTimeZoneFormat.parse(theValue); + } else if (theValue.endsWith("Z")) { + myValue = ourYearMonthDayTimeUTCZFormat.parse(theValue); + } else { + myValue = ourYearMonthDayTimeFormat.parse(theValue); } + } catch (ParseException p2) { + throw new DataFormatException("Invalid data/time string (" + p2.getMessage() + "): " + theValue); } + setTimeZone(theValue, hasMillis); setPrecision(TemporalPrecisionEnum.SECOND); } @@ -266,27 +310,29 @@ public abstract class BaseDateTimeDt extends BasePrimitive { } } - 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))); + private boolean hasOffset(String theValue) { + boolean inTime = false; + for (int i = 0; i < theValue.length(); i++) { + switch (theValue.charAt(i)) { + case 'T': + inTime = true; + break; + case '+': + case '-': + if (inTime) { + return true; + } + break; + } } - } - - private void clearTimeZone() { - myTimeZone = null; - myTimeZoneZulu = false; + return 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 getFormatters() { + return ourFormatters; + } } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java index 37c4b5f037b..a3c60fabb07 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java @@ -2,7 +2,11 @@ package ca.uhn.fhir.model.primitive; import static org.junit.Assert.*; +import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; import java.util.TimeZone; import org.apache.commons.lang3.time.FastDateFormat; @@ -15,13 +19,32 @@ import ca.uhn.fhir.parser.DataFormatException; public class BaseDateTimeDtTest { private SimpleDateFormat myDateInstantParser; private FastDateFormat myDateInstantZoneParser; - + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseDateTimeDtTest.class); + @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 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 public void testParseYear() throws DataFormatException { DateTimeDt dt = new DateTimeDt();