Skip to content

Commit 87824f1

Browse files
Deal with xml dateTime
1 parent c5f6b65 commit 87824f1

File tree

5 files changed

+165
-3
lines changed

5 files changed

+165
-3
lines changed

bindings.xjb

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
<jxb:bindings>
99
<jxb:globalBindings underscoreBinding="asCharInWord">
10-
<xjc:javaType name="java.time.LocalDateTime" xmlType="xs:dateTime" adapter="org.rutebanken.util.LocalDateTimeISO8601XmlAdapter" />
11-
<xjc:javaType name="java.time.ZonedDateTime" xmlType="xs:dateTime" adapter="org.rutebanken.util.ZonedDateTimeISO8601XmlAdapter" />
10+
<xjc:javaType name="org.rutebanken.time.XmlDateTime" xmlType="xs:dateTime" adapter="org.rutebanken.util.XmlDateTimeAdapter" />
11+
<!--<xjc:javaType name="java.time.ZonedDateTime" xmlType="xs:dateTime" adapter="org.rutebanken.util.ZonedDateTimeISO8601XmlAdapter" />-->
1212
<xjc:javaType name="java.time.LocalTime" xmlType="xs:time" adapter="org.rutebanken.util.LocalTimeISO8601XmlAdapter" />
1313
<xjc:javaType name="java.time.LocalDateTime" xmlType="xs:date" adapter="org.rutebanken.util.LocalDateXmlAdapter" />
1414
<xjc:javaType name="java.time.Duration" xmlType="xs:duration" adapter="org.rutebanken.util.DurationXmlAdapter" />

pom.xml

+8-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@
7474

7575
<!-- Unit test frameworks versions -->
7676
<assertj.core.version>3.22.0</assertj.core.version>
77-
<junit.version>5.8.2</junit.version>
77+
<junit.version>5.11.1</junit.version>
7878

7979
<!-- Maven plugin versions -->
8080
<nexus-staging-maven-plugin.version>1.6.13</nexus-staging-maven-plugin.version>
@@ -163,6 +163,12 @@
163163
<version>${junit.version}</version>
164164
<scope>test</scope>
165165
</dependency>
166+
<dependency>
167+
<groupId>org.junit.jupiter</groupId>
168+
<artifactId>junit-jupiter</artifactId>
169+
<version>5.11.1</version>
170+
<scope>test</scope>
171+
</dependency>
166172
<dependency>
167173
<groupId>org.assertj</groupId>
168174
<artifactId>assertj-core</artifactId>
@@ -175,6 +181,7 @@
175181
<version>${slf4j.version}</version>
176182
<scope>test</scope>
177183
</dependency>
184+
178185
</dependencies>
179186

180187
<build>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package org.rutebanken.time;
2+
3+
import java.time.LocalDateTime;
4+
import java.time.ZoneId;
5+
import java.time.ZonedDateTime;
6+
import java.util.Objects;
7+
8+
/**
9+
* A wrapper type to represent the fact that xml xml dateTime can both include local and zoned
10+
* date times.
11+
*/
12+
public class XmlDateTime {
13+
14+
private ZonedDateTime zonedDateTime;
15+
private LocalDateTime localDateTime;
16+
17+
public XmlDateTime(ZonedDateTime zonedDateTime) {
18+
this.zonedDateTime = Objects.requireNonNull(zonedDateTime);
19+
}
20+
21+
public XmlDateTime(LocalDateTime localDateTime) {
22+
this.localDateTime = Objects.requireNonNull(localDateTime);
23+
}
24+
25+
public ZonedDateTime atZone(ZoneId zoneId) {
26+
return zonedDateTime != null ? zonedDateTime.withZoneSameInstant(zoneId) : localDateTime.atZone(zoneId);
27+
}
28+
29+
/**
30+
* Returns true if the date time included a zone id (time zone).
31+
*/
32+
public boolean isZoned() {
33+
return zonedDateTime != null;
34+
}
35+
36+
/**
37+
* Returns true if the date time did no include a zone id (time zone).
38+
*/
39+
public boolean isLocal() {
40+
return localDateTime != null;
41+
}
42+
43+
@Override
44+
public String toString() {
45+
if(zonedDateTime != null) {
46+
return zonedDateTime.toString();
47+
}
48+
else {
49+
return localDateTime.toString();
50+
}
51+
}
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
3+
* the European Commission - subsequent versions of the EUPL (the "Licence");
4+
* You may not use this work except in compliance with the Licence.
5+
* You may obtain a copy of the Licence at:
6+
*
7+
* https://joinup.ec.europa.eu/software/page/eupl
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the Licence is distributed on an "AS IS" basis,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the Licence for the specific language governing permissions and
13+
* limitations under the Licence.
14+
*/
15+
16+
package org.rutebanken.util;
17+
18+
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
19+
import java.time.LocalDateTime;
20+
import java.time.ZoneId;
21+
import java.time.ZonedDateTime;
22+
import java.time.format.DateTimeFormatter;
23+
import java.time.format.DateTimeFormatterBuilder;
24+
import java.time.temporal.ChronoField;
25+
import org.rutebanken.time.XmlDateTime;
26+
27+
public class XmlDateTimeAdapter extends XmlAdapter<String, XmlDateTime> {
28+
29+
public static final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
30+
.parseCaseInsensitive()
31+
.parseLenient()
32+
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
33+
.optionalStart()
34+
.parseStrict()
35+
.appendOffset("+HH:MM:ss", "Z")
36+
.parseLenient()
37+
.optionalEnd()
38+
.optionalStart()
39+
.appendOffset("+HHmmss", "Z")
40+
.optionalEnd()
41+
.toFormatter();
42+
43+
@Override
44+
public XmlDateTime unmarshal(String inputDate) {
45+
var temporal = formatter.parse(inputDate);
46+
if(temporal.isSupported(ChronoField.OFFSET_SECONDS)) {
47+
var zdt = ZonedDateTime.from(temporal);
48+
return new XmlDateTime(zdt);
49+
}
50+
else {
51+
var ldt = LocalDateTime.from(temporal);
52+
return new XmlDateTime(ldt);
53+
}
54+
}
55+
56+
@Override
57+
public String marshal(XmlDateTime inputDate) {
58+
if(inputDate != null) {
59+
return formatter.format(inputDate.atZone(ZoneId.of("UTC")));
60+
} else {
61+
return null;
62+
}
63+
}
64+
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package org.rutebanken.util;
2+
3+
import static org.junit.jupiter.api.Assertions.assertTrue;
4+
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.ValueSource;
7+
8+
class XmlDateTimeAdapterTest {
9+
10+
@ParameterizedTest
11+
@ValueSource(strings = {
12+
"2025-02-13T17:30:15+02:00",
13+
"2025-02-13T17:30:15+0200",
14+
"2025-02-13T17:30:15Z",
15+
"2025-02-13T17:30:15.010+02:00",
16+
"2025-02-13T17:30:15.0+02:00",
17+
"2025-02-13T17:30:15.100+02:00",
18+
"2025-02-13T17:30:15.099+02:00",
19+
"2025-02-13T17:30:15.99+02:00",
20+
})
21+
void withZone(String input){
22+
var adapter = new XmlDateTimeAdapter();
23+
var time = adapter.unmarshal(input);
24+
assertTrue(time.isZoned());
25+
}
26+
27+
@ParameterizedTest
28+
@ValueSource(strings = {
29+
"2025-02-13T17:30:15",
30+
"2025-02-13T17:30:15.10",
31+
"2025-02-13T17:30:15.999",
32+
})
33+
void withoutZone(String input){
34+
var adapter = new XmlDateTimeAdapter();
35+
var xmlDate = adapter.unmarshal(input);
36+
assertTrue(xmlDate.isLocal());
37+
}
38+
}

0 commit comments

Comments
 (0)