Skip to content

Commit

Permalink
#229:fix daylight savings issue
Browse files Browse the repository at this point in the history
  • Loading branch information
ohr committed Jul 28, 2015
1 parent 0449679 commit 682122e
Show file tree
Hide file tree
Showing 7 changed files with 525 additions and 54 deletions.
54 changes: 12 additions & 42 deletions hapi-base/src/main/java/ca/uhn/hl7v2/model/primitive/CommonTM.java
Original file line number Diff line number Diff line change
Expand Up @@ -630,53 +630,23 @@ public void setValueToSecond(Date theDate) throws DataTypeException {
* @since 1.1
*/
public Calendar getValueAsCalendar() {
Calendar retVal = new GregorianCalendar();
int gmtOff = getGMTOffset();
Calendar retVal;
if (gmtOff != GMT_OFFSET_NOT_SET_VALUE && !omitOffsetFg) {
int hrOffset = gmtOff / 100;
int minOffset = Math.abs(gmtOff % 100);
String timeZone = String.format("GMT%+d:%02d", hrOffset, minOffset);
retVal = new GregorianCalendar(TimeZone.getTimeZone(timeZone));
} else {
retVal = Calendar.getInstance();
}

retVal.set(Calendar.HOUR_OF_DAY, getHour());
retVal.set(Calendar.MINUTE, getMinute());
retVal.set(Calendar.SECOND, getSecond());

float fractSecond = getFractSecond();
retVal.set(Calendar.MILLISECOND, (int) Math.round(fractSecond * 1000.0));

int gmtOff = getGMTOffset();
if (gmtOff != GMT_OFFSET_NOT_SET_VALUE && !omitOffsetFg) {
retVal.set(Calendar.ZONE_OFFSET, (gmtOff/100) * (1000 * 60 * 60));

/*
* The following sets the TimeZone associated with the returned calendar
* to use the offset specified in the value if this conflicts with the
* value it already contains.
*
* This is needed in situations where daylight savings is in effect
* during part of the year, and a date is parsed which contains the
* other part of the year (i.e. parsing a DST DateTime when it is not actually
* DST according to the system clock).
*
* See CommonTSTest#testGetCalendarRespectsDaylightSavings() for an example
* which fails if this is removed.
*/
if (retVal.getTimeZone().getRawOffset() != retVal.get(Calendar.ZONE_OFFSET)) {
int hrOffset = gmtOff / 100;
int minOffset = gmtOff % 100;
StringBuilder tzBuilder = new StringBuilder("GMT");

if (hrOffset < 0) {
tzBuilder.append('-');
} else {
tzBuilder.append('+');
}
tzBuilder.append(Math.abs(hrOffset));
tzBuilder.append(':');
if (minOffset < 10) {
tzBuilder.append('0');
}
tzBuilder.append(minOffset);

retVal.setTimeZone(TimeZone.getTimeZone(tzBuilder.toString()));
}

}


return retVal;
}

Expand Down
20 changes: 10 additions & 10 deletions hapi-base/src/main/java/ca/uhn/hl7v2/model/primitive/CommonTS.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@

package ca.uhn.hl7v2.model.primitive;

import ca.uhn.hl7v2.model.DataTypeException;
import ca.uhn.hl7v2.model.DataTypeUtil;

import java.io.Serializable;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.io.Serializable;

import ca.uhn.hl7v2.model.DataTypeException;
import ca.uhn.hl7v2.model.DataTypeUtil;

/**
* <p>
Expand Down Expand Up @@ -499,7 +499,7 @@ public void setValue(String val) throws DataTypeException {
if (sm != -1)
indexOfSign = sm;

if (offsetExists == false) {
if (!offsetExists) {
if (val.length() <= 8) {
dateVal = val;
}
Expand All @@ -511,7 +511,7 @@ public void setValue(String val) throws DataTypeException {
}
} //offset not exist

if (offsetExists == true) {
if (offsetExists) {
if (indexOfSign > 8) {
dateVal = val.substring(0, 8);
timeVal = val.substring(8);
Expand All @@ -529,9 +529,9 @@ public void setValue(String val) throws DataTypeException {
dt = new CommonDT();
//set the value of the date object to the input date value
dt.setValue(dateVal);
//if the offset does not exist and a timvalue does not exist then
//if the offset does not exist and a timevalue does not exist then
//we must provide a default offset = to the local time zone
if (timeVal == null && offsetExists == false) {
if (timeVal == null && !offsetExists) {
// int defaultOffset = DataTypeUtil.getLocalGMTOffset();
tm = new CommonTM();
//tm.setOffset(defaultOffset);
Expand All @@ -540,7 +540,7 @@ public void setValue(String val) throws DataTypeException {

//if we have a time value then make a new time object and set it to the
//input time value (as long as the time val has time + offset or just time only)
if (timeVal != null && timeValIsOffsetOnly == false) {
if (timeVal != null && !timeValIsOffsetOnly) {
// must make sure that the time component contains both hours
// at the very least -- must be at least 2 chars in length.
// Note: this changed as of v2.5, before hours AND minutes were required.
Expand All @@ -558,7 +558,7 @@ public void setValue(String val) throws DataTypeException {

//if we have a time value and it only has the offset then make a new
//time object and set the offset value to the input value
if (timeVal != null && timeValIsOffsetOnly == true) {
if (timeVal != null && timeValIsOffsetOnly) {
//we know that the time value is just the offset so we
//must check to see if it is the right length before setting the
//offset field in the tm object
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package ca.uhn.hl7v2.model.primitive;

import ca.uhn.hl7v2.model.DataTypeException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

@RunWith(Parameterized.class)
public class CommonTSGetCalendarAtlanticAzoresTest {
private static TimeZone defaultTZ;

@BeforeClass
public static void setUpBeforeClass() {
defaultTZ = TimeZone.getDefault();
TimeZone.setDefault(TimeZone.getTimeZone("Atlantic/Azores"));
}

@AfterClass
public static void tearDownAfterClass() {
TimeZone.setDefault(defaultTZ);
}

@Parameters(name = "{0},{1}")
public static Collection<Object[]> params() {
List<String> tzs = new ArrayList<String>();
{
// west to east
tzs.add("Canada/Eastern");
tzs.add("GMT-1");
tzs.add("UTC");
tzs.add("GMT");
tzs.add("Europe/London");
tzs.add("GMT+1");
tzs.add("CET");
tzs.add("Europe/Copenhagen");
tzs.add("GMT+2");
}
List<String> instants = new ArrayList<String>();
{
instants.add("2015010112");
instants.add("2015060112");
}

List<Object[]> params = new ArrayList<Object[]>();
for (String tz : tzs) {
for (String i : instants) {
params.add(new Object[]{tz, i});
}
}
return params;
}


@Rule
public ErrorCollector collector = new ErrorCollector();

SimpleDateFormat dfm = new SimpleDateFormat("yyyyMMddHH");
CommonTS cts = new CommonTS();
GregorianCalendar expected;
final TimeZone tz;

public CommonTSGetCalendarAtlanticAzoresTest(
String tz, String instant) throws ParseException {
this.tz = TimeZone.getTimeZone(tz);

this.expected = new GregorianCalendar(this.tz);
dfm.setTimeZone(this.tz);
Date parse = dfm.parse(instant);
expected.setTime(parse);
}

@Test
public void calendar() throws DataTypeException {
cts.setValue(expected);
GregorianCalendar actual = (GregorianCalendar) cts.getValueAsCalendar();

compareCalendar(actual);
}

@Test
public void calendarWithDefaultChanged() throws DataTypeException {
TimeZone dfTz = TimeZone.getDefault();
TimeZone.setDefault(tz);
try {
cts.setValue(expected);
GregorianCalendar actual =
(GregorianCalendar) cts.getValueAsCalendar();

compareCalendar(actual);
} finally {
TimeZone.setDefault(dfTz);
}
}

@Test
public void date() throws DataTypeException {
cts.setValue(expected);
Date actual = cts.getValueAsDate();

assertThat(actual, equalTo(expected.getTime()));
}

@Test
public void constructCalendarManuallyUsingGMTOffsetWithCommonTMGetters() throws DataTypeException {
cts.setValue(expected);

int offset = cts.getGMTOffset() / 100;
final TimeZone timeZone;
if (offset >= 0) {
timeZone = TimeZone.getTimeZone("GMT+" + offset);
} else {
timeZone = TimeZone.getTimeZone("GMT" + offset);
}
GregorianCalendar actual = new GregorianCalendar(timeZone);
actual.set(Calendar.YEAR, cts.getYear());
actual.set(Calendar.MONTH, cts.getMonth() - 1);
actual.set(Calendar.DATE, cts.getDay());
actual.set(Calendar.HOUR_OF_DAY, cts.getHour());
actual.set(Calendar.MINUTE, cts.getMinute());
actual.set(Calendar.SECOND, cts.getSecond());

float fractSecond = cts.getFractSecond();
actual.set(Calendar.MILLISECOND, (int) Math.round(fractSecond * 1000.0));

compareCalendar(actual);
}

@Test
public void constructCalendarManuallyUsingUTCWithCommonTMGetters() throws DataTypeException {
cts.setValue(expected);

TimeZone timeZone = TimeZone.getTimeZone("UTC");
GregorianCalendar actual = new GregorianCalendar(timeZone);
actual.set(Calendar.YEAR, cts.getYear());
actual.set(Calendar.MONTH, cts.getMonth() - 1);
actual.set(Calendar.DATE, cts.getDay());
actual.set(Calendar.HOUR_OF_DAY, cts.getHour());
actual.set(Calendar.MINUTE, cts.getMinute());
actual.set(Calendar.SECOND, cts.getSecond());

float fractSecond = cts.getFractSecond();
actual.set(Calendar.MILLISECOND, (int) Math.round(fractSecond * 1000.0));
int offset = cts.getGMTOffset() / -100;
actual.add(Calendar.HOUR, offset);
actual.getTime();

compareCalendar(actual);
}


private void compareCalendar(GregorianCalendar actual) {
collector.checkThat(actual.getTime(), equalTo(expected.getTime()));
}
}
Loading

0 comments on commit 682122e

Please sign in to comment.