mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2026-07-31 07:44:24 +02:00
GPX: Fix parsing of timestamps without timezone
Although these are non-conforming, there are a lot of gpx files online that do not contain a timezone and fail to be parsed.
This commit is contained in:
@@ -160,6 +160,10 @@ public class GpxParser {
|
||||
if (text == null || text.length() < 1) {
|
||||
return defaultDate;
|
||||
}
|
||||
// If no timezone indicator is present, assume UTC
|
||||
if (!text.endsWith("Z") && !text.contains("+") && !(text.length() > 19 && text.charAt(19) == '-')) {
|
||||
return ISO8601Utils.parse(text + "Z", new ParsePosition(0));
|
||||
}
|
||||
return ISO8601Utils.parse(text, new ParsePosition(0));
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +201,31 @@ public class GPXParserTest extends TestBase {
|
||||
Assert.assertEquals(trackpoint.getVdop(), activityPoint.getLocation().getVdop(), 0.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldParseDatetimeVariants() throws IOException, GpxParseException {
|
||||
// Validates parsing of timestamps with no timezone (treated as UTC), explicit UTC (Z),
|
||||
// positive offset, and negative offset.
|
||||
try (final InputStream inputStream = getClass().getResourceAsStream("/gpx-parser-test-datetime.gpx")) {
|
||||
final GpxParser gpxParser = new GpxParser(inputStream);
|
||||
final GpxFile gpxFile = gpxParser.getGpxFile();
|
||||
|
||||
// Metadata time: No timezone indicator: should be treated as UTC
|
||||
Assert.assertEquals(Date.from(Instant.parse("2026-05-23T19:31:00Z")), gpxFile.getTime());
|
||||
|
||||
final List<GpxTrackPoint> points = gpxFile.getPoints();
|
||||
Assert.assertEquals(4, points.size());
|
||||
|
||||
// No timezone: treated as UTC
|
||||
Assert.assertEquals(Date.from(Instant.parse("2026-05-23T19:31:24Z")), points.get(0).getTime());
|
||||
// Explicit UTC (Z)
|
||||
Assert.assertEquals(Date.from(Instant.parse("2026-05-23T19:32:00Z")), points.get(1).getTime());
|
||||
// Positive offset (+02:00): 21:33 local = 19:33 UTC
|
||||
Assert.assertEquals(Date.from(Instant.parse("2026-05-23T19:33:00Z")), points.get(2).getTime());
|
||||
// Negative offset (-07:00): 12:34 local = 19:34 UTC
|
||||
Assert.assertEquals(Date.from(Instant.parse("2026-05-23T19:34:00Z")), points.get(3).getTime());
|
||||
}
|
||||
}
|
||||
|
||||
// test import of old GPX v1.0
|
||||
@Test
|
||||
public void TestGpxImportOld() throws Exception {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|
||||
<gpx version="1.1" creator="NonConformingDevice"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
|
||||
xmlns="http://www.topografix.com/GPX/1/1">
|
||||
<metadata>
|
||||
<name>Test Timezone Variants</name>
|
||||
<!-- No timezone indicator: should be treated as UTC -->
|
||||
<time>2026-05-23T19:31:00</time>
|
||||
</metadata>
|
||||
<trk>
|
||||
<trkseg>
|
||||
<!-- No timezone: treated as UTC -->
|
||||
<trkpt lon="9.7393798828125" lat="47.5048828125">
|
||||
<ele>440.0</ele>
|
||||
<time>2026-05-23T19:31:24</time>
|
||||
</trkpt>
|
||||
<!-- Explicit UTC (Z) -->
|
||||
<trkpt lon="9.7394798828125" lat="47.5058828125">
|
||||
<ele>441.0</ele>
|
||||
<time>2026-05-23T19:32:00Z</time>
|
||||
</trkpt>
|
||||
<!-- Positive offset (+02:00): 21:33 local = 19:33 UTC -->
|
||||
<trkpt lon="9.7395798828125" lat="47.5068828125">
|
||||
<ele>442.0</ele>
|
||||
<time>2026-05-23T21:33:00+02:00</time>
|
||||
</trkpt>
|
||||
<!-- Negative offset (-07:00): 12:34 local = 19:34 UTC -->
|
||||
<trkpt lon="9.7396798828125" lat="47.5078828125">
|
||||
<ele>443.0</ele>
|
||||
<time>2026-05-23T12:34:00-07:00</time>
|
||||
</trkpt>
|
||||
</trkseg>
|
||||
</trk>
|
||||
</gpx>
|
||||
Reference in New Issue
Block a user