Index: src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java
===================================================================
--- src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java	(revision 14428)
+++ src/org/openstreetmap/josm/actions/relation/ExportRelationToGpxAction.java	(working copy)
@@ -20,6 +20,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.Stack;
+import java.util.concurrent.TimeUnit;
 
 import org.openstreetmap.josm.actions.GpxExportAction;
 import org.openstreetmap.josm.actions.IPrimitiveAction;
@@ -173,7 +174,7 @@
 
         GpxData gpxData = new GpxData();
         String layerName = " (GPX export)";
-        long time = System.currentTimeMillis()-24*3600*1000;
+        long time = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) - 24*3600;
 
         if (!flat.isEmpty()) {
             Map<String, Object> trkAttr = new HashMap<>();
@@ -196,16 +197,18 @@
                         if (trkAttr.isEmpty()) {
                             Relation r = Way.getParentRelations(Arrays.asList(flat.get(i).getWay()))
                                     .stream().filter(relsFound::contains).findFirst().orElseGet(null);
-                            if (r != null)
+                            if (r != null) {
                                 trkAttr.put("name", r.getName() != null ? r.getName() : r.getId());
+                                trkAttr.put("desc", tr("based on osm route relation data, timestamps are synthetic"));
+                            }
                             GpxData.ensureUniqueName(trkAttr, names);
                         }
                         List<Node> ln = flat.get(i).getWay().getNodes();
                         if (wct.get(i).direction == WayConnectionType.Direction.BACKWARD)
                             Collections.reverse(ln);
                         for (Node n: ln) {
-                            trkseg.add(OsmDataLayer.nodeToWayPoint(n, time));
-                            time += 1000;
+                            trkseg.add(OsmDataLayer.nodeToWayPoint(n, TimeUnit.SECONDS.toMillis(time)));
+                            time += 1;
                         }
                     }
                 }
Index: src/org/openstreetmap/josm/data/gpx/WayPoint.java
===================================================================
--- src/org/openstreetmap/josm/data/gpx/WayPoint.java	(revision 14428)
+++ src/org/openstreetmap/josm/data/gpx/WayPoint.java	(working copy)
@@ -128,7 +128,7 @@
     }
 
     /**
-     * Sets the {@link #time} field as well as the {@link #PT_TIME} attribute to the specified time
+     * Sets the {@link #time} field as well as the {@link #PT_TIME} attribute to the specified time.
      *
      * @param time the time to set
      * @since 9383
@@ -139,19 +139,35 @@
     }
 
     /**
-     * Convert the time stamp of the waypoint into seconds from the epoch
+     * Sets the {@link #time} field as well as the {@link #PT_TIME} attribute to the specified time.
+     *
+     * @param ts seconds from the epoch
+     * @since 13210
      */
-    public void setTime() {
-        setTimeFromAttribute();
+    public void setTime(long ts) {
+        this.time = ts;
+        this.attr.put(PT_TIME, DateUtils.fromTimestamp(ts));
     }
 
     /**
-     * Set the the time stamp of the waypoint into seconds from the epoch,
-     * @param time millisecond from the epoch
-     * @since 13210
+     * Sets the {@link #time} field as well as the {@link #PT_TIME} attribute to the specified time.
+     *
+     * @param ts milliseconds from the epoch
+     * @since xxx
      */
-    public void setTime(long time) {
-        this.time = time / 1000.;
+    public void setTimeInMillis(long ts) {
+        this.time = ts / 1000.;
+        this.attr.put(PT_TIME, DateUtils.fromTimestampInMillis(ts));
+    }
+
+    /**
+     * Convert the time stamp of the waypoint into seconds from the epoch.
+     *
+     * @deprecated call {@link #setTimeFromAttribute()} directly if you need this
+     */
+    @Deprecated
+    public void setTime() {
+        setTimeFromAttribute();
     }
 
     /**
Index: src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java	(revision 14428)
+++ src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java	(working copy)
@@ -192,7 +192,11 @@
      */
     protected int changesetId;
 
-    protected int timestamp;
+    /**
+     * A time value, measured in seconds from the epoch, or in other words,
+     * a number of seconds that have passed since 1970-01-01T00:00:00Z
+     */
+    protected long timestamp;
 
     /**
      * Get and write all attributes from the parameter. Does not fire any listener, so
@@ -316,11 +320,11 @@
 
     @Override
     public void setTimestamp(Date timestamp) {
-        this.timestamp = (int) TimeUnit.MILLISECONDS.toSeconds(timestamp.getTime());
+        this.timestamp = TimeUnit.MILLISECONDS.toSeconds(timestamp.getTime());
     }
 
     @Override
-    public void setRawTimestamp(int timestamp) {
+    public void setRawTimestamp(long timestamp) {
         this.timestamp = timestamp;
     }
 
@@ -330,7 +334,7 @@
     }
 
     @Override
-    public int getRawTimestamp() {
+    public long getRawTimestamp() {
         return timestamp;
     }
 
Index: src/org/openstreetmap/josm/data/osm/IPrimitive.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/IPrimitive.java	(revision 14428)
+++ src/org/openstreetmap/josm/data/osm/IPrimitive.java	(working copy)
@@ -285,7 +285,7 @@
      * @return last modification as timestamp
      * @see #setRawTimestamp
      */
-    int getRawTimestamp();
+    long getRawTimestamp();
 
     /**
      * Sets time of last modification to this object
@@ -299,7 +299,7 @@
      * @param timestamp date of last modification
      * @see #getRawTimestamp
      */
-    void setRawTimestamp(int timestamp);
+    void setRawTimestamp(long timestamp);
 
     /**
      * Determines if this primitive has no timestamp information.
Index: src/org/openstreetmap/josm/data/osm/PrimitiveData.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/PrimitiveData.java	(revision 14428)
+++ src/org/openstreetmap/josm/data/osm/PrimitiveData.java	(working copy)
@@ -115,7 +115,7 @@
         oos.writeLong(user == null ? -1 : user.getId());
         oos.writeInt(version);
         oos.writeInt(changesetId);
-        oos.writeInt(timestamp);
+        oos.writeLong(timestamp);
         oos.writeObject(keys);
         oos.writeShort(flags);
         oos.defaultWriteObject();
@@ -128,7 +128,7 @@
         user = userId == -1 ? null : User.getById(userId);
         version = ois.readInt();
         changesetId = ois.readInt();
-        timestamp = ois.readInt();
+        timestamp = ois.readLong();
         keys = (String[]) ois.readObject();
         flags = ois.readShort();
         ois.defaultReadObject();
Index: src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java
===================================================================
--- src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(revision 14428)
+++ src/org/openstreetmap/josm/gui/layer/OsmDataLayer.java	(working copy)
@@ -779,31 +779,28 @@
      * @since 13210
      */
     public static WayPoint nodeToWayPoint(Node n) {
-        return nodeToWayPoint(n, 0);
+        return nodeToWayPoint(n, Long.MIN_VALUE);
     }
 
     /**
      * @param n the {@code Node} to convert
-     * @param time a time value in milliseconds from the epoch.
+     * @param ts a timestamp value, in milliseconds from the epoch
      * @return {@code WayPoint} object
      * @since 13210
      */
-    public static WayPoint nodeToWayPoint(Node n, long time) {
+    public static WayPoint nodeToWayPoint(Node n, long ts) {
         WayPoint wpt = new WayPoint(n.getCoor());
 
         // Position info
 
         addDoubleIfPresent(wpt, n, GpxConstants.PT_ELE);
 
-        if (time > 0) {
-            wpt.put(GpxConstants.PT_TIME, DateUtils.fromTimestamp(time));
-            wpt.setTime(time);
+        if (ts != Long.MIN_VALUE) {
+            wpt.setTimeInMillis(ts);
         } else if (n.hasKey(GpxConstants.PT_TIME)) {
-            wpt.put(GpxConstants.PT_TIME, DateUtils.fromString(n.get(GpxConstants.PT_TIME)));
-            wpt.setTime();
+            wpt.setTime(DateUtils.fromString(n.get(GpxConstants.PT_TIME)));
         } else if (!n.isTimestampEmpty()) {
-            wpt.put(GpxConstants.PT_TIME, DateUtils.fromTimestamp(n.getRawTimestamp()));
-            wpt.setTime();
+            wpt.setTime(n.getRawTimestamp());
         }
 
         addDoubleIfPresent(wpt, n, GpxConstants.PT_MAGVAR);
Index: src/org/openstreetmap/josm/io/AbstractReader.java
===================================================================
--- src/org/openstreetmap/josm/io/AbstractReader.java	(revision 14428)
+++ src/org/openstreetmap/josm/io/AbstractReader.java	(working copy)
@@ -13,6 +13,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
 import org.openstreetmap.josm.data.Bounds;
@@ -409,7 +410,7 @@
 
     protected final void parseTimestamp(PrimitiveData current, String time) {
         if (time != null && !time.isEmpty()) {
-            current.setRawTimestamp((int) (DateUtils.tsFromString(time)/1000));
+            current.setRawTimestamp(TimeUnit.MILLISECONDS.toSeconds(DateUtils.tsFromString(time)));
         }
     }
 
Index: src/org/openstreetmap/josm/io/GpxReader.java
===================================================================
--- src/org/openstreetmap/josm/io/GpxReader.java	(revision 14428)
+++ src/org/openstreetmap/josm/io/GpxReader.java	(working copy)
@@ -444,7 +444,7 @@
                 case "cmt":
                 case "desc":
                     currentWayPoint.put(localName, accumulator.toString());
-                    currentWayPoint.setTime();
+                    currentWayPoint.setTimeFromAttribute();
                     break;
                 case "rtept":
                     currentState = states.pop();
Index: src/org/openstreetmap/josm/io/nmea/NmeaReader.java
===================================================================
--- src/org/openstreetmap/josm/io/nmea/NmeaReader.java	(revision 14428)
+++ src/org/openstreetmap/josm/io/nmea/NmeaReader.java	(working copy)
@@ -524,7 +524,7 @@
             ps.pDate = currentDate;
             if (ps.pWp != currentwp) {
                 if (ps.pWp != null) {
-                    ps.pWp.setTime();
+                    ps.pWp.setTimeFromAttribute();
                 }
                 ps.pWp = currentwp;
                 ps.waypoints.add(currentwp);
Index: src/org/openstreetmap/josm/tools/date/DateUtils.java
===================================================================
--- src/org/openstreetmap/josm/tools/date/DateUtils.java	(revision 14428)
+++ src/org/openstreetmap/josm/tools/date/DateUtils.java	(working copy)
@@ -152,8 +152,18 @@
      * @return The formatted date
      * @since 14055
      */
-    public static synchronized String fromTimestamp(long timestamp) {
-        final ZonedDateTime temporal = Instant.ofEpochMilli(TimeUnit.SECONDS.toMillis(timestamp)).atZone(ZoneOffset.UTC);
+    public static String fromTimestamp(long timestamp) {
+        return fromTimestampInMillis(TimeUnit.SECONDS.toMillis(timestamp));
+    }
+
+    /**
+     * Formats a date to the XML UTC format regardless of current locale.
+     * @param timestamp number of milliseconds since the epoch
+     * @return The formatted date
+     * @since xxx
+     */
+    public static synchronized String fromTimestampInMillis(long timestamp) {
+        final ZonedDateTime temporal = Instant.ofEpochMilli(timestamp).atZone(ZoneOffset.UTC);
         return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(temporal);
     }
 
