Commit b8a123f9 authored by R.W.Majeed's avatar R.W.Majeed

allow default local timezone for parsing local ISO timestamps

parent de135289
......@@ -27,7 +27,6 @@ import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
......@@ -39,7 +38,6 @@ import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.time.temporal.UnsupportedTemporalTypeException;
import java.util.Date;
import java.util.Objects;
......@@ -259,19 +257,37 @@ public class DateTimeAccuracy implements Temporal, Comparable<DateTimeAccuracy>
return b.toString();
}
/**
* Parses a partial ISO 8601 date time string.
* [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hhmm]
* <p>
* At least the year must be specified. All other fields can be left out.
*
* If no zone offset is specified, UTC is assumed.
* </p>
* @param str ISO 8601 string
* If {@code null} is specified, no offset adjustments are done, with same result as UTC
* @return date time with accuracy as derived from parse
* @throws ParseException for unparsable string
* @throws IllegalArgumentException unparsable string (old unchecked exception)
*/
public static DateTimeAccuracy parsePartialIso8601(String str)throws ParseException{
return parsePartialIso8601(str, null);
}
/**
* Parses a partial ISO 8601 date time string.
* [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hhmm]
* <p>
* At least the year must be specified. All other fields can be left out.
* </p>
* @param str ISO 8601 string
* @param localZone time zone to use for offset if no offset is specified.
* If {@code null} is specified, no offset adjustments are done, with same result as UTC
* @return date time with accuracy as derived from parse
* @throws ParseException for unparsable string
* @throws IllegalArgumentException unparsable string (old unchecked exception)
*/
public static DateTimeAccuracy parsePartialIso8601(String str, ZoneId localZone)throws ParseException{
ParsePosition pos = new ParsePosition(0);
TemporalAccessor a = PARTIAL_FORMATTER.parseUnresolved(str, pos);
// first check that everything was parsed
......@@ -315,6 +331,9 @@ public class DateTimeAccuracy implements Temporal, Comparable<DateTimeAccuracy>
off = ZoneOffset.ofTotalSeconds(a.get(ChronoField.OFFSET_SECONDS));
// adjust to UTC
dateTime = dateTime.atOffset(off).withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime();
}else if( localZone != null ){
// use specified local zone
dateTime = dateTime.atZone(localZone).toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime();
}
DateTimeAccuracy me = new DateTimeAccuracy(dateTime);
me.accuracy = accuracy;
......
package de.sekmi.histream.xml;
import java.text.ParseException;
import java.time.ZoneId;
import javax.xml.bind.annotation.adapters.XmlAdapter;
......@@ -13,17 +14,31 @@ import de.sekmi.histream.DateTimeAccuracy;
*
*/
public class DateTimeAccuracyAdapter extends XmlAdapter<String, DateTimeAccuracy>{
private ZoneId zoneId;
/**
* Specify a local time zone. If non-{@code null}, the result of {@link #marshal(DateTimeAccuracy)}
* will always include the offset of the specified zone.
* When unmarshalling a string without offset, then the timestamp is treated as if it was
* in the specified zone id.
* @param zoneId local zone id to use as default for both {@link #marshal(DateTimeAccuracy)} and {@link #unmarshal(String)}
*/
public void setZoneId(ZoneId zoneId){
this.zoneId = zoneId;
}
@Override
public DateTimeAccuracy unmarshal(String v) throws ParseException {
if( v == null )return null;
// parsing will support any zone offset
// TODO if zone is missing, assume specified zone
return DateTimeAccuracy.parsePartialIso8601(v);
}
@Override
public String marshal(DateTimeAccuracy v) {
if( v == null )return null;
return v.toPartialIso8601();
return v.toPartialIso8601(zoneId);
}
}
......@@ -14,6 +14,8 @@ import org.junit.Assert;
import static org.junit.Assert.*;
import org.junit.Test;
import de.sekmi.histream.xml.DateTimeAccuracyAdapter;
public class TestDateTimeAccuracy {
@Test
......@@ -153,5 +155,11 @@ public class TestDateTimeAccuracy {
}catch( DateTimeParseException e ){
}
}
@Test
public void verifyParsingWithLocalZone() throws ParseException{
DateTimeAccuracy a = DateTimeAccuracy.parsePartialIso8601("2001-02-03T04", ZoneId.of("Asia/Shanghai"));
// date should be treated as if it had a +08:00 offset
assertEquals("2001-02-02T20Z", a.toPartialIso8601(ZoneId.of("UTC")));
}
// TODO: further tests
}
package de.sekmi.histream.xml;
import org.junit.Test;
import static org.junit.Assert.*;
import java.text.ParseException;
import java.time.ZoneId;
public class TestDateTimeAccuracyAdapter {
@Test
public void verifyParseAndFormat() throws ParseException{
DateTimeAccuracyAdapter a = new DateTimeAccuracyAdapter();
String[] testValues = new String[]{"2001","2001-02-03","2001-02-03T04"};
for( int i=0; i<testValues.length; i++ ){
assertEquals(testValues[i],a.marshal(a.unmarshal(testValues[i])));
}
}
@Test
public void verifyParseAndFormatWithZoneOffset() throws ParseException{
DateTimeAccuracyAdapter a = new DateTimeAccuracyAdapter();
a.setZoneId(ZoneId.of("Asia/Shanghai"));
String[] testValues = new String[]{"2001","2001-02-03","2001-02-03T04+0800"};
for( int i=0; i<testValues.length; i++ ){
assertEquals(testValues[i],a.marshal(a.unmarshal(testValues[i])));
}
a.setZoneId(ZoneId.of("UTC"));
testValues = new String[]{"2001","2001-02-03","2001-02-03T04Z"};
for( int i=0; i<testValues.length; i++ ){
assertEquals(testValues[i],a.marshal(a.unmarshal(testValues[i])));
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment