Handle tachograph activity times without offsets
This commit is contained in:
parent
a20d4c241e
commit
7209a73d30
|
|
@ -26,6 +26,7 @@ import java.util.UUID;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
|
|
@ -33,7 +34,6 @@ public class DriverCardXmlExtractionService {
|
||||||
|
|
||||||
private final DriverKeyFactory driverKeyFactory;
|
private final DriverKeyFactory driverKeyFactory;
|
||||||
private final VehicleKeyFactory vehicleKeyFactory;
|
private final VehicleKeyFactory vehicleKeyFactory;
|
||||||
private final XmlExpressionEvaluator xml = new XmlExpressionEvaluator();
|
|
||||||
|
|
||||||
public DriverCardXmlExtractionService(DriverKeyFactory driverKeyFactory, VehicleKeyFactory vehicleKeyFactory) {
|
public DriverCardXmlExtractionService(DriverKeyFactory driverKeyFactory, VehicleKeyFactory vehicleKeyFactory) {
|
||||||
this.driverKeyFactory = driverKeyFactory;
|
this.driverKeyFactory = driverKeyFactory;
|
||||||
|
|
@ -104,27 +104,28 @@ public class DriverCardXmlExtractionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractedDriverCard extractDriverCard(Document document, List<ExtractionWarning> warnings) {
|
private ExtractedDriverCard extractDriverCard(Document document, List<ExtractionWarning> warnings) {
|
||||||
Element identification = firstElement(document, "/DriverCard/Identification[1]");
|
Element identification = child(document.getDocumentElement(), "Identification");
|
||||||
if (identification == null) {
|
if (identification == null) {
|
||||||
warnings.add(new ExtractionWarning("MISSING_IDENTIFICATION", "Driver card identification block is missing.", "/DriverCard"));
|
warnings.add(new ExtractionWarning("MISSING_IDENTIFICATION", "Driver card identification block is missing.", "/DriverCard"));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String cardNation = text(identification, "cardIdentification/cardIssuingMemberState");
|
Element cardIdentification = child(identification, "cardIdentification");
|
||||||
|
String cardNation = childText(cardIdentification, "cardIssuingMemberState");
|
||||||
String cardNumber = joinCardNumber(identification);
|
String cardNumber = joinCardNumber(identification);
|
||||||
String authority = text(identification, "cardIdentification/cardIssuingAuthorityName/name");
|
String authority = childText(child(cardIdentification, "cardIssuingAuthorityName"), "name");
|
||||||
return new ExtractedDriverCard(
|
return new ExtractedDriverCard(
|
||||||
null,
|
null,
|
||||||
cardNation,
|
cardNation,
|
||||||
cardNumber,
|
cardNumber,
|
||||||
authority,
|
authority,
|
||||||
offsetDateTime(text(identification, "cardIdentification/cardIssueDate")),
|
offsetDateTime(childText(cardIdentification, "cardIssueDate")),
|
||||||
offsetDateTime(text(identification, "cardIdentification/cardValidityBegin")),
|
offsetDateTime(childText(cardIdentification, "cardValidityBegin")),
|
||||||
offsetDateTime(text(identification, "cardIdentification/cardExpiryDate"))
|
offsetDateTime(childText(cardIdentification, "cardExpiryDate"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractedDriver extractDriver(Document document, String driverKey, List<ExtractionWarning> warnings) {
|
private ExtractedDriver extractDriver(Document document, String driverKey, List<ExtractionWarning> warnings) {
|
||||||
Element identification = firstElement(document, "/DriverCard/Identification[1]");
|
Element identification = child(document.getDocumentElement(), "Identification");
|
||||||
if (identification == null) {
|
if (identification == null) {
|
||||||
warnings.add(new ExtractionWarning("MISSING_DRIVER", "Driver holder identification block is missing.", "/DriverCard"));
|
warnings.add(new ExtractionWarning("MISSING_DRIVER", "Driver holder identification block is missing.", "/DriverCard"));
|
||||||
return new ExtractedDriver(
|
return new ExtractedDriver(
|
||||||
|
|
@ -139,17 +140,20 @@ public class DriverCardXmlExtractionService {
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Element licenceInfo = firstElement(document, "/DriverCard/DrivingLicenseInfo[1]");
|
Element driverCardHolderIdentification = child(identification, "driverCardHolderIdentification");
|
||||||
|
Element cardHolderName = child(driverCardHolderIdentification, "cardHolderName");
|
||||||
|
Element licenceInfo = child(document.getDocumentElement(), "DrivingLicenseInfo");
|
||||||
|
Element cardDrivingLicenseInformation = child(licenceInfo, "cardDrivingLicenseInformation");
|
||||||
return new ExtractedDriver(
|
return new ExtractedDriver(
|
||||||
driverKey,
|
driverKey,
|
||||||
driverKeyFactory.createSourceDriverId(driverKey),
|
driverKeyFactory.createSourceDriverId(driverKey),
|
||||||
text(identification, "driverCardHolderIdentification/cardHolderName/holderSurname/name"),
|
childText(child(cardHolderName, "holderSurname"), "name"),
|
||||||
text(identification, "driverCardHolderIdentification/cardHolderName/holderFirstNames/name"),
|
childText(child(cardHolderName, "holderFirstNames"), "name"),
|
||||||
localDate(identification, "driverCardHolderIdentification/cardHolderBirthDate"),
|
localDate(child(driverCardHolderIdentification, "cardHolderBirthDate")),
|
||||||
text(identification, "driverCardHolderIdentification/cardHolderPreferredLanguage"),
|
childText(driverCardHolderIdentification, "cardHolderPreferredLanguage"),
|
||||||
text(licenceInfo, "cardDrivingLicenseInformation/drivingLicenceNumber"),
|
childText(cardDrivingLicenseInformation, "drivingLicenceNumber"),
|
||||||
text(licenceInfo, "cardDrivingLicenseInformation/drivingLicenceIssuingNation"),
|
childText(cardDrivingLicenseInformation, "drivingLicenceIssuingNation"),
|
||||||
text(licenceInfo, "cardDrivingLicenseInformation/drivingLicenceIssuingAuthority/name")
|
childText(child(cardDrivingLicenseInformation, "drivingLicenceIssuingAuthority"), "name")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,15 +163,17 @@ public class DriverCardXmlExtractionService {
|
||||||
Map<String, ExtractedVehicle> vehiclesByKey,
|
Map<String, ExtractedVehicle> vehiclesByKey,
|
||||||
List<ExtractionWarning> warnings
|
List<ExtractionWarning> warnings
|
||||||
) {
|
) {
|
||||||
NodeList records = nodes(document, "/DriverCard/VehiclesUsed/cardVehiclesUsed/cardVehicleRecords");
|
Element cardVehiclesUsed = child(child(document.getDocumentElement(), "VehiclesUsed"), "cardVehiclesUsed");
|
||||||
List<ExtractedCardVehicleUsageInterval> intervals = new ArrayList<>();
|
List<Element> records = children(cardVehiclesUsed, "cardVehicleRecords");
|
||||||
for (int i = 0; i < records.getLength(); i++) {
|
List<ExtractedCardVehicleUsageInterval> intervals = new ArrayList<>(records.size());
|
||||||
Element record = (Element) records.item(i);
|
for (int i = 0; i < records.size(); i++) {
|
||||||
|
Element record = records.get(i);
|
||||||
String path = "/DriverCard/VehiclesUsed/cardVehiclesUsed/cardVehicleRecords[" + (i + 1) + "]";
|
String path = "/DriverCard/VehiclesUsed/cardVehiclesUsed/cardVehicleRecords[" + (i + 1) + "]";
|
||||||
OffsetDateTime from = offsetDateTime(text(record, "vehicleFirstUse"));
|
OffsetDateTime from = offsetDateTime(childText(record, "vehicleFirstUse"));
|
||||||
OffsetDateTime to = offsetDateTime(text(record, "vehicleLastUse"));
|
OffsetDateTime to = offsetDateTime(childText(record, "vehicleLastUse"));
|
||||||
String registrationNation = text(record, "vehicleRegistration/vehicleRegistrationNation");
|
Element vehicleRegistration = child(record, "vehicleRegistration");
|
||||||
String registrationNumber = text(record, "vehicleRegistration/vehicleRegistrationNumber/vehicleRegNumber");
|
String registrationNation = childText(vehicleRegistration, "vehicleRegistrationNation");
|
||||||
|
String registrationNumber = childText(child(vehicleRegistration, "vehicleRegistrationNumber"), "vehicleRegNumber");
|
||||||
String registrationKey = vehicleKeyFactory.createRegistrationKey(registrationNation, registrationNumber);
|
String registrationKey = vehicleKeyFactory.createRegistrationKey(registrationNation, registrationNumber);
|
||||||
registrationsByKey.putIfAbsent(
|
registrationsByKey.putIfAbsent(
|
||||||
registrationKey,
|
registrationKey,
|
||||||
|
|
@ -178,7 +184,7 @@ public class DriverCardXmlExtractionService {
|
||||||
registrationNumber
|
registrationNumber
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
String vin = text(record, "vehicleIdentificationNumber");
|
String vin = childText(record, "vehicleIdentificationNumber");
|
||||||
String vehicleKey = vehicleKeyFactory.createVehicleKey(vin);
|
String vehicleKey = vehicleKeyFactory.createVehicleKey(vin);
|
||||||
if (vehicleKey != null) {
|
if (vehicleKey != null) {
|
||||||
vehiclesByKey.putIfAbsent(
|
vehiclesByKey.putIfAbsent(
|
||||||
|
|
@ -194,8 +200,8 @@ public class DriverCardXmlExtractionService {
|
||||||
"CVU-" + (i + 1),
|
"CVU-" + (i + 1),
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
longValue(text(record, "vehicleOdometerBegin")),
|
longValue(childText(record, "vehicleOdometerBegin")),
|
||||||
longValue(text(record, "vehicleOdometerEnd")),
|
longValue(childText(record, "vehicleOdometerEnd")),
|
||||||
registrationKey,
|
registrationKey,
|
||||||
vehicleKey,
|
vehicleKey,
|
||||||
path
|
path
|
||||||
|
|
@ -206,33 +212,34 @@ public class DriverCardXmlExtractionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<ExtractedCardActivityInterval> extractActivityIntervals(Document document, List<ExtractionWarning> warnings) {
|
private List<ExtractedCardActivityInterval> extractActivityIntervals(Document document, List<ExtractionWarning> warnings) {
|
||||||
NodeList dayRecords = nodes(document, "/DriverCard/DriverActivityData/cardDriverActivity/cardActivityDailyRecord");
|
Element cardDriverActivity = child(child(document.getDocumentElement(), "DriverActivityData"), "cardDriverActivity");
|
||||||
List<ExtractedCardActivityInterval> intervals = new ArrayList<>();
|
List<Element> dayRecords = children(cardDriverActivity, "cardActivityDailyRecord");
|
||||||
|
List<ExtractedCardActivityInterval> intervals = new ArrayList<>(dayRecords.size() * 8);
|
||||||
int intervalNo = 0;
|
int intervalNo = 0;
|
||||||
for (int dayIndex = 0; dayIndex < dayRecords.getLength(); dayIndex++) {
|
for (int dayIndex = 0; dayIndex < dayRecords.size(); dayIndex++) {
|
||||||
Element dayRecord = (Element) dayRecords.item(dayIndex);
|
Element dayRecord = dayRecords.get(dayIndex);
|
||||||
String dayPath = "/DriverCard/DriverActivityData/cardDriverActivity/cardActivityDailyRecord[" + (dayIndex + 1) + "]";
|
String dayPath = "/DriverCard/DriverActivityData/cardDriverActivity/cardActivityDailyRecord[" + (dayIndex + 1) + "]";
|
||||||
OffsetDateTime recordDate = offsetDateTime(text(dayRecord, "activityRecordDate"));
|
OffsetDateTime recordDate = offsetDateTime(childText(dayRecord, "activityRecordDate"));
|
||||||
if (recordDate == null) {
|
if (recordDate == null) {
|
||||||
warnings.add(new ExtractionWarning("MISSING_ACTIVITY_RECORD_DATE", "Activity daily record has no activityRecordDate.", dayPath));
|
warnings.add(new ExtractionWarning("MISSING_ACTIVITY_RECORD_DATE", "Activity daily record has no activityRecordDate.", dayPath));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
LocalDate date = recordDate.withOffsetSameInstant(ZoneOffset.UTC).toLocalDate();
|
LocalDate date = recordDate.withOffsetSameInstant(ZoneOffset.UTC).toLocalDate();
|
||||||
NodeList changes = nodes(dayRecord, "activityChangeInfos");
|
List<Element> changes = children(dayRecord, "activityChangeInfos");
|
||||||
List<ActivityChange> parsedChanges = new ArrayList<>();
|
List<ActivityChange> parsedChanges = new ArrayList<>(changes.size());
|
||||||
for (int changeIndex = 0; changeIndex < changes.getLength(); changeIndex++) {
|
for (int changeIndex = 0; changeIndex < changes.size(); changeIndex++) {
|
||||||
Element change = (Element) changes.item(changeIndex);
|
Element change = changes.get(changeIndex);
|
||||||
OffsetDateTime from = combine(date, text(change, "timeOfChange"));
|
OffsetDateTime from = combine(date, childText(change, "timeOfChange"));
|
||||||
if (from == null) {
|
if (from == null) {
|
||||||
warnings.add(new ExtractionWarning("INVALID_ACTIVITY_CHANGE_TIME", "Activity change has invalid timeOfChange.", dayPath + "/activityChangeInfos[" + (changeIndex + 1) + "]"));
|
warnings.add(new ExtractionWarning("INVALID_ACTIVITY_CHANGE_TIME", "Activity change has invalid timeOfChange.", dayPath + "/activityChangeInfos[" + (changeIndex + 1) + "]"));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
parsedChanges.add(new ActivityChange(
|
parsedChanges.add(new ActivityChange(
|
||||||
from,
|
from,
|
||||||
normalizeActivity(text(change, "activity")),
|
normalizeActivity(childText(change, "activity")),
|
||||||
normalizeToken(text(change, "slot")),
|
normalizeToken(childText(change, "slot")),
|
||||||
normalizeToken(text(change, "cardStatus")),
|
normalizeToken(childText(change, "cardStatus")),
|
||||||
normalizeToken(text(change, "drivingStatus")),
|
normalizeToken(childText(change, "drivingStatus")),
|
||||||
dayPath + "/activityChangeInfos[" + (changeIndex + 1) + "]"
|
dayPath + "/activityChangeInfos[" + (changeIndex + 1) + "]"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
@ -351,20 +358,42 @@ public class DriverCardXmlExtractionService {
|
||||||
return timestamp.plusSeconds(1);
|
return timestamp.plusSeconds(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Element firstElement(Object node, String expression) {
|
private Element child(Element parent, String name) {
|
||||||
NodeList nodes = nodes(node, expression);
|
if (parent == null) {
|
||||||
if (nodes.getLength() == 0) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (Element) nodes.item(0);
|
NodeList childNodes = parent.getChildNodes();
|
||||||
|
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||||
|
Node child = childNodes.item(i);
|
||||||
|
if (child.getNodeType() == Node.ELEMENT_NODE && name.equals(child.getNodeName())) {
|
||||||
|
return (Element) child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NodeList nodes(Object node, String expression) {
|
private List<Element> children(Element parent, String name) {
|
||||||
return xml.nodes(node, expression);
|
if (parent == null) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
NodeList childNodes = parent.getChildNodes();
|
||||||
|
List<Element> children = new ArrayList<>();
|
||||||
|
for (int i = 0; i < childNodes.getLength(); i++) {
|
||||||
|
Node child = childNodes.item(i);
|
||||||
|
if (child.getNodeType() == Node.ELEMENT_NODE && name.equals(child.getNodeName())) {
|
||||||
|
children.add((Element) child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String text(Object node, String expression) {
|
private String childText(Element parent, String name) {
|
||||||
return xml.text(node, expression);
|
Element child = child(parent, name);
|
||||||
|
if (child == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String value = child.getTextContent();
|
||||||
|
return value == null || value.isBlank() ? null : value.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
private OffsetDateTime offsetDateTime(String value) {
|
private OffsetDateTime offsetDateTime(String value) {
|
||||||
|
|
@ -374,14 +403,13 @@ public class DriverCardXmlExtractionService {
|
||||||
return OffsetDateTime.parse(value.trim()).withOffsetSameInstant(ZoneOffset.UTC);
|
return OffsetDateTime.parse(value.trim()).withOffsetSameInstant(ZoneOffset.UTC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalDate localDate(Element element, String expression) {
|
private LocalDate localDate(Element dateElement) {
|
||||||
Element dateElement = firstElement(element, expression);
|
|
||||||
if (dateElement == null) {
|
if (dateElement == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String year = text(dateElement, "year");
|
String year = childText(dateElement, "year");
|
||||||
String month = text(dateElement, "month");
|
String month = childText(dateElement, "month");
|
||||||
String day = text(dateElement, "day");
|
String day = childText(dateElement, "day");
|
||||||
if (year == null || month == null || day == null) {
|
if (year == null || month == null || day == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -389,11 +417,7 @@ public class DriverCardXmlExtractionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private OffsetDateTime combine(LocalDate date, String timeText) {
|
private OffsetDateTime combine(LocalDate date, String timeText) {
|
||||||
if (date == null || timeText == null || timeText.isBlank()) {
|
return TachographTimeParser.combineUtc(date, timeText);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
LocalTime time = java.time.OffsetTime.parse(timeText.trim()).withOffsetSameInstant(ZoneOffset.UTC).toLocalTime();
|
|
||||||
return date.atTime(time).atOffset(ZoneOffset.UTC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Long longValue(String value) {
|
private Long longValue(String value) {
|
||||||
|
|
@ -435,12 +459,14 @@ public class DriverCardXmlExtractionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String joinCardNumber(Element identification) {
|
private String joinCardNumber(Element identification) {
|
||||||
String driverIdentification = text(identification, "cardIdentification/cardNumber/driverIdentification");
|
Element cardIdentification = child(identification, "cardIdentification");
|
||||||
|
Element cardNumber = child(cardIdentification, "cardNumber");
|
||||||
|
String driverIdentification = childText(cardNumber, "driverIdentification");
|
||||||
if (driverIdentification == null) {
|
if (driverIdentification == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String replacement = text(identification, "cardIdentification/cardNumber/cardReplacementIndex");
|
String replacement = childText(cardNumber, "cardReplacementIndex");
|
||||||
String renewal = text(identification, "cardIdentification/cardNumber/cardRenewalIndex");
|
String renewal = childText(cardNumber, "cardRenewalIndex");
|
||||||
StringBuilder builder = new StringBuilder(driverIdentification);
|
StringBuilder builder = new StringBuilder(driverIdentification);
|
||||||
if (replacement != null) {
|
if (replacement != null) {
|
||||||
builder.append(replacement);
|
builder.append(replacement);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package at.procon.eventhub.tachographfilesession.service;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.time.OffsetTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.time.format.DateTimeParseException;
|
||||||
|
|
||||||
|
final class TachographTimeParser {
|
||||||
|
|
||||||
|
private TachographTimeParser() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static OffsetDateTime combineUtc(LocalDate date, String timeText) {
|
||||||
|
if (date == null || timeText == null || timeText.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String normalized = timeText.trim();
|
||||||
|
try {
|
||||||
|
OffsetTime offsetTime = OffsetTime.parse(normalized);
|
||||||
|
return date.atTime(offsetTime.toLocalTime())
|
||||||
|
.atOffset(offsetTime.getOffset())
|
||||||
|
.withOffsetSameInstant(ZoneOffset.UTC);
|
||||||
|
} catch (DateTimeParseException ignored) {
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return date.atTime(LocalTime.parse(normalized)).atOffset(ZoneOffset.UTC);
|
||||||
|
} catch (DateTimeParseException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -727,11 +727,7 @@ public class VehicleUnitXmlExtractionService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private OffsetDateTime combine(LocalDate date, String timeText) {
|
private OffsetDateTime combine(LocalDate date, String timeText) {
|
||||||
if (date == null || timeText == null || timeText.isBlank()) {
|
return TachographTimeParser.combineUtc(date, timeText);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
LocalTime time = java.time.OffsetTime.parse(timeText.trim()).withOffsetSameInstant(ZoneOffset.UTC).toLocalTime();
|
|
||||||
return date.atTime(time).atOffset(ZoneOffset.UTC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String normalizeActivity(String value) {
|
private String normalizeActivity(String value) {
|
||||||
|
|
|
||||||
|
|
@ -52,4 +52,31 @@ class DriverCardXmlExtractionServiceTest {
|
||||||
assertThat(driver.cardActivityIntervals().get(3).registrationKey()).isEqualTo("12:W-54321B");
|
assertThat(driver.cardActivityIntervals().get(3).registrationKey()).isEqualTo("12:W-54321B");
|
||||||
assertThat(driver.cardActivityIntervals().get(4).registrationKey()).isNull();
|
assertThat(driver.cardActivityIntervals().get(4).registrationKey()).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void extractsActivitiesWhenTimeOfChangeHasNoOffset() {
|
||||||
|
TachographFileSession session = service.extract(
|
||||||
|
parser.parse(DriverCardXmlSamples.validDriverCardXmlWithLocalActivityTimes()),
|
||||||
|
new TachographFileSessionMetadata(
|
||||||
|
"default",
|
||||||
|
"legalrequirements-drivercard",
|
||||||
|
"sample",
|
||||||
|
"sample.ddd",
|
||||||
|
"abc",
|
||||||
|
10,
|
||||||
|
"42",
|
||||||
|
"def",
|
||||||
|
true,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
Instant.now(),
|
||||||
|
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||||
|
);
|
||||||
|
|
||||||
|
DriverExtractionSession driver = session.driversByKey().values().iterator().next();
|
||||||
|
assertThat(driver.cardActivityIntervals()).hasSize(5);
|
||||||
|
assertThat(driver.cardActivityIntervals())
|
||||||
|
.extracting(interval -> interval.from().toString())
|
||||||
|
.contains("2026-04-01T08:00Z", "2026-04-01T12:30Z");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,4 +112,11 @@ final class DriverCardXmlSamples {
|
||||||
</DriverCard>
|
</DriverCard>
|
||||||
""";
|
""";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String validDriverCardXmlWithLocalActivityTimes() {
|
||||||
|
return validDriverCardXml()
|
||||||
|
.replace("<timeOfChange>08:00:00Z</timeOfChange>", "<timeOfChange>08:00:00</timeOfChange>")
|
||||||
|
.replace("<timeOfChange>09:00:00Z</timeOfChange>", "<timeOfChange>09:00:00</timeOfChange>")
|
||||||
|
.replace("<timeOfChange>12:30:00Z</timeOfChange>", "<timeOfChange>12:30:00</timeOfChange>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package at.procon.eventhub.tachographfilesession.service;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class TachographTimeParserTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void combinesPlainLocalTimeAsUtc() {
|
||||||
|
assertThat(TachographTimeParser.combineUtc(LocalDate.of(2026, 4, 1), "00:00:00"))
|
||||||
|
.hasToString("2026-04-01T00:00Z");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void preservesDateShiftWhenOffsetTimeCrossesUtcMidnight() {
|
||||||
|
assertThat(TachographTimeParser.combineUtc(LocalDate.of(2026, 4, 1), "00:30:00+02:00"))
|
||||||
|
.hasToString("2026-03-31T22:30Z");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void returnsNullForInvalidTimeText() {
|
||||||
|
assertThat(TachographTimeParser.combineUtc(LocalDate.of(2026, 4, 1), "not-a-time")).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -76,6 +76,32 @@ class VehicleUnitXmlExtractionServiceTest {
|
||||||
.contains("OPEN_VU_CARD_INTERVAL", "VU_ACTIVITY_UNASSIGNED");
|
.contains("OPEN_VU_CARD_INTERVAL", "VU_ACTIVITY_UNASSIGNED");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void extractsActivitiesWhenVuTimeOfChangeHasNoOffset() throws Exception {
|
||||||
|
TachographFileSession session = service.extract(
|
||||||
|
new TachographXmlParser.ParsedTachographXml(document(VehicleUnitXmlSamples.vehicleUnitXmlWithLocalActivityTimes()), "VehicleUnit"),
|
||||||
|
new TachographFileSessionMetadata(
|
||||||
|
"default",
|
||||||
|
"legalrequirements-vehicleunit",
|
||||||
|
"sample-vu",
|
||||||
|
"sample-vu.ddd",
|
||||||
|
"abc",
|
||||||
|
10,
|
||||||
|
"42",
|
||||||
|
"def",
|
||||||
|
false,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
Instant.now(),
|
||||||
|
Instant.now().plus(4, ChronoUnit.HOURS)
|
||||||
|
);
|
||||||
|
|
||||||
|
DriverExtractionSession firstDriver = session.driversByKey().get("12:12345678901200");
|
||||||
|
DriverExtractionSession secondDriver = session.driversByKey().get("12:99999999999911");
|
||||||
|
assertThat(firstDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-01T08:00Z");
|
||||||
|
assertThat(secondDriver.cardActivityIntervals().get(0).from().toString()).isEqualTo("2026-04-02T07:30Z");
|
||||||
|
}
|
||||||
|
|
||||||
private Document document(String xml) throws Exception {
|
private Document document(String xml) throws Exception {
|
||||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||||
factory.setNamespaceAware(false);
|
factory.setNamespaceAware(false);
|
||||||
|
|
|
||||||
|
|
@ -173,4 +173,12 @@ final class VehicleUnitXmlSamples {
|
||||||
</VehicleUnit>
|
</VehicleUnit>
|
||||||
""";
|
""";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String vehicleUnitXmlWithLocalActivityTimes() {
|
||||||
|
return vehicleUnitXml()
|
||||||
|
.replace("<timeOfChange>08:00:00Z</timeOfChange>", "<timeOfChange>08:00:00</timeOfChange>")
|
||||||
|
.replace("<timeOfChange>09:00:00Z</timeOfChange>", "<timeOfChange>09:00:00</timeOfChange>")
|
||||||
|
.replace("<timeOfChange>11:00:00Z</timeOfChange>", "<timeOfChange>11:00:00</timeOfChange>")
|
||||||
|
.replace("<timeOfChange>07:30:00Z</timeOfChange>", "<timeOfChange>07:30:00</timeOfChange>");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue