Fix #444 - Correct handling of parsing milliseconds in dates before 1970
This commit is contained in:
parent
ed7e6929a7
commit
a2ffc6af05
|
@ -449,7 +449,12 @@ public abstract class BaseDateTimeDt extends BasePrimitive<Date> {
|
|||
myPrecision = thePrecision;
|
||||
myFractionalSeconds = "";
|
||||
if (theValue != null) {
|
||||
String fractionalSeconds = Integer.toString((int) (theValue.getTime() % 1000));
|
||||
long millis = theValue.getTime() % 1000;
|
||||
if (millis < 0) {
|
||||
// This is for times before 1970 (see bug #444)
|
||||
millis = 1000 + millis;
|
||||
}
|
||||
String fractionalSeconds = Integer.toString((int) millis);
|
||||
myFractionalSeconds = StringUtils.leftPad(fractionalSeconds, 3, '0');
|
||||
}
|
||||
super.setValue(theValue);
|
||||
|
|
|
@ -3,22 +3,21 @@ package ca.uhn.fhir.model.primitive;
|
|||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.either;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
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.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
|
@ -35,6 +34,33 @@ public class BaseDateTimeDtDstu2Test {
|
|||
private SimpleDateFormat myDateInstantParser;
|
||||
private FastDateFormat myDateInstantZoneParser;
|
||||
|
||||
/**
|
||||
* See #444
|
||||
*/
|
||||
@Test
|
||||
public void testParseAndEncodeDateBefore1970() {
|
||||
LocalDateTime ldt = LocalDateTime.of(1960, 9, 7, 0, 44, 25, 12387401);
|
||||
Date from = Date.from(ldt.toInstant(ZoneOffset.UTC));
|
||||
InstantDt type = (InstantDt) new InstantDt(from).setTimeZoneZulu(true);
|
||||
String encoded = type.getValueAsString();
|
||||
|
||||
ourLog.info("LDT: "+ ldt.toString());
|
||||
ourLog.info("Expected: "+"1960-09-07T00:44:25.012");
|
||||
ourLog.info("Actual: "+encoded);
|
||||
|
||||
assertEquals("1960-09-07T00:44:25.012Z", encoded);
|
||||
|
||||
type = new InstantDt(encoded);
|
||||
assertEquals(1960, type.getYear().intValue());
|
||||
assertEquals(8, type.getMonth().intValue()); // 0-indexed unlike LocalDateTime.of
|
||||
assertEquals(7, type.getDay().intValue());
|
||||
assertEquals(0, type.getHour().intValue());
|
||||
assertEquals(44, type.getMinute().intValue());
|
||||
assertEquals(25, type.getSecond().intValue());
|
||||
assertEquals(12, type.getMillis().intValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFromTime() {
|
||||
long millis;
|
||||
|
|
|
@ -429,7 +429,12 @@ public abstract class BaseDateTimeType extends PrimitiveType<Date> {
|
|||
myPrecision = thePrecision;
|
||||
myFractionalSeconds = "";
|
||||
if (theValue != null) {
|
||||
String fractionalSeconds = Integer.toString((int) (theValue.getTime() % 1000));
|
||||
long millis = theValue.getTime() % 1000;
|
||||
if (millis < 0) {
|
||||
// This is for times before 1970 (see bug #444)
|
||||
millis = 1000 + millis;
|
||||
}
|
||||
String fractionalSeconds = Integer.toString((int) millis);
|
||||
myFractionalSeconds = StringUtils.leftPad(fractionalSeconds, 3, '0');
|
||||
}
|
||||
super.setValue(theValue);
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.io.IOException;
|
|||
import java.io.StringReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -103,7 +105,7 @@ public class XmlParserDstu3Test {
|
|||
parent = (Organization) child.getPartOf().getResource();
|
||||
assertEquals("parent", parent.getName());
|
||||
|
||||
gp = (Organization)parent.getPartOf().getResource();
|
||||
gp = (Organization) parent.getPartOf().getResource();
|
||||
assertEquals("grandparent", gp.getName());
|
||||
}
|
||||
|
||||
|
@ -127,7 +129,7 @@ public class XmlParserDstu3Test {
|
|||
assertEquals("#3", performer.getId());
|
||||
}
|
||||
|
||||
@Test(expected=DataFormatException.class)
|
||||
@Test(expected = DataFormatException.class)
|
||||
public void testContainedResourceWithNoId() throws IOException {
|
||||
String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_contained_with_no_id.xml"), StandardCharsets.UTF_8);
|
||||
|
||||
|
@ -136,7 +138,6 @@ public class XmlParserDstu3Test {
|
|||
parser.parseResource(Bundle.class, string);
|
||||
}
|
||||
|
||||
|
||||
@Test()
|
||||
public void testContainedResourceWithNoIdLenient() throws IOException {
|
||||
String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_contained_with_no_id.xml"), StandardCharsets.UTF_8);
|
||||
|
@ -146,7 +147,7 @@ public class XmlParserDstu3Test {
|
|||
parser.parseResource(Bundle.class, string);
|
||||
}
|
||||
|
||||
@Test(expected=DataFormatException.class)
|
||||
@Test(expected = DataFormatException.class)
|
||||
public void testParseWithInvalidLocalRef() throws IOException {
|
||||
String string = IOUtils.toString(getClass().getResourceAsStream("/bundle_with_invalid_contained_ref.xml"), StandardCharsets.UTF_8);
|
||||
|
||||
|
@ -409,7 +410,6 @@ public class XmlParserDstu3Test {
|
|||
ourCtx = null;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseContainedNonCustomTypes() {
|
||||
ourCtx = FhirContext.forDstu3();
|
||||
|
@ -1435,12 +1435,12 @@ public class XmlParserDstu3Test {
|
|||
ourLog.info(enc);
|
||||
assertThat(enc, containsString("<reference value=\"http://foo.com/Organization/2\"/>"));
|
||||
|
||||
parser.setDontStripVersionsFromReferencesAtPaths((String[])null);
|
||||
parser.setDontStripVersionsFromReferencesAtPaths((String[]) null);
|
||||
enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
|
||||
ourLog.info(enc);
|
||||
assertThat(enc, containsString("<reference value=\"http://foo.com/Organization/2\"/>"));
|
||||
|
||||
parser.setDontStripVersionsFromReferencesAtPaths((List<String>)null);
|
||||
parser.setDontStripVersionsFromReferencesAtPaths((List<String>) null);
|
||||
enc = parser.setPrettyPrint(true).encodeResourceToString(auditEvent);
|
||||
ourLog.info(enc);
|
||||
assertThat(enc, containsString("<reference value=\"http://foo.com/Organization/2\"/>"));
|
||||
|
|
|
@ -68,6 +68,62 @@ public class CreateDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithIncorrectContent1() throws Exception {
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/xml+fhir; charset=utf-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertThat(responseContent, containsString("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\""));
|
||||
assertThat(responseContent, containsString("Failed to parse request body as XML resource."));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithIncorrectContent2() throws Exception {
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+xml; charset=utf-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertThat(responseContent, containsString("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\""));
|
||||
assertThat(responseContent, containsString("Failed to parse request body as XML resource."));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateWithIncorrectContent3() throws Exception {
|
||||
|
||||
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||
httpPost.setEntity(new StringEntity("{\"foo\":\"bar\"}", ContentType.parse("application/fhir+json; charset=utf-8")));
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertThat(responseContent, containsString("Failed to parse request body as JSON resource."));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch() throws Exception {
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@ import static org.junit.Assert.assertTrue;
|
|||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
@ -19,6 +21,7 @@ import java.util.TimeZone;
|
|||
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
|
@ -27,6 +30,7 @@ import org.junit.Test;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
|
||||
|
@ -54,6 +58,7 @@ public class BaseDateTimeTypeDstu3Test {
|
|||
/**
|
||||
* Test for #57
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void testConstructorRejectsInvalidPrecision() {
|
||||
try {
|
||||
|
@ -283,8 +288,6 @@ public class BaseDateTimeTypeDstu3Test {
|
|||
assertEquals("1990-01-03T02:22Z", date.getValueAsString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testNewInstance() throws InterruptedException {
|
||||
InstantType now = InstantType.withCurrentTime();
|
||||
|
@ -293,6 +296,35 @@ public class BaseDateTimeTypeDstu3Test {
|
|||
assertTrue(now.getValue().before(then.getValue()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* See #444
|
||||
*/
|
||||
@Test
|
||||
public void testParseAndEncodeDateBefore1970() {
|
||||
LocalDateTime ldt = LocalDateTime.of(1960, 9, 7, 0, 44, 25, 12387401);
|
||||
Date from = Date.from(ldt.toInstant(ZoneOffset.UTC));
|
||||
InstantType type = (InstantType) new InstantType(from).setTimeZoneZulu(true);
|
||||
String encoded = type.asStringValue();
|
||||
|
||||
ourLog.info("LDT: "+ ldt.toString());
|
||||
ourLog.info("Expected: "+"1960-09-07T00:44:25.012");
|
||||
ourLog.info("Actual: "+encoded);
|
||||
|
||||
assertEquals("1960-09-07T00:44:25.012Z", encoded);
|
||||
|
||||
type = new InstantType(encoded);
|
||||
assertEquals(1960, type.getYear().intValue());
|
||||
assertEquals(8, type.getMonth().intValue()); // 0-indexed unlike LocalDateTime.of
|
||||
assertEquals(7, type.getDay().intValue());
|
||||
assertEquals(0, type.getHour().intValue());
|
||||
assertEquals(44, type.getMinute().intValue());
|
||||
assertEquals(25, type.getSecond().intValue());
|
||||
assertEquals(12, type.getMillis().intValue());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseDate() {
|
||||
new DateType("2012-03-31");
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
Remove Maven dependency on Saxon library, as it is not actually used. Thanks
|
||||
to Lem Edmondson for the suggestion!
|
||||
</action>
|
||||
<action type="fix" issue="444">
|
||||
Times before 1970 with fractional milliseconds were parsed incorrectly. Thanks
|
||||
to GitHub user @CarthageKing for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.0" date="2016-08-30">
|
||||
<action type="fix">
|
||||
|
|
Loading…
Reference in New Issue