Index: /trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 17844)
+++ /trunk/src/org/openstreetmap/josm/data/gpx/GpxData.java	(revision 17845)
@@ -33,4 +33,5 @@
 import org.openstreetmap.josm.tools.ListenerList;
 import org.openstreetmap.josm.tools.ListeningCollection;
+import org.openstreetmap.josm.tools.date.Interval;
 
 /**
@@ -718,7 +719,7 @@
      * returns minimum and maximum timestamps in the track
      * @param trk track to analyze
-     * @return  minimum and maximum dates in array of 2 elements
-     */
-    public static Instant[] getMinMaxTimeForTrack(IGpxTrack trk) {
+     * @return minimum and maximum as interval
+     */
+    public static Optional<Interval> getMinMaxTimeForTrack(IGpxTrack trk) {
         final LongSummaryStatistics statistics = trk.getSegments().stream()
                 .flatMap(seg -> seg.getWayPoints().stream())
@@ -726,6 +727,6 @@
                 .summaryStatistics();
         return statistics.getCount() == 0 || (statistics.getMin() == 0 && statistics.getMax() == 0)
-                ? null
-                : new Instant[]{Instant.ofEpochMilli(statistics.getMin()), Instant.ofEpochMilli(statistics.getMax())};
+                ? Optional.empty()
+                : Optional.of(new Interval(Instant.ofEpochMilli(statistics.getMin()), Instant.ofEpochMilli(statistics.getMax())));
     }
 
@@ -734,8 +735,8 @@
     * Warning: there are lot of track with broken timestamps,
     * so we just ignore points from future and from year before 1970 in this method
-    * @return minimum and maximum dates in array of 2 elements
+    * @return minimum and maximum as interval
     * @since 7319
     */
-    public synchronized Instant[] getMinMaxTimeForAllTracks() {
+    public synchronized Optional<Interval> getMinMaxTimeForAllTracks() {
         long now = System.currentTimeMillis();
         final LongSummaryStatistics statistics = tracks.stream()
@@ -746,6 +747,6 @@
                 .summaryStatistics();
         return statistics.getCount() == 0
-                ? new Instant[0]
-                : new Instant[]{Instant.ofEpochMilli(statistics.getMin()), Instant.ofEpochMilli(statistics.getMax())};
+                ? Optional.empty()
+                : Optional.of(new Interval(Instant.ofEpochMilli(statistics.getMin()), Instant.ofEpochMilli(statistics.getMax())));
     }
 
Index: /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 17844)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java	(revision 17845)
@@ -40,5 +40,5 @@
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
-import org.openstreetmap.josm.tools.date.DateUtils;
+import org.openstreetmap.josm.tools.date.Interval;
 
 import javax.swing.AbstractAction;
@@ -53,6 +53,4 @@
 import java.io.File;
 import java.time.Instant;
-import java.time.format.DateTimeFormatter;
-import java.time.format.FormatStyle;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -157,30 +155,5 @@
      */
     public static String getTimespanForTrack(IGpxTrack trk) {
-        Instant[] bounds = GpxData.getMinMaxTimeForTrack(trk);
-        return bounds != null ? formatTimespan(bounds) : "";
-    }
-
-    /**
-     * Formats the timespan of the given track as a human readable string
-     * @param bounds The bounds to format, i.e., an array containing the min/max date
-     * @return The timespan as a string
-     */
-    public static String formatTimespan(Instant[] bounds) {
-        String ts = "";
-        DateTimeFormatter df = DateUtils.getDateFormatter(FormatStyle.SHORT);
-        String earliestDate = df.format(bounds[0]);
-        String latestDate = df.format(bounds[1]);
-
-        if (earliestDate.equals(latestDate)) {
-            DateTimeFormatter tf = DateUtils.getTimeFormatter(FormatStyle.SHORT);
-            ts += earliestDate + ' ';
-            ts += tf.format(bounds[0]) + " \u2013 " + tf.format(bounds[1]);
-        } else {
-            DateTimeFormatter dtf = DateUtils.getDateTimeFormatter(FormatStyle.SHORT, FormatStyle.MEDIUM);
-            ts += dtf.format(bounds[0]) + " \u2013 " + dtf.format(bounds[1]);
-        }
-
-        ts += String.format(" (%s)", Utils.getDurationString(bounds[1].toEpochMilli() - bounds[0].toEpochMilli()));
-        return ts;
+        return GpxData.getMinMaxTimeForTrack(trk).map(Interval::format).orElse("");
     }
 
@@ -354,8 +327,8 @@
         long to = toDate.toEpochMilli();
         for (IGpxTrack trk : data.getTracks()) {
-            Instant[] t = GpxData.getMinMaxTimeForTrack(trk);
+            Interval t = GpxData.getMinMaxTimeForTrack(trk).orElse(null);
 
             if (t == null) continue;
-            long tm = t[1].toEpochMilli();
+            long tm = t.getEnd().toEpochMilli();
             trackVisibility[i] = (tm == 0 && showWithoutDate) || (from <= tm && tm <= to);
             i++;
Index: /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java	(revision 17844)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java	(revision 17845)
@@ -54,4 +54,5 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.OpenBrowser;
+import org.openstreetmap.josm.tools.date.Interval;
 
 /**
@@ -88,5 +89,5 @@
             String name = (String) Optional.ofNullable(attr.get(GpxConstants.GPX_NAME)).orElse("");
             String desc = (String) Optional.ofNullable(attr.get(GpxConstants.GPX_DESC)).orElse("");
-            Instant[] time = GpxData.getMinMaxTimeForTrack(trk);
+            Interval time = GpxData.getMinMaxTimeForTrack(trk).orElse(null);
             String url = (String) Optional.ofNullable(attr.get("url")).orElse("");
             tracks[i] = new Object[]{name, desc, time, trk.length(), url, trk};
@@ -140,5 +141,5 @@
         t.setRowSorter(rowSorter);
         rowSorter.setModel(model);
-        rowSorter.setComparator(2, Comparator.comparing((Instant[] d) -> d == null ? Instant.MIN : d[0]));
+        rowSorter.setComparator(2, Comparator.comparing((Interval d) -> d == null ? Instant.MIN : d.getStart()));
         rowSorter.setComparator(3, Comparator.comparingDouble(length -> (double) length));
         // default column widths
@@ -150,6 +151,6 @@
             public Component getTableCellRendererComponent(
                     JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
-                if (value instanceof Instant[]) {
-                    value = GpxLayer.formatTimespan(((Instant[]) value));
+                if (value instanceof Interval) {
+                    value = ((Interval) value).format();
                 }
                 return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
@@ -327,5 +328,5 @@
                 JComponent jc = (JComponent) c;
                 Object value = getValueAt(row, col);
-                jc.setToolTipText(col == 2 ? Arrays.toString((Instant[]) value) : String.valueOf(value));
+                jc.setToolTipText(String.valueOf(value));
                 if (content.length > row
                         && content[row].length > 5
Index: /trunk/src/org/openstreetmap/josm/gui/layer/gpx/DateFilterPanel.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/gpx/DateFilterPanel.java	(revision 17844)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/gpx/DateFilterPanel.java	(revision 17845)
@@ -20,4 +20,5 @@
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.tools.GBC;
+import org.openstreetmap.josm.tools.date.Interval;
 
 /**
@@ -51,18 +52,10 @@
         this.layer = layer;
 
-        final Instant startTime, endTime;
-        Instant[] bounds = layer.data.getMinMaxTimeForAllTracks();
-        if (bounds.length == 0) {
-            startTime = ZonedDateTime.of(2000, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()).toInstant();
-            endTime = Instant.now();
-        } else {
-            startTime = bounds[0];
-            endTime = bounds[1];
-        }
-
-        dateFrom.setDate(startTime);
-        dateTo.setDate(endTime);
-        dateFrom.setRange(startTime, endTime);
-        dateTo.setRange(startTime, endTime);
+        Interval interval = layer.data.getMinMaxTimeForAllTracks()
+                .orElseGet(() -> new Interval(ZonedDateTime.of(2000, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()).toInstant(), Instant.now()));
+        dateFrom.setDate(interval.getStart());
+        dateTo.setDate(interval.getEnd());
+        dateFrom.setRange(interval.getStart(), interval.getEnd());
+        dateTo.setRange(interval.getStart(), interval.getEnd());
 
         add(noTimestampCb, GBC.std().grid(1, 1).insets(0, 0, 5, 0));
Index: /trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java	(revision 17844)
+++ /trunk/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java	(revision 17845)
@@ -60,4 +60,5 @@
 import org.openstreetmap.josm.tools.Stopwatch;
 import org.openstreetmap.josm.tools.Utils;
+import org.openstreetmap.josm.tools.date.Interval;
 
 /**
@@ -565,12 +566,7 @@
         double now = System.currentTimeMillis()/1000.0;
         if (colored == ColorMode.TIME) {
-            Instant[] bounds = data.getMinMaxTimeForAllTracks();
-            if (bounds.length >= 2) {
-                minval = bounds[0].getEpochSecond();
-                maxval = bounds[1].getEpochSecond();
-            } else {
-                minval = 0;
-                maxval = now;
-            }
+            Interval interval = data.getMinMaxTimeForAllTracks().orElse(new Interval(Instant.EPOCH, Instant.now()));
+            minval = interval.getStart().getEpochSecond();
+            maxval = interval.getEnd().getEpochSecond();
             dateScale.setRange(minval, maxval);
         }
Index: /trunk/src/org/openstreetmap/josm/tools/date/Interval.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/date/Interval.java	(revision 17845)
+++ /trunk/src/org/openstreetmap/josm/tools/date/Interval.java	(revision 17845)
@@ -0,0 +1,76 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools.date;
+
+import org.openstreetmap.josm.tools.Utils;
+
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.format.FormatStyle;
+import java.util.Objects;
+
+/**
+ * A timespan defined by a start and end instant.
+ */
+public final class Interval {
+
+    private final Instant start;
+    private final Instant end;
+
+    public Interval(Instant start, Instant end) {
+        this.start = start;
+        this.end = end;
+    }
+
+    /**
+     * Returns an ISO 8601 compatible string
+     * @return an ISO 8601 compatible string
+     */
+    @Override
+    public String toString() {
+        return start + "/" + end;
+    }
+
+    /**
+     * Formats the interval of the given track as a human readable string
+     * @return The interval as a string
+     */
+    public String format() {
+        String ts = "";
+        DateTimeFormatter df = DateUtils.getDateFormatter(FormatStyle.SHORT);
+        String earliestDate = df.format(getStart());
+        String latestDate = df.format(getEnd());
+
+        if (earliestDate.equals(latestDate)) {
+            DateTimeFormatter tf = DateUtils.getTimeFormatter(FormatStyle.SHORT);
+            ts += earliestDate + ' ';
+            ts += tf.format(getStart()) + " \u2013 " + tf.format(getEnd());
+        } else {
+            DateTimeFormatter dtf = DateUtils.getDateTimeFormatter(FormatStyle.SHORT, FormatStyle.MEDIUM);
+            ts += dtf.format(getStart()) + " \u2013 " + dtf.format(getEnd());
+        }
+
+        ts += String.format(" (%s)", Utils.getDurationString(getEnd().toEpochMilli() - getStart().toEpochMilli()));
+        return ts;
+    }
+
+    public Instant getStart() {
+        return start;
+    }
+
+    public Instant getEnd() {
+        return end;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof Interval)) return false;
+        Interval interval = (Interval) o;
+        return Objects.equals(start, interval.start) && Objects.equals(end, interval.end);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(start, end);
+    }
+}
Index: /trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java	(revision 17844)
+++ /trunk/test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java	(revision 17845)
@@ -33,4 +33,5 @@
 import org.openstreetmap.josm.testutils.JOSMTestRules;
 import org.openstreetmap.josm.tools.ListenerList;
+import org.openstreetmap.josm.tools.date.Interval;
 import org.xml.sax.SAXException;
 
@@ -330,5 +331,5 @@
     @Test
     void testGetMinMaxTimeForAllTracks() {
-        assertEquals(0, data.getMinMaxTimeForAllTracks().length);
+        assertFalse(data.getMinMaxTimeForAllTracks().isPresent());
 
         WayPoint p1 = new WayPoint(LatLon.NORTH_POLE);
@@ -343,8 +344,8 @@
         data.addTrack(new GpxTrack(Arrays.asList(Arrays.asList(p3, p4, p5)), Collections.emptyMap()));
 
-        Instant[] times = data.getMinMaxTimeForAllTracks();
-        assertEquals(times.length, 2);
-        assertEquals(Instant.ofEpochMilli(100020), times[0]);
-        assertEquals(Instant.ofEpochMilli(500020), times[1]);
+        Interval times = data.getMinMaxTimeForAllTracks().orElse(null);
+        assertEquals("1970-01-01T00:01:40.020Z/1970-01-01T00:08:20.020Z", times.toString());
+        assertEquals(Instant.ofEpochMilli(100020), times.getStart());
+        assertEquals(Instant.ofEpochMilli(500020), times.getEnd());
     }
 
Index: /trunk/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java	(revision 17844)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java	(revision 17845)
@@ -10,5 +10,4 @@
 import java.awt.Color;
 import java.io.IOException;
-import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -217,13 +216,4 @@
 
     /**
-     * Unit test of {@link GpxLayer#formatTimespan}.
-     */
-    @Test
-    void testFormatTimespan() {
-        Instant[] timespan = {Instant.parse("2021-03-01T17:53:16Z"), Instant.parse("2021-04-03T08:19:19Z")};
-        assertEquals("2021-03-01 17:53:16 \u2013 2021-04-03 08:19:19 (32 days 14 h)", GpxLayer.formatTimespan(timespan));
-    }
-
-    /**
      * Unit test of {@link GpxLayer#mergeFrom}.
      * @throws Exception if any error occurs
Index: /trunk/test/unit/org/openstreetmap/josm/tools/date/IntervalTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/tools/date/IntervalTest.java	(revision 17845)
+++ /trunk/test/unit/org/openstreetmap/josm/tools/date/IntervalTest.java	(revision 17845)
@@ -0,0 +1,51 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools.date;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+import java.time.Instant;
+import java.util.Locale;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class IntervalTest {
+
+    /**
+     * Setup test.
+     */
+    @RegisterExtension
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules().preferences();
+
+    /**
+     * Setup test.
+     */
+    @BeforeEach
+    void setUp() {
+        Locale.setDefault(Locale.ROOT);
+        DateUtils.PROP_ISO_DATES.put(true);
+    }
+
+    /**
+     * Unit test of {@link Interval#format}.
+     */
+    @Test
+    void testFormat() {
+        Interval interval = new Interval(Instant.parse("2021-03-01T17:53:16Z"), Instant.parse("2021-04-03T08:19:19Z"));
+        assertEquals("2021-03-01 17:53:16 \u2013 2021-04-03 08:19:19 (32 days 14 h)", interval.format());
+    }
+
+    /**
+     * Unit test of {@link Interval#toString}.
+     */
+    @Test
+    void testToString() {
+        Interval interval = new Interval(Instant.parse("2021-03-01T17:53:16Z"), Instant.parse("2021-04-03T08:19:19Z"));
+        assertEquals("2021-03-01T17:53:16Z/2021-04-03T08:19:19Z", interval.toString());
+    }
+
+}
