This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new 54ffc5014e [MNG-5729] Use monotonic time measurements (#1965)
54ffc5014e is described below

commit 54ffc5014e3fc2ebc0869e30136d8c239db7eacb
Author: Guillaume Nodet <gno...@gmail.com>
AuthorDate: Thu Dec 12 12:02:17 2024 +0100

    [MNG-5729] Use monotonic time measurements (#1965)
---
 api/maven-api-core/pom.xml                         |   6 ++
 .../main/java/org/apache/maven/api/Constants.java  |   8 ++
 .../java/org/apache/maven/api/MonotonicClock.java  | 120 +++++++++++++++++++++
 .../java/org/apache/maven/api/ProtoSession.java    |   2 +-
 .../maven/cling/event/ExecutionEventLogger.java    |  14 ++-
 .../maven/cling/invoker/mvn/MavenInvoker.java      |   4 +-
 .../transfer/AbstractMavenTransferListener.java    |  12 ++-
 .../cling/transfer/Slf4jMavenTransferListener.java |  13 ++-
 .../maven/cling/utils/CLIReportingUtils.java       |  63 ++++-------
 .../main/java/org/apache/maven/DefaultMaven.java   |   4 +-
 .../org/apache/maven/execution/BuildFailure.java   |  15 ++-
 .../org/apache/maven/execution/BuildSuccess.java   |  16 ++-
 .../org/apache/maven/execution/BuildSummary.java   |  26 +++--
 .../execution/DefaultMavenExecutionRequest.java    |  23 +++-
 .../maven/execution/MavenExecutionRequest.java     |   7 ++
 .../org/apache/maven/execution/MavenSession.java   |   6 ++
 .../org/apache/maven/execution/ReactorManager.java |   9 ++
 .../DefaultRepositorySystemSessionFactory.java     |   1 +
 .../apache/maven/internal/impl/DefaultSession.java |   2 +-
 .../lifecycle/internal/LifecycleModuleBuilder.java |  11 +-
 .../lifecycle/internal/builder/BuilderCommon.java  |  11 +-
 .../internal/concurrent/BuildPlanExecutor.java     |  30 +++---
 .../project/DefaultProjectBuildingRequest.java     |  15 ++-
 .../maven/project/ProjectBuildingRequest.java      |  17 +++
 .../internal/impl/model/MavenBuildTimestamp.java   |  20 ++--
 .../impl/resolver/LocalSnapshotMetadata.java       |   6 +-
 .../resolver/LocalSnapshotMetadataGenerator.java   |   8 +-
 .../internal/impl/resolver/MavenMetadata.java      |  19 ++--
 .../impl/resolver/MavenSnapshotMetadata.java       |   4 +-
 .../internal/impl/resolver/PluginsMetadata.java    |   6 +-
 .../impl/resolver/PluginsMetadataGenerator.java    |   8 +-
 .../impl/resolver/RemoteSnapshotMetadata.java      |  20 ++--
 .../resolver/RemoteSnapshotMetadataGenerator.java  |   7 +-
 .../internal/impl/resolver/VersionsMetadata.java   |   6 +-
 .../impl/resolver/VersionsMetadataGenerator.java   |   8 +-
 .../impl/model/DefaultModelInterpolatorTest.java   |  24 +++--
 .../impl/model/MavenBuildTimestampTest.java        |   4 +-
 .../maven/internal/impl/standalone/ApiRunner.java  |   3 +-
 .../org/apache/maven/slf4j/MavenBaseLogger.java    |  12 ++-
 .../maven/slf4j/SimpleLoggerConfiguration.java     |   7 +-
 src/site/markdown/configuration.properties         | 114 ++++++++++----------
 src/site/markdown/configuration.yaml               |   6 ++
 src/site/markdown/maven-configuration.md           |  35 +++---
 43 files changed, 503 insertions(+), 249 deletions(-)

diff --git a/api/maven-api-core/pom.xml b/api/maven-api-core/pom.xml
index ae950fbb48..f713e2050f 100644
--- a/api/maven-api-core/pom.xml
+++ b/api/maven-api-core/pom.xml
@@ -59,6 +59,12 @@
       <groupId>org.apache.maven</groupId>
       <artifactId>maven-api-di</artifactId>
     </dependency>
+
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-api</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
 </project>
diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java
index dacf1ec6a2..a80fbd68f6 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Constants.java
@@ -437,5 +437,13 @@ public final class Constants {
     @Config(type = "java.lang.Integer")
     public static final String MAVEN_DEPLOY_SNAPSHOT_BUILD_NUMBER = 
"maven.deploy.snapshot.buildNumber";
 
+    /**
+     * User property used to store the build timestamp.
+     *
+     * @since 4.1.0
+     */
+    @Config(type = "java.time.Instant")
+    public static final String MAVEN_START_INSTANT = "maven.startInstant";
+
     private Constants() {}
 }
diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/MonotonicClock.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/MonotonicClock.java
new file mode 100644
index 0000000000..5fa3de655c
--- /dev/null
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/MonotonicClock.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.api;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+
+/**
+ * A Clock implementation that combines monotonic timing with wall-clock time.
+ * <p>
+ * This class provides precise time measurements using {@link 
System#nanoTime()}
+ * while maintaining wall-clock time information in UTC. The wall-clock time
+ * is computed from the monotonic duration since system start to ensure 
consistency
+ * between time measurements.
+ * <p>
+ * This implementation is singleton-based and always uses UTC timezone. The 
clock
+ * cannot be adjusted to different timezones to maintain consistent monotonic 
behavior.
+ * Users needing local time representation should convert the result of {@link 
#instant()}
+ * to their desired timezone:
+ * <pre>{@code
+ * Instant now = MonotonicClock.now();
+ * ZonedDateTime local = now.atZone(ZoneId.systemDefault());
+ * }</pre>
+ *
+ * @see System#nanoTime()
+ * @see Clock
+ */
+public class MonotonicClock extends Clock {
+    private static final MonotonicClock CLOCK = new MonotonicClock();
+
+    private final long startNanos;
+    private final Instant startInstant;
+
+    /**
+     * Private constructor to enforce singleton pattern.
+     * Initializes the clock with the current system time and nanoTime.
+     */
+    private MonotonicClock() {
+        this.startNanos = System.nanoTime();
+        this.startInstant = Clock.systemUTC().instant();
+    }
+
+    /**
+     * Returns the singleton instance of MonotonicClock.
+     *
+     * @return the monotonic clock instance
+     */
+    public static MonotonicClock get() {
+        return CLOCK;
+    }
+
+    /**
+     * Returns the current instant from the monotonic clock.
+     * This is a convenience method equivalent to {@code get().instant()}.
+     *
+     * @return the current instant using monotonic timing
+     */
+    public static Instant now() {
+        return get().instant();
+    }
+
+    /**
+     * Returns a monotonically increasing instant.
+     * <p>
+     * The returned instant is calculated by adding the elapsed nanoseconds
+     * since clock creation to the initial wall clock time. This ensures that
+     * the time never goes backwards and maintains a consistent relationship
+     * with the wall clock time.
+     *
+     * @return the current instant using monotonic timing
+     */
+    @Override
+    public Instant instant() {
+        long elapsedNanos = System.nanoTime() - startNanos;
+        return startInstant.plusNanos(elapsedNanos);
+    }
+
+    /**
+     * Returns the zone ID of this clock, which is always UTC.
+     *
+     * @return the UTC zone ID
+     */
+    @Override
+    public ZoneId getZone() {
+        return ZoneOffset.UTC;
+    }
+
+    /**
+     * Returns this clock since timezone adjustments are not supported.
+     * <p>
+     * This implementation maintains UTC time to ensure monotonic behavior.
+     * The provided zone parameter is ignored.
+     *
+     * @param zone the target timezone (ignored)
+     * @return this clock instance
+     */
+    @Override
+    public Clock withZone(ZoneId zone) {
+        // Monotonic clock is always UTC-based
+        return this;
+    }
+}
diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java
index 97ac59cb24..4b986da8d3 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/ProtoSession.java
@@ -106,7 +106,7 @@ public interface ProtoSession {
      * Returns new builder from scratch.
      */
     static Builder newBuilder() {
-        return new Builder().withStartTime(Instant.now());
+        return new Builder().withStartTime(MonotonicClock.now());
     }
 
     class Builder {
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/event/ExecutionEventLogger.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/event/ExecutionEventLogger.java
index 5c401ba7e0..791f81a15e 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/event/ExecutionEventLogger.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/event/ExecutionEventLogger.java
@@ -20,9 +20,13 @@ package org.apache.maven.cling.event;
 
 import java.io.File;
 import java.nio.file.Path;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneId;
 import java.util.List;
 import java.util.Objects;
 
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.api.services.MessageBuilder;
 import org.apache.maven.api.services.MessageBuilderFactory;
 import org.apache.maven.execution.AbstractExecutionListener;
@@ -223,7 +227,7 @@ public class ExecutionEventLogger extends 
AbstractExecutionListener {
             } else if (buildSummary instanceof BuildSuccess) {
                 buffer.append(builder().success("SUCCESS"));
                 buffer.append(" [");
-                String buildTimeDuration = 
formatDuration(buildSummary.getTime());
+                String buildTimeDuration = 
formatDuration(buildSummary.getExecTime());
                 int padSize = MAX_PADDED_BUILD_TIME_DURATION_LENGTH - 
buildTimeDuration.length();
                 if (padSize > 0) {
                     buffer.append(chars(' ', padSize));
@@ -233,7 +237,7 @@ public class ExecutionEventLogger extends 
AbstractExecutionListener {
             } else if (buildSummary instanceof BuildFailure) {
                 buffer.append(builder().failure("FAILURE"));
                 buffer.append(" [");
-                String buildTimeDuration = 
formatDuration(buildSummary.getTime());
+                String buildTimeDuration = 
formatDuration(buildSummary.getExecTime());
                 int padSize = MAX_PADDED_BUILD_TIME_DURATION_LENGTH - 
buildTimeDuration.length();
                 if (padSize > 0) {
                     buffer.append(chars(' ', padSize));
@@ -266,15 +270,15 @@ public class ExecutionEventLogger extends 
AbstractExecutionListener {
     private void logStats(MavenSession session) {
         infoLine('-');
 
-        long finish = System.currentTimeMillis();
+        Instant finish = MonotonicClock.now();
 
-        long time = finish - session.getRequest().getStartTime().getTime();
+        Duration time = 
Duration.between(session.getRequest().getStartInstant(), finish);
 
         String wallClock = session.getRequest().getDegreeOfConcurrency() > 1 ? 
" (Wall Clock)" : "";
 
         logger.info("Total time:  {}{}", formatDuration(time), wallClock);
 
-        logger.info("Finished at: {}", formatTimestamp(finish));
+        logger.info("Finished at: {}", 
formatTimestamp(finish.atZone(ZoneId.systemDefault())));
     }
 
     @Override
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
index 667fc55623..6a6c2a9d27 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/invoker/mvn/MavenInvoker.java
@@ -22,7 +22,6 @@ import java.io.FileNotFoundException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -33,6 +32,7 @@ import java.util.regex.Pattern;
 import org.apache.maven.InternalErrorException;
 import org.apache.maven.Maven;
 import org.apache.maven.api.Constants;
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.api.cli.InvokerRequest;
 import org.apache.maven.api.cli.Logger;
 import org.apache.maven.api.cli.mvn.MavenOptions;
@@ -105,7 +105,7 @@ public abstract class MavenInvoker<C extends MavenContext> 
extends LookupInvoker
         mavenExecutionRequest.setIgnoreMissingArtifactDescriptor(true);
         mavenExecutionRequest.setRecursive(true);
         
mavenExecutionRequest.setReactorFailureBehavior(MavenExecutionRequest.REACTOR_FAIL_FAST);
-        mavenExecutionRequest.setStartTime(new Date());
+        mavenExecutionRequest.setStartInstant(MonotonicClock.now());
         
mavenExecutionRequest.setLoggingLevel(MavenExecutionRequest.LOGGING_LEVEL_INFO);
         mavenExecutionRequest.setDegreeOfConcurrency(1);
         mavenExecutionRequest.setBuilderId("singlethreaded");
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java
index b07abab37e..4cba621f55 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/AbstractMavenTransferListener.java
@@ -19,7 +19,10 @@
 package org.apache.maven.cling.transfer;
 
 import java.io.PrintWriter;
+import java.time.Duration;
+import java.time.Instant;
 
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.api.services.MessageBuilder;
 import org.apache.maven.api.services.MessageBuilderFactory;
 import org.eclipse.aether.transfer.AbstractTransferListener;
@@ -80,11 +83,12 @@ public abstract class AbstractMavenTransferListener extends 
AbstractTransferList
         message.resetStyle().append(resource.getResourceName());
         message.style(STYLE).append(" (").append(format.format(contentLength));
 
-        long duration = System.currentTimeMillis() - 
resource.getTransferStartTime();
-        if (duration > 0L) {
-            double bytesPerSecond = contentLength / (duration / 1000.0);
+        Duration duration =
+                
Duration.between(Instant.ofEpochMilli(resource.getTransferStartTime()), 
MonotonicClock.now());
+        if ((duration.getSeconds() | duration.getNano()) > 0) { // 
duration.isPositive()
+            long bytesPerSecond = Math.round(contentLength / (double) 
duration.toSeconds());
             message.append(" at ");
-            format.format(message, (long) bytesPerSecond);
+            format.format(message, bytesPerSecond);
             message.append("/s");
         }
 
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/Slf4jMavenTransferListener.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/Slf4jMavenTransferListener.java
index b5ab1b6574..7db7403e91 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/Slf4jMavenTransferListener.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/transfer/Slf4jMavenTransferListener.java
@@ -18,6 +18,10 @@
  */
 package org.apache.maven.cling.transfer;
 
+import java.time.Duration;
+import java.time.Instant;
+
+import org.apache.maven.api.MonotonicClock;
 import org.eclipse.aether.transfer.AbstractTransferListener;
 import org.eclipse.aether.transfer.TransferCancelledException;
 import org.eclipse.aether.transfer.TransferEvent;
@@ -83,11 +87,12 @@ public class Slf4jMavenTransferListener extends 
AbstractTransferListener {
                 .append(" (");
         format.format(message, contentLength);
 
-        long duration = System.currentTimeMillis() - 
resource.getTransferStartTime();
-        if (duration > 0L) {
-            double bytesPerSecond = contentLength / (duration / 1000.0);
+        Duration duration =
+                
Duration.between(Instant.ofEpochMilli(resource.getTransferStartTime()), 
MonotonicClock.now());
+        if ((duration.getSeconds() | duration.getNano()) > 0) { // 
duration.isPositive()
+            long bytesPerSecond = Math.round(contentLength / (double) 
duration.toSeconds());
             message.append(" at ");
-            format.format(message, (long) bytesPerSecond);
+            format.format(message, bytesPerSecond);
             message.append("/s");
         }
 
diff --git 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/utils/CLIReportingUtils.java
 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/utils/CLIReportingUtils.java
index d18f0a7c18..3af1415497 100644
--- 
a/impl/maven-cli/src/main/java/org/apache/maven/cling/utils/CLIReportingUtils.java
+++ 
b/impl/maven-cli/src/main/java/org/apache/maven/cling/utils/CLIReportingUtils.java
@@ -20,8 +20,9 @@ package org.apache.maven.cling.utils;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.time.Duration;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.TemporalAccessor;
 import java.util.Locale;
 import java.util.Properties;
 
@@ -105,23 +106,13 @@ public final class CLIReportingUtils {
      * @return Readable build info
      */
     public static String createMavenVersionString(Properties buildProperties) {
-        String timestamp = reduce(buildProperties.getProperty("timestamp"));
         String version = 
reduce(buildProperties.getProperty(BUILD_VERSION_PROPERTY));
         String rev = reduce(buildProperties.getProperty("buildNumber"));
         String distributionName = 
reduce(buildProperties.getProperty("distributionName"));
 
-        String msg = distributionName + " ";
-        msg += (version != null ? version : "<version unknown>");
-        if (rev != null || timestamp != null) {
-            msg += " (";
-            msg += (rev != null ? rev : "");
-            if (timestamp != null && !timestamp.isEmpty()) {
-                String ts = formatTimestamp(Long.parseLong(timestamp));
-                msg += (rev != null ? "; " : "") + ts;
-            }
-            msg += ")";
-        }
-        return msg;
+        return distributionName + " "
+                + (version != null ? version : "<version unknown>")
+                + (rev != null ? " (" + rev + ")" : "");
     }
 
     private static String reduce(String s) {
@@ -169,35 +160,25 @@ public final class CLIReportingUtils {
         }
     }
 
-    public static String formatTimestamp(long timestamp) {
-        SimpleDateFormat sdf = new 
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
-        return sdf.format(new Date(timestamp));
+    public static String formatTimestamp(TemporalAccessor instant) {
+        return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(instant);
     }
 
-    public static String formatDuration(long duration) {
-        // CHECKSTYLE_OFF: MagicNumber
-        long ms = duration % 1000;
-        long s = (duration / ONE_SECOND) % 60;
-        long m = (duration / ONE_MINUTE) % 60;
-        long h = (duration / ONE_HOUR) % 24;
-        long d = duration / ONE_DAY;
-        // CHECKSTYLE_ON: MagicNumber
-
-        String format;
-        if (d > 0) {
-            // Length 11+ chars
-            format = "%d d %02d:%02d h";
-        } else if (h > 0) {
-            // Length 7 chars
-            format = "%2$02d:%3$02d h";
-        } else if (m > 0) {
-            // Length 9 chars
-            format = "%3$02d:%4$02d min";
+    public static String formatDuration(Duration duration) {
+        long days = duration.toDays();
+        long hours = duration.toHoursPart();
+        long minutes = duration.toMinutesPart();
+        long seconds = duration.toSecondsPart();
+        long millis = duration.toMillisPart();
+
+        if (days > 0) {
+            return String.format("%d d %02d:%02d h", days, hours, minutes);
+        } else if (hours > 0) {
+            return String.format("%02d:%02d h", hours, minutes);
+        } else if (minutes > 0) {
+            return String.format("%02d:%02d min", minutes, seconds);
         } else {
-            // Length 7-8 chars
-            format = "%4$d.%5$03d s";
+            return String.format("%d.%03d s", seconds, millis);
         }
-
-        return String.format(format, d, h, m, s, ms);
     }
 }
diff --git a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java 
b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java
index 0c24c22c02..41d87dd85e 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/DefaultMaven.java
@@ -28,7 +28,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
@@ -39,6 +38,7 @@ import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Stream;
 
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.api.Session;
 import org.apache.maven.api.model.Model;
 import org.apache.maven.api.model.Prerequisites;
@@ -194,7 +194,7 @@ public class DefaultMaven implements Maven {
     //
     @SuppressWarnings("checkstyle:methodlength")
     private MavenExecutionResult doExecute(MavenExecutionRequest request) {
-        request.setStartTime(new Date());
+        request.setStartInstant(MonotonicClock.now());
 
         MavenExecutionResult result = new DefaultMavenExecutionResult();
 
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/execution/BuildFailure.java 
b/impl/maven-core/src/main/java/org/apache/maven/execution/BuildFailure.java
index 2c43f1e943..dcde525f58 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/execution/BuildFailure.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/execution/BuildFailure.java
@@ -18,6 +18,8 @@
  */
 package org.apache.maven.execution;
 
+import java.time.Duration;
+
 import org.apache.maven.project.MavenProject;
 
 /**
@@ -39,6 +41,17 @@ public class BuildFailure extends BuildSummary {
      * @param cause The cause of the build failure, may be {@code null}.
      */
     public BuildFailure(MavenProject project, long time, Throwable cause) {
+        this(project, Duration.ofMillis(time), cause);
+    }
+
+    /**
+     * Creates a new build summary for the specified project.
+     *
+     * @param project The project being summarized, must not be {@code null}.
+     * @param time The build time of the project in milliseconds.
+     * @param cause The cause of the build failure, may be {@code null}.
+     */
+    public BuildFailure(MavenProject project, Duration time, Throwable cause) {
         this(project, time, time, cause);
     }
 
@@ -50,7 +63,7 @@ public class BuildFailure extends BuildSummary {
      * @param wallTime The wall time of the project in milliseconds.
      * @param cause The cause of the build failure, may be {@code null}.
      */
-    public BuildFailure(MavenProject project, long execTime, long wallTime, 
Throwable cause) {
+    public BuildFailure(MavenProject project, Duration execTime, Duration 
wallTime, Throwable cause) {
         super(project, execTime, wallTime);
         this.cause = cause;
     }
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/execution/BuildSuccess.java 
b/impl/maven-core/src/main/java/org/apache/maven/execution/BuildSuccess.java
index a2a4546b23..34dc8dbdd3 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/execution/BuildSuccess.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/execution/BuildSuccess.java
@@ -18,6 +18,8 @@
  */
 package org.apache.maven.execution;
 
+import java.time.Duration;
+
 import org.apache.maven.project.MavenProject;
 
 /**
@@ -33,7 +35,17 @@ public class BuildSuccess extends BuildSummary {
      * @param time The build time of the project in milliseconds.
      */
     public BuildSuccess(MavenProject project, long time) {
-        super(project, time, time);
+        this(project, Duration.ofMillis(time));
+    }
+
+    /**
+     * Creates a new build summary for the specified project.
+     *
+     * @param project The project being summarized, must not be {@code null}.
+     * @param time The build time of the project in milliseconds.
+     */
+    public BuildSuccess(MavenProject project, Duration time) {
+        this(project, time, time);
     }
 
     /**
@@ -43,7 +55,7 @@ public class BuildSuccess extends BuildSummary {
      * @param wallTime The wall time of the project in milliseconds.
      * @param execTime The exec time of the project in milliseconds.
      */
-    public BuildSuccess(MavenProject project, long wallTime, long execTime) {
+    public BuildSuccess(MavenProject project, Duration wallTime, Duration 
execTime) {
         super(project, wallTime, execTime);
     }
 }
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/execution/BuildSummary.java 
b/impl/maven-core/src/main/java/org/apache/maven/execution/BuildSummary.java
index efc0da1d8b..0f6c37bedc 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/execution/BuildSummary.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/execution/BuildSummary.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.execution;
 
+import java.time.Duration;
 import java.util.Objects;
 
 import org.apache.maven.project.MavenProject;
@@ -36,12 +37,12 @@ public abstract class BuildSummary {
     /**
      * The build time of the project in milliseconds.
      */
-    private final long wallTime;
+    private final Duration wallTime;
 
     /**
      * The total amount of time spent for to run mojos in milliseconds.
      */
-    private final long execTime;
+    private final Duration execTime;
 
     /**
      * Creates a new build summary for the specified project.
@@ -50,7 +51,7 @@ public abstract class BuildSummary {
      * @param time The build time of the project in milliseconds.
      */
     protected BuildSummary(MavenProject project, long time) {
-        this(project, time, time);
+        this(project, Duration.ofMillis(time), Duration.ofMillis(time));
     }
 
     /**
@@ -60,7 +61,7 @@ public abstract class BuildSummary {
      * @param execTime The exec time of the project in milliseconds.
      * @param wallTime The wall time of the project in milliseconds.
      */
-    protected BuildSummary(MavenProject project, long execTime, long wallTime) 
{
+    protected BuildSummary(MavenProject project, Duration execTime, Duration 
wallTime) {
         this.project = Objects.requireNonNull(project, "project cannot be 
null");
         // TODO Validate for < 0?
         this.execTime = execTime;
@@ -82,15 +83,24 @@ public abstract class BuildSummary {
      * @return The wall time of the project in milliseconds.
      */
     public long getTime() {
-        return execTime;
+        return execTime.toMillis();
     }
 
     /**
-     * Gets the exec time of the project in milliseconds.
+     * Gets the wall time of the project.
      *
-     * @return The exec time of the project in milliseconds.
+     * @return The wall time of the project.
      */
-    public long getWallTime() {
+    public Duration getWallTime() {
         return wallTime;
     }
+
+    /**
+     * Gets the exec time of the project.
+     *
+     * @return The exec time of the project.
+     */
+    public Duration getExecTime() {
+        return execTime;
+    }
 }
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java
 
b/impl/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java
index aa9748ca51..eedfe23016 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/execution/DefaultMavenExecutionRequest.java
@@ -20,6 +20,7 @@ package org.apache.maven.execution;
 
 import java.io.File;
 import java.nio.file.Path;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -28,6 +29,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Properties;
 
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.eventspy.internal.EventSpyDispatcher;
 import org.apache.maven.model.Profile;
@@ -135,7 +137,7 @@ public class DefaultMavenExecutionRequest implements 
MavenExecutionRequest {
 
     private Properties userProperties;
 
-    private Date startTime = new Date();
+    private Instant startTime = MonotonicClock.now();
 
     private boolean showErrors = false;
 
@@ -214,7 +216,7 @@ public class DefaultMavenExecutionRequest implements 
MavenExecutionRequest {
         copy.setExecutionListener(original.getExecutionListener());
         
copy.setUseLegacyLocalRepository(original.isUseLegacyLocalRepository());
         copy.setBuilderId(original.getBuilderId());
-        copy.setStartTime(original.getStartTime());
+        copy.setStartInstant(original.getStartInstant());
         return copy;
     }
 
@@ -299,10 +301,22 @@ public class DefaultMavenExecutionRequest implements 
MavenExecutionRequest {
     }
 
     @Override
+    @Deprecated
     public Date getStartTime() {
+        return new Date(startTime.toEpochMilli());
+    }
+
+    @Override
+    public Instant getStartInstant() {
         return startTime;
     }
 
+    @Override
+    public MavenExecutionRequest setStartInstant(Instant startTime) {
+        this.startTime = startTime;
+        return this;
+    }
+
     @Override
     public boolean isShowErrors() {
         return showErrors;
@@ -423,9 +437,10 @@ public class DefaultMavenExecutionRequest implements 
MavenExecutionRequest {
         return this;
     }
 
+    @Deprecated
     @Override
     public MavenExecutionRequest setStartTime(Date startTime) {
-        this.startTime = startTime;
+        this.startTime = Instant.ofEpochMilli(startTime.getTime());
 
         return this;
     }
@@ -981,7 +996,7 @@ public class DefaultMavenExecutionRequest implements 
MavenExecutionRequest {
             
projectBuildingRequest.setInactiveProfileIds(getInactiveProfiles());
             projectBuildingRequest.setProfiles(getProfiles());
             projectBuildingRequest.setProcessPlugins(true);
-            projectBuildingRequest.setBuildStartTime(getStartTime());
+            projectBuildingRequest.setBuildStartInstant(getStartInstant());
         }
 
         return projectBuildingRequest;
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java
 
b/impl/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java
index f95f48b26c..baf0080177 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/execution/MavenExecutionRequest.java
@@ -20,6 +20,7 @@ package org.apache.maven.execution;
 
 import java.io.File;
 import java.nio.file.Path;
+import java.time.Instant;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -105,10 +106,16 @@ public interface MavenExecutionRequest {
     String getBaseDirectory();
 
     // Timing (remove this)
+    @Deprecated
     MavenExecutionRequest setStartTime(Date start);
 
+    @Deprecated
     Date getStartTime();
 
+    MavenExecutionRequest setStartInstant(Instant start);
+
+    Instant getStartInstant();
+
     // Goals
     MavenExecutionRequest setGoals(List<String> goals);
 
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java 
b/impl/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java
index 5d118ff7dd..8fa6e4098a 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/execution/MavenSession.java
@@ -20,6 +20,7 @@ package org.apache.maven.execution;
 
 import java.io.File;
 import java.nio.file.Path;
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
@@ -245,10 +246,15 @@ public class MavenSession implements Cloneable {
         }
     }
 
+    @Deprecated
     public Date getStartTime() {
         return request.getStartTime();
     }
 
+    public Instant getStartInstant() {
+        return request.getStartInstant();
+    }
+
     public boolean isParallel() {
         return parallel;
     }
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java 
b/impl/maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java
index 2ac0643135..bc02255c4f 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/execution/ReactorManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.execution;
 
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -123,6 +124,10 @@ public class ReactorManager {
         buildFailuresByProject.put(getProjectKey(project), new 
BuildFailure(project, time, error));
     }
 
+    public void registerBuildFailure(MavenProject project, Exception error, 
String task, Duration time) {
+        buildFailuresByProject.put(getProjectKey(project), new 
BuildFailure(project, time, error));
+    }
+
     public boolean hasBuildFailures() {
         return !buildFailuresByProject.isEmpty();
     }
@@ -147,6 +152,10 @@ public class ReactorManager {
         buildSuccessesByProject.put(getProjectKey(project), new 
BuildSuccess(project, time));
     }
 
+    public void registerBuildSuccess(MavenProject project, Duration time) {
+        buildSuccessesByProject.put(getProjectKey(project), new 
BuildSuccess(project, time));
+    }
+
     public BuildFailure getBuildFailure(MavenProject project) {
         return buildFailuresByProject.get(getProjectKey(project));
     }
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
 
b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
index 235b68d222..371c345f3d 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/DefaultRepositorySystemSessionFactory.java
@@ -168,6 +168,7 @@ public class DefaultRepositorySystemSessionFactory 
implements RepositorySystemSe
         configProps.put(ConfigurationProperties.USER_AGENT, getUserAgent());
         configProps.put(ConfigurationProperties.INTERACTIVE, 
request.isInteractiveMode());
         configProps.put("maven.startTime", request.getStartTime());
+        configProps.put(Constants.MAVEN_START_INSTANT, 
request.getStartInstant());
 
         sessionBuilder.setOffline(request.isOffline());
         sessionBuilder.setChecksumPolicy(request.getGlobalChecksumPolicy());
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
 
b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
index a931b9874b..27c30927f0 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
@@ -146,7 +146,7 @@ public class DefaultSession extends AbstractSession 
implements InternalMavenSess
     @Nonnull
     @Override
     public Instant getStartTime() {
-        return getMavenSession().getRequest().getStartTime().toInstant();
+        return getMavenSession().getRequest().getStartInstant();
     }
 
     @Override
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
 
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
index 5450e16ea5..0994baf006 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/LifecycleModuleBuilder.java
@@ -22,9 +22,12 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import java.time.Duration;
+import java.time.Instant;
 import java.util.HashSet;
 import java.util.List;
 
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.execution.BuildSuccess;
 import org.apache.maven.execution.ExecutionEvent;
 import org.apache.maven.execution.MavenSession;
@@ -81,7 +84,7 @@ public class LifecycleModuleBuilder {
             TaskSegment taskSegment) {
         session.setCurrentProject(currentProject);
 
-        long buildStartTime = System.currentTimeMillis();
+        Instant buildStartTime = MonotonicClock.now();
 
         try {
 
@@ -106,12 +109,14 @@ public class LifecycleModuleBuilder {
                     new ProjectExecutionEvent(session, currentProject, 
mojoExecutions));
             mojoExecutor.execute(session, mojoExecutions);
 
-            long buildEndTime = System.currentTimeMillis();
+            Instant buildEndTime = MonotonicClock.now();
 
             projectExecutionListener.afterProjectExecutionSuccess(
                     new ProjectExecutionEvent(session, currentProject, 
mojoExecutions));
 
-            reactorContext.getResult().addBuildSummary(new 
BuildSuccess(currentProject, buildEndTime - buildStartTime));
+            reactorContext
+                    .getResult()
+                    .addBuildSummary(new BuildSuccess(currentProject, 
Duration.between(buildStartTime, buildEndTime)));
 
             eventCatapult.fire(ExecutionEvent.Type.ProjectSucceeded, session, 
null);
         } catch (Throwable t) {
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
 
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
index e1c2b5b195..b683a06cdb 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/builder/BuilderCommon.java
@@ -22,9 +22,12 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import java.time.Duration;
+import java.time.Instant;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.execution.BuildFailure;
 import org.apache.maven.execution.ExecutionEvent;
@@ -164,11 +167,13 @@ public class BuilderCommon {
             final MavenSession currentSession,
             final MavenProject mavenProject,
             Throwable t,
-            final long buildStartTime) {
+            final Instant buildStartTime) {
         // record the error and mark the project as failed
-        long buildEndTime = System.currentTimeMillis();
+        Instant buildEndTime = MonotonicClock.now();
         buildContext.getResult().addException(t);
-        buildContext.getResult().addBuildSummary(new 
BuildFailure(mavenProject, buildEndTime - buildStartTime, t));
+        buildContext
+                .getResult()
+                .addBuildSummary(new BuildFailure(mavenProject, 
Duration.between(buildStartTime, buildEndTime), t));
 
         // notify listeners about "soft" project build failures only
         if (t instanceof Exception && !(t instanceof RuntimeException)) {
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
 
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
index 3134ac94e7..ccc16724a1 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/concurrent/BuildPlanExecutor.java
@@ -23,6 +23,8 @@ import javax.inject.Named;
 import javax.xml.stream.XMLStreamException;
 
 import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -34,13 +36,13 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.apache.maven.api.Lifecycle;
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.api.services.LifecycleRegistry;
 import org.apache.maven.api.services.MavenException;
 import org.apache.maven.api.xml.XmlNode;
@@ -905,31 +907,31 @@ public class BuildPlanExecutor {
     }
 
     protected static class Clock {
-        long start;
-        long end;
-        long resumed;
-        long exec;
+        Instant start;
+        Instant end;
+        Instant resumed;
+        Duration exec = Duration.ZERO;
 
         protected void start() {
-            if (start == 0) {
-                start = System.nanoTime();
+            if (start == null) {
+                start = MonotonicClock.now();
                 resumed = start;
             } else {
-                resumed = System.nanoTime();
+                resumed = MonotonicClock.now();
             }
         }
 
         protected void stop() {
-            end = System.nanoTime();
-            exec += end - resumed;
+            end = MonotonicClock.now();
+            exec = exec.plus(Duration.between(resumed, end));
         }
 
-        protected long wallTime() {
-            return TimeUnit.NANOSECONDS.toMillis(end - start);
+        protected Duration wallTime() {
+            return Duration.between(start, end);
         }
 
-        protected long execTime() {
-            return TimeUnit.NANOSECONDS.toMillis(exec);
+        protected Duration execTime() {
+            return exec;
         }
     }
 }
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingRequest.java
 
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingRequest.java
index 8f18435478..dbf6611b0f 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingRequest.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/project/DefaultProjectBuildingRequest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.project;
 
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -59,7 +60,7 @@ public class DefaultProjectBuildingRequest implements 
ProjectBuildingRequest {
 
     private Properties userProperties;
 
-    private Date buildStartTime;
+    private Instant buildStartTime;
 
     private boolean resolveDependencies;
 
@@ -261,11 +262,21 @@ public class DefaultProjectBuildingRequest implements 
ProjectBuildingRequest {
         return profiles;
     }
 
+    @Deprecated
     public Date getBuildStartTime() {
-        return buildStartTime;
+        return buildStartTime != null ? new 
Date(buildStartTime.toEpochMilli()) : null;
     }
 
+    @Deprecated
     public void setBuildStartTime(Date buildStartTime) {
+        setBuildStartInstant(buildStartTime != null ? 
Instant.ofEpochMilli(buildStartTime.getTime()) : null);
+    }
+
+    public Instant getBuildStartInstant() {
+        return this.buildStartTime;
+    }
+
+    public void setBuildStartInstant(Instant buildStartTime) {
         this.buildStartTime = buildStartTime;
     }
 
diff --git 
a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingRequest.java
 
b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingRequest.java
index 7d3cae86af..c2f8fb3bf3 100644
--- 
a/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingRequest.java
+++ 
b/impl/maven-core/src/main/java/org/apache/maven/project/ProjectBuildingRequest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.maven.project;
 
+import java.time.Instant;
 import java.util.Date;
 import java.util.List;
 import java.util.Properties;
@@ -138,6 +139,7 @@ public interface ProjectBuildingRequest {
      *
      * @return The start time of the build or {@code null} if unknown.
      */
+    @Deprecated
     Date getBuildStartTime();
 
     /**
@@ -145,8 +147,23 @@ public interface ProjectBuildingRequest {
      *
      * @param buildStartTime The start time of the build, may be {@code null}.
      */
+    @Deprecated
     void setBuildStartTime(Date buildStartTime);
 
+    /**
+     * Gets the start time of the build.
+     *
+     * @return The start time of the build or {@code null} if unknown.
+     */
+    Instant getBuildStartInstant();
+
+    /**
+     * Sets the start time of the build.
+     *
+     * @param buildStartInstant The start time of the build, may be {@code 
null}.
+     */
+    void setBuildStartInstant(Instant buildStartInstant);
+
     RepositorySystemSession getRepositorySession();
 
     ProjectBuildingRequest setRepositorySession(RepositorySystemSession 
repositorySession);
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/MavenBuildTimestamp.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/MavenBuildTimestamp.java
index 3ee89cedd8..412a8b53a9 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/MavenBuildTimestamp.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/model/MavenBuildTimestamp.java
@@ -18,15 +18,14 @@
  */
 package org.apache.maven.internal.impl.model;
 
-import java.text.SimpleDateFormat;
 import java.time.Instant;
-import java.util.Date;
-import java.util.GregorianCalendar;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.Map;
 import java.util.Properties;
-import java.util.TimeZone;
 
 import org.apache.maven.api.Constants;
+import org.apache.maven.api.MonotonicClock;
 
 /**
  * MavenBuildTimestamp
@@ -35,12 +34,10 @@ public class MavenBuildTimestamp {
     // ISO 8601-compliant timestamp for machine readability
     public static final String DEFAULT_BUILD_TIMESTAMP_FORMAT = 
"yyyy-MM-dd'T'HH:mm:ssXXX";
 
-    public static final TimeZone DEFAULT_BUILD_TIME_ZONE = 
TimeZone.getTimeZone("Etc/UTC");
-
     private final String formattedTimestamp;
 
     public MavenBuildTimestamp() {
-        this(Instant.now());
+        this(MonotonicClock.now());
     }
 
     public MavenBuildTimestamp(Instant time) {
@@ -66,12 +63,11 @@ public class MavenBuildTimestamp {
             timestampFormat = DEFAULT_BUILD_TIMESTAMP_FORMAT;
         }
         if (time == null) {
-            time = Instant.now();
+            time = MonotonicClock.now();
         }
-        SimpleDateFormat dateFormat = new SimpleDateFormat(timestampFormat);
-        dateFormat.setCalendar(new GregorianCalendar());
-        dateFormat.setTimeZone(DEFAULT_BUILD_TIME_ZONE);
-        formattedTimestamp = dateFormat.format(new Date(time.toEpochMilli()));
+        DateTimeFormatter formatter =
+                
DateTimeFormatter.ofPattern(timestampFormat).withZone(ZoneId.of("UTC"));
+        formattedTimestamp = formatter.format(time);
     }
 
     public String formattedTimestamp() {
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/LocalSnapshotMetadata.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/LocalSnapshotMetadata.java
index 511a2246b4..51e71f9f65 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/LocalSnapshotMetadata.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/LocalSnapshotMetadata.java
@@ -20,9 +20,9 @@ package org.apache.maven.internal.impl.resolver;
 
 import java.io.File;
 import java.nio.file.Path;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
@@ -39,11 +39,11 @@ final class LocalSnapshotMetadata extends MavenMetadata {
 
     private final Collection<Artifact> artifacts = new ArrayList<>();
 
-    LocalSnapshotMetadata(Artifact artifact, Date timestamp) {
+    LocalSnapshotMetadata(Artifact artifact, Instant timestamp) {
         super(createMetadata(artifact), (Path) null, timestamp);
     }
 
-    LocalSnapshotMetadata(Metadata metadata, Path path, Date timestamp) {
+    LocalSnapshotMetadata(Metadata metadata, Path path, Instant timestamp) {
         super(metadata, path, timestamp);
     }
 
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/LocalSnapshotMetadataGenerator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/LocalSnapshotMetadataGenerator.java
index e122812e2e..9d8eca5133 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/LocalSnapshotMetadataGenerator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/LocalSnapshotMetadataGenerator.java
@@ -18,12 +18,14 @@
  */
 package org.apache.maven.internal.impl.resolver;
 
+import java.time.Instant;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import org.apache.maven.api.Constants;
+import org.apache.maven.api.MonotonicClock;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.impl.MetadataGenerator;
@@ -40,10 +42,10 @@ class LocalSnapshotMetadataGenerator implements 
MetadataGenerator {
 
     private final Map<Object, LocalSnapshotMetadata> snapshots;
 
-    private final Date timestamp;
+    private final Instant timestamp;
 
     LocalSnapshotMetadataGenerator(RepositorySystemSession session, 
InstallRequest request) {
-        timestamp = (Date) ConfigUtils.getObject(session, new Date(), 
"maven.startTime");
+        timestamp = (Instant) ConfigUtils.getObject(session, 
MonotonicClock.now(), Constants.MAVEN_START_INSTANT);
 
         snapshots = new LinkedHashMap<>();
     }
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/MavenMetadata.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/MavenMetadata.java
index 0d8e653119..16e653e418 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/MavenMetadata.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/MavenMetadata.java
@@ -26,12 +26,11 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
 import java.util.Collections;
-import java.util.Date;
 import java.util.Map;
-import java.util.TimeZone;
 
 import org.apache.maven.api.metadata.Metadata;
 import org.apache.maven.metadata.v4.MetadataStaxReader;
@@ -46,28 +45,26 @@ abstract class MavenMetadata extends AbstractMetadata 
implements MergeableMetada
 
     static final String MAVEN_METADATA_XML = "maven-metadata.xml";
 
-    static DateFormat fmt;
+    static DateTimeFormatter fmt;
 
     static {
-        TimeZone timezone = TimeZone.getTimeZone("UTC");
-        fmt = new SimpleDateFormat("yyyyMMddHHmmss");
-        fmt.setTimeZone(timezone);
+        fmt = 
DateTimeFormatter.ofPattern("yyyyMMddHHmmss").withZone(ZoneOffset.UTC);
     }
 
     protected Metadata metadata;
 
     private final Path path;
 
-    protected final Date timestamp;
+    protected final Instant timestamp;
 
     private boolean merged;
 
     @Deprecated
-    protected MavenMetadata(Metadata metadata, File file, Date timestamp) {
+    protected MavenMetadata(Metadata metadata, File file, Instant timestamp) {
         this(metadata, file != null ? file.toPath() : null, timestamp);
     }
 
-    protected MavenMetadata(Metadata metadata, Path path, Date timestamp) {
+    protected MavenMetadata(Metadata metadata, Path path, Instant timestamp) {
         this.metadata = metadata;
         this.path = path;
         this.timestamp = timestamp;
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/MavenSnapshotMetadata.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/MavenSnapshotMetadata.java
index 4aa0ba526c..13c3b163da 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/MavenSnapshotMetadata.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/MavenSnapshotMetadata.java
@@ -19,9 +19,9 @@
 package org.apache.maven.internal.impl.resolver;
 
 import java.nio.file.Path;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Date;
 
 import org.apache.maven.api.metadata.Metadata;
 import org.eclipse.aether.artifact.Artifact;
@@ -33,7 +33,7 @@ abstract class MavenSnapshotMetadata extends MavenMetadata {
 
     protected final Collection<Artifact> artifacts = new ArrayList<>();
 
-    protected MavenSnapshotMetadata(Metadata metadata, Path path, Date 
timestamp) {
+    protected MavenSnapshotMetadata(Metadata metadata, Path path, Instant 
timestamp) {
         super(metadata, path, timestamp);
     }
 
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/PluginsMetadata.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/PluginsMetadata.java
index 260c0c4db2..a019d26390 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/PluginsMetadata.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/PluginsMetadata.java
@@ -20,7 +20,7 @@ package org.apache.maven.internal.impl.resolver;
 
 import java.io.File;
 import java.nio.file.Path;
-import java.util.Date;
+import java.time.Instant;
 import java.util.LinkedHashMap;
 import java.util.List;
 
@@ -56,12 +56,12 @@ final class PluginsMetadata extends MavenMetadata {
 
     private final PluginInfo pluginInfo;
 
-    PluginsMetadata(PluginInfo pluginInfo, Date timestamp) {
+    PluginsMetadata(PluginInfo pluginInfo, Instant timestamp) {
         super(createRepositoryMetadata(pluginInfo), (Path) null, timestamp);
         this.pluginInfo = pluginInfo;
     }
 
-    PluginsMetadata(PluginInfo pluginInfo, Path path, Date timestamp) {
+    PluginsMetadata(PluginInfo pluginInfo, Path path, Instant timestamp) {
         super(createRepositoryMetadata(pluginInfo), path, timestamp);
         this.pluginInfo = pluginInfo;
     }
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/PluginsMetadataGenerator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/PluginsMetadataGenerator.java
index bdc4bf62b2..c309ab6943 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/PluginsMetadataGenerator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/PluginsMetadataGenerator.java
@@ -21,9 +21,9 @@ package org.apache.maven.internal.impl.resolver;
 import java.io.InputStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.time.Instant;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
@@ -31,6 +31,8 @@ import java.util.Objects;
 import java.util.jar.JarFile;
 import java.util.zip.ZipEntry;
 
+import org.apache.maven.api.Constants;
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.api.xml.XmlNode;
 import org.apache.maven.internal.impl.resolver.PluginsMetadata.PluginInfo;
 import org.apache.maven.internal.xml.XmlNodeStaxBuilder;
@@ -56,7 +58,7 @@ class PluginsMetadataGenerator implements MetadataGenerator {
 
     private final Map<Object, PluginsMetadata> processedPlugins;
 
-    private final Date timestamp;
+    private final Instant timestamp;
 
     PluginsMetadataGenerator(RepositorySystemSession session, InstallRequest 
request) {
         this(session, request.getMetadata());
@@ -68,7 +70,7 @@ class PluginsMetadataGenerator implements MetadataGenerator {
 
     private PluginsMetadataGenerator(RepositorySystemSession session, 
Collection<? extends Metadata> metadatas) {
         this.processedPlugins = new LinkedHashMap<>();
-        this.timestamp = (Date) ConfigUtils.getObject(session, new Date(), 
"maven.startTime");
+        this.timestamp = (Instant) ConfigUtils.getObject(session, 
MonotonicClock.now(), Constants.MAVEN_START_INSTANT);
 
         /*
          * NOTE: This should be considered a quirk to support interop with 
Maven's legacy ArtifactDeployer which
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/RemoteSnapshotMetadata.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/RemoteSnapshotMetadata.java
index 2365cb2482..7eb841a06a 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/RemoteSnapshotMetadata.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/RemoteSnapshotMetadata.java
@@ -20,14 +20,12 @@ package org.apache.maven.internal.impl.resolver;
 
 import java.io.File;
 import java.nio.file.Path;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
-import java.util.Date;
-import java.util.GregorianCalendar;
 import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.TimeZone;
 
 import org.apache.maven.api.metadata.Metadata;
 import org.apache.maven.api.metadata.Snapshot;
@@ -41,18 +39,16 @@ import org.eclipse.aether.artifact.Artifact;
 final class RemoteSnapshotMetadata extends MavenSnapshotMetadata {
     public static final String DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT = 
"yyyyMMdd.HHmmss";
 
-    public static final TimeZone DEFAULT_SNAPSHOT_TIME_ZONE = 
TimeZone.getTimeZone("Etc/UTC");
-
     private final Map<String, SnapshotVersion> versions = new 
LinkedHashMap<>();
 
     private final Integer buildNumber;
 
-    RemoteSnapshotMetadata(Artifact artifact, Date timestamp, Integer 
buildNumber) {
+    RemoteSnapshotMetadata(Artifact artifact, Instant timestamp, Integer 
buildNumber) {
         super(createRepositoryMetadata(artifact), null, timestamp);
         this.buildNumber = buildNumber;
     }
 
-    private RemoteSnapshotMetadata(Metadata metadata, Path path, Date 
timestamp, Integer buildNumber) {
+    private RemoteSnapshotMetadata(Metadata metadata, Path path, Instant 
timestamp, Integer buildNumber) {
         super(metadata, path, timestamp);
         this.buildNumber = buildNumber;
     }
@@ -79,13 +75,11 @@ final class RemoteSnapshotMetadata extends 
MavenSnapshotMetadata {
         String lastUpdated;
 
         if (metadata.getVersioning() == null) {
-            DateFormat utcDateFormatter = new 
SimpleDateFormat(DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT);
-            utcDateFormatter.setCalendar(new GregorianCalendar());
-            utcDateFormatter.setTimeZone(DEFAULT_SNAPSHOT_TIME_ZONE);
+            DateTimeFormatter utcDateFormatter = 
DateTimeFormatter.ofPattern(DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT);
 
             snapshot = Snapshot.newBuilder()
                     .buildNumber(buildNumber != null ? buildNumber : 
getBuildNumber(recessive) + 1)
-                    .timestamp(utcDateFormatter.format(this.timestamp))
+                    
.timestamp(utcDateFormatter.format(this.timestamp.atZone(ZoneOffset.UTC)))
                     .build();
 
             lastUpdated = fmt.format(timestamp);
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/RemoteSnapshotMetadataGenerator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/RemoteSnapshotMetadataGenerator.java
index 102e67eaf3..c2e81b941b 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/RemoteSnapshotMetadataGenerator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/RemoteSnapshotMetadataGenerator.java
@@ -18,13 +18,14 @@
  */
 package org.apache.maven.internal.impl.resolver;
 
+import java.time.Instant;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
 import org.apache.maven.api.Constants;
+import org.apache.maven.api.MonotonicClock;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.deployment.DeployRequest;
@@ -41,12 +42,12 @@ class RemoteSnapshotMetadataGenerator implements 
MetadataGenerator {
 
     private final Map<Object, RemoteSnapshotMetadata> snapshots;
 
-    private final Date timestamp;
+    private final Instant timestamp;
 
     private final Integer buildNumber;
 
     RemoteSnapshotMetadataGenerator(RepositorySystemSession session, 
DeployRequest request) {
-        timestamp = (Date) ConfigUtils.getObject(session, new Date(), 
"maven.startTime");
+        timestamp = (Instant) ConfigUtils.getObject(session, 
MonotonicClock.now(), Constants.MAVEN_START_INSTANT);
         Object bn = ConfigUtils.getObject(session, null, 
Constants.MAVEN_DEPLOY_SNAPSHOT_BUILD_NUMBER);
         if (bn instanceof Integer) {
             this.buildNumber = (Integer) bn;
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/VersionsMetadata.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/VersionsMetadata.java
index 219550e0ba..b55342dca6 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/VersionsMetadata.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/VersionsMetadata.java
@@ -20,9 +20,9 @@ package org.apache.maven.internal.impl.resolver;
 
 import java.io.File;
 import java.nio.file.Path;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Date;
 import java.util.LinkedHashSet;
 import java.util.List;
 
@@ -38,12 +38,12 @@ final class VersionsMetadata extends MavenMetadata {
 
     private final Artifact artifact;
 
-    VersionsMetadata(Artifact artifact, Date timestamp) {
+    VersionsMetadata(Artifact artifact, Instant timestamp) {
         super(createRepositoryMetadata(artifact), (Path) null, timestamp);
         this.artifact = artifact;
     }
 
-    VersionsMetadata(Artifact artifact, Path path, Date timestamp) {
+    VersionsMetadata(Artifact artifact, Path path, Instant timestamp) {
         super(createRepositoryMetadata(artifact), path, timestamp);
         this.artifact = artifact;
     }
diff --git 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/VersionsMetadataGenerator.java
 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/VersionsMetadataGenerator.java
index f23a482499..c684311fce 100644
--- 
a/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/VersionsMetadataGenerator.java
+++ 
b/impl/maven-impl/src/main/java/org/apache/maven/internal/impl/resolver/VersionsMetadataGenerator.java
@@ -18,13 +18,15 @@
  */
 package org.apache.maven.internal.impl.resolver;
 
+import java.time.Instant;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.Map;
 
+import org.apache.maven.api.Constants;
+import org.apache.maven.api.MonotonicClock;
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.Artifact;
 import org.eclipse.aether.deployment.DeployRequest;
@@ -44,7 +46,7 @@ class VersionsMetadataGenerator implements MetadataGenerator {
 
     private final Map<Object, VersionsMetadata> processedVersions;
 
-    private final Date timestamp;
+    private final Instant timestamp;
 
     VersionsMetadataGenerator(RepositorySystemSession session, InstallRequest 
request) {
         this(session, request.getMetadata());
@@ -57,7 +59,7 @@ class VersionsMetadataGenerator implements MetadataGenerator {
     private VersionsMetadataGenerator(RepositorySystemSession session, 
Collection<? extends Metadata> metadatas) {
         versions = new LinkedHashMap<>();
         processedVersions = new LinkedHashMap<>();
-        timestamp = (Date) ConfigUtils.getObject(session, new Date(), 
"maven.startTime");
+        timestamp = (Instant) ConfigUtils.getObject(session, 
MonotonicClock.now(), Constants.MAVEN_START_INSTANT);
 
         /*
          * NOTE: This should be considered a quirk to support interop with 
Maven's legacy ArtifactDeployer which
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/DefaultModelInterpolatorTest.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/DefaultModelInterpolatorTest.java
index beae5c686a..177dbb1870 100644
--- 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/DefaultModelInterpolatorTest.java
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/DefaultModelInterpolatorTest.java
@@ -21,10 +21,11 @@ package org.apache.maven.internal.impl.model;
 import java.nio.file.FileSystem;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
 import java.util.Calendar;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -108,7 +109,7 @@ class DefaultModelInterpolatorTest {
     @Test
     public void 
testDefaultBuildTimestampFormatShouldFormatTimeIn24HourFormat() {
         Calendar cal = Calendar.getInstance();
-        cal.setTimeZone(MavenBuildTimestamp.DEFAULT_BUILD_TIME_ZONE);
+        cal.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
         cal.set(Calendar.HOUR, 12);
         cal.set(Calendar.AM_PM, Calendar.AM);
 
@@ -120,7 +121,7 @@ class DefaultModelInterpolatorTest {
         cal.set(Calendar.MONTH, Calendar.NOVEMBER);
         cal.set(Calendar.DATE, 11);
 
-        Date firstTestDate = cal.getTime();
+        Instant firstTestDate = Instant.ofEpochMilli(cal.getTime().getTime());
 
         cal.set(Calendar.HOUR, 11);
         cal.set(Calendar.AM_PM, Calendar.PM);
@@ -128,10 +129,11 @@ class DefaultModelInterpolatorTest {
         // just to make sure all the bases are covered...
         cal.set(Calendar.HOUR_OF_DAY, 23);
 
-        Date secondTestDate = cal.getTime();
+        Instant secondTestDate = Instant.ofEpochMilli(cal.getTime().getTime());
+
+        DateTimeFormatter format = 
DateTimeFormatter.ofPattern(MavenBuildTimestamp.DEFAULT_BUILD_TIMESTAMP_FORMAT)
+                .withZone(ZoneId.of("UTC"));
 
-        SimpleDateFormat format = new 
SimpleDateFormat(MavenBuildTimestamp.DEFAULT_BUILD_TIMESTAMP_FORMAT);
-        format.setTimeZone(MavenBuildTimestamp.DEFAULT_BUILD_TIME_ZONE);
         assertEquals("1976-11-11T00:16:00Z", format.format(firstTestDate));
         assertEquals("1976-11-11T23:16:00Z", format.format(secondTestDate));
     }
@@ -148,14 +150,14 @@ class DefaultModelInterpolatorTest {
         cal.set(Calendar.MONTH, Calendar.JUNE);
         cal.set(Calendar.DATE, 16);
 
-        Date firstTestDate = cal.getTime();
+        Instant firstTestDate = Instant.ofEpochMilli(cal.getTime().getTime());
 
         cal.set(Calendar.MONTH, Calendar.NOVEMBER);
 
-        Date secondTestDate = cal.getTime();
+        Instant secondTestDate = Instant.ofEpochMilli(cal.getTime().getTime());
 
-        SimpleDateFormat format = new 
SimpleDateFormat(MavenBuildTimestamp.DEFAULT_BUILD_TIMESTAMP_FORMAT);
-        format.setTimeZone(MavenBuildTimestamp.DEFAULT_BUILD_TIME_ZONE);
+        DateTimeFormatter format = 
DateTimeFormatter.ofPattern(MavenBuildTimestamp.DEFAULT_BUILD_TIMESTAMP_FORMAT)
+                .withZone(ZoneId.of("UTC"));
         assertEquals("2014-06-15T23:16:00Z", format.format(firstTestDate));
         assertEquals("2014-11-16T00:16:00Z", format.format(secondTestDate));
     }
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/MavenBuildTimestampTest.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/MavenBuildTimestampTest.java
index d19d32f45a..d08cac07ad 100644
--- 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/MavenBuildTimestampTest.java
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/model/MavenBuildTimestampTest.java
@@ -18,10 +18,10 @@
  */
 package org.apache.maven.internal.impl.model;
 
-import java.time.Instant;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.maven.api.MonotonicClock;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -31,7 +31,7 @@ class MavenBuildTimestampTest {
     void testMavenBuildTimestampUsesUTC() {
         Map<String, String> interpolationProperties = new HashMap<>();
         interpolationProperties.put("maven.build.timestamp.format", 
"yyyyMMdd'T'HHmm'Z'");
-        MavenBuildTimestamp timestamp = new MavenBuildTimestamp(Instant.now(), 
interpolationProperties);
+        MavenBuildTimestamp timestamp = new 
MavenBuildTimestamp(MonotonicClock.now(), interpolationProperties);
         String formattedTimestamp = timestamp.formattedTimestamp();
         assertTrue(formattedTimestamp.endsWith("Z"), "We expect the UTC marker 
at the end of the timestamp.");
     }
diff --git 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/ApiRunner.java
 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/ApiRunner.java
index 7eba766de2..ecf5d5fb18 100644
--- 
a/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/ApiRunner.java
+++ 
b/impl/maven-impl/src/test/java/org/apache/maven/internal/impl/standalone/ApiRunner.java
@@ -34,6 +34,7 @@ import java.util.stream.Collectors;
 
 import org.apache.maven.api.Artifact;
 import org.apache.maven.api.Lifecycle;
+import org.apache.maven.api.MonotonicClock;
 import org.apache.maven.api.Packaging;
 import org.apache.maven.api.ProducedArtifact;
 import org.apache.maven.api.Project;
@@ -101,7 +102,7 @@ public class ApiRunner {
     static class DefaultSession extends AbstractSession {
 
         private final Map<String, String> systemProperties;
-        private final Instant startTime = Instant.now();
+        private final Instant startTime = MonotonicClock.now();
 
         DefaultSession(RepositorySystemSession session, RepositorySystem 
repositorySystem, Lookup lookup) {
             this(session, repositorySystem, Collections.emptyList(), null, 
lookup);
diff --git 
a/impl/maven-logging/src/main/java/org/apache/maven/slf4j/MavenBaseLogger.java 
b/impl/maven-logging/src/main/java/org/apache/maven/slf4j/MavenBaseLogger.java
index 273f13ebc7..19fc45a665 100644
--- 
a/impl/maven-logging/src/main/java/org/apache/maven/slf4j/MavenBaseLogger.java
+++ 
b/impl/maven-logging/src/main/java/org/apache/maven/slf4j/MavenBaseLogger.java
@@ -19,10 +19,13 @@
 package org.apache.maven.slf4j;
 
 import java.io.PrintStream;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.ArrayList;
-import java.util.Date;
 import java.util.List;
 
+import org.apache.maven.api.MonotonicClock;
 import org.slf4j.Logger;
 import org.slf4j.Marker;
 import org.slf4j.event.Level;
@@ -145,7 +148,8 @@ public class MavenBaseLogger extends LegacyAbstractLogger {
 
     private static final long serialVersionUID = -632788891211436180L;
 
-    private static final long START_TIME = System.currentTimeMillis();
+    private static final Clock MONOTONIC_CLOCK = Clock.tick(Clock.systemUTC(), 
Duration.ofMillis(1));
+    private static final Instant START_TIME = MonotonicClock.now();
 
     protected static final int LOG_LEVEL_TRACE = LocationAwareLogger.TRACE_INT;
     protected static final int LOG_LEVEL_DEBUG = LocationAwareLogger.DEBUG_INT;
@@ -265,7 +269,7 @@ public class MavenBaseLogger extends LegacyAbstractLogger {
     }
 
     protected String getFormattedDate() {
-        Date now = new Date();
+        Instant now = MonotonicClock.now();
         String dateText;
         synchronized (CONFIG_PARAMS.dateFormatter) {
             dateText = CONFIG_PARAMS.dateFormatter.format(now);
@@ -382,7 +386,7 @@ public class MavenBaseLogger extends LegacyAbstractLogger {
                 buf.append(getFormattedDate());
                 buf.append(SP);
             } else {
-                buf.append(System.currentTimeMillis() - START_TIME);
+                buf.append(Duration.between(START_TIME, 
MonotonicClock.now()).toMillis());
                 buf.append(SP);
             }
         }
diff --git 
a/impl/maven-logging/src/main/java/org/apache/maven/slf4j/SimpleLoggerConfiguration.java
 
b/impl/maven-logging/src/main/java/org/apache/maven/slf4j/SimpleLoggerConfiguration.java
index 02ca0f464e..63b3667402 100644
--- 
a/impl/maven-logging/src/main/java/org/apache/maven/slf4j/SimpleLoggerConfiguration.java
+++ 
b/impl/maven-logging/src/main/java/org/apache/maven/slf4j/SimpleLoggerConfiguration.java
@@ -22,8 +22,7 @@ import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.io.PrintStream;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+import java.time.format.DateTimeFormatter;
 import java.util.Properties;
 
 import org.apache.maven.slf4j.OutputChoice.OutputChoiceType;
@@ -56,7 +55,7 @@ public class SimpleLoggerConfiguration {
     private static final String DATE_TIME_FORMAT_STR_DEFAULT = null;
     private static String dateTimeFormatStr = DATE_TIME_FORMAT_STR_DEFAULT;
 
-    DateFormat dateFormatter = null;
+    DateTimeFormatter dateFormatter = null;
 
     private static final boolean SHOW_THREAD_NAME_DEFAULT = true;
     boolean showThreadName = SHOW_THREAD_NAME_DEFAULT;
@@ -116,7 +115,7 @@ public class SimpleLoggerConfiguration {
 
         if (dateTimeFormatStr != null) {
             try {
-                dateFormatter = new SimpleDateFormat(dateTimeFormatStr);
+                dateFormatter = DateTimeFormatter.ofPattern(dateTimeFormatStr);
             } catch (IllegalArgumentException e) {
                 Reporter.error("Bad date format in " + CONFIGURATION_FILE + "; 
will output relative time", e);
             }
diff --git a/src/site/markdown/configuration.properties 
b/src/site/markdown/configuration.properties
index b6366ad964..44b45571ff 100644
--- a/src/site/markdown/configuration.properties
+++ b/src/site/markdown/configuration.properties
@@ -16,7 +16,7 @@
 # specific language governing permissions and limitations
 # under the License.
 #
-props.count = 43
+props.count = 44
 props.1.key = maven.build.timestamp.format
 props.1.configurationType = String
 props.1.description = Build timestamp format.
@@ -171,105 +171,111 @@ props.26.configurationType = String
 props.26.description = 
 props.26.defaultValue = ${maven.user.conf}/settings-security4.xml
 props.26.configurationSource = User properties
-props.27.key = maven.style.color
+props.27.key = maven.startInstant
 props.27.configurationType = String
-props.27.description = Maven output color mode. Allowed values are 
<code>auto</code>, <code>always</code>, <code>never</code>.
-props.27.defaultValue = auto
-props.27.since = 4.0.0
+props.27.description = User property used to store the build timestamp.
+props.27.defaultValue = 
+props.27.since = 4.1.0
 props.27.configurationSource = User properties
-props.28.key = maven.style.debug
+props.28.key = maven.style.color
 props.28.configurationType = String
-props.28.description = Color style for debug messages.
-props.28.defaultValue = bold,f:cyan
+props.28.description = Maven output color mode. Allowed values are 
<code>auto</code>, <code>always</code>, <code>never</code>.
+props.28.defaultValue = auto
 props.28.since = 4.0.0
 props.28.configurationSource = User properties
-props.29.key = maven.style.error
+props.29.key = maven.style.debug
 props.29.configurationType = String
-props.29.description = Color style for error messages.
-props.29.defaultValue = bold,f:red
+props.29.description = Color style for debug messages.
+props.29.defaultValue = bold,f:cyan
 props.29.since = 4.0.0
 props.29.configurationSource = User properties
-props.30.key = maven.style.failure
+props.30.key = maven.style.error
 props.30.configurationType = String
-props.30.description = Color style for failure messages.
+props.30.description = Color style for error messages.
 props.30.defaultValue = bold,f:red
 props.30.since = 4.0.0
 props.30.configurationSource = User properties
-props.31.key = maven.style.info
+props.31.key = maven.style.failure
 props.31.configurationType = String
-props.31.description = Color style for info messages.
-props.31.defaultValue = bold,f:blue
+props.31.description = Color style for failure messages.
+props.31.defaultValue = bold,f:red
 props.31.since = 4.0.0
 props.31.configurationSource = User properties
-props.32.key = maven.style.mojo
+props.32.key = maven.style.info
 props.32.configurationType = String
-props.32.description = Color style for mojo messages.
-props.32.defaultValue = f:green
+props.32.description = Color style for info messages.
+props.32.defaultValue = bold,f:blue
 props.32.since = 4.0.0
 props.32.configurationSource = User properties
-props.33.key = maven.style.project
+props.33.key = maven.style.mojo
 props.33.configurationType = String
-props.33.description = Color style for project messages.
-props.33.defaultValue = f:cyan
+props.33.description = Color style for mojo messages.
+props.33.defaultValue = f:green
 props.33.since = 4.0.0
 props.33.configurationSource = User properties
-props.34.key = maven.style.strong
+props.34.key = maven.style.project
 props.34.configurationType = String
-props.34.description = Color style for strong messages.
-props.34.defaultValue = bold
+props.34.description = Color style for project messages.
+props.34.defaultValue = f:cyan
 props.34.since = 4.0.0
 props.34.configurationSource = User properties
-props.35.key = maven.style.success
+props.35.key = maven.style.strong
 props.35.configurationType = String
-props.35.description = Color style for success messages.
-props.35.defaultValue = bold,f:green
+props.35.description = Color style for strong messages.
+props.35.defaultValue = bold
 props.35.since = 4.0.0
 props.35.configurationSource = User properties
-props.36.key = maven.style.trace
+props.36.key = maven.style.success
 props.36.configurationType = String
-props.36.description = Color style for trace messages.
-props.36.defaultValue = bold,f:magenta
+props.36.description = Color style for success messages.
+props.36.defaultValue = bold,f:green
 props.36.since = 4.0.0
 props.36.configurationSource = User properties
-props.37.key = maven.style.transfer
+props.37.key = maven.style.trace
 props.37.configurationType = String
-props.37.description = Color style for transfer messages.
-props.37.defaultValue = f:bright-black
+props.37.description = Color style for trace messages.
+props.37.defaultValue = bold,f:magenta
 props.37.since = 4.0.0
 props.37.configurationSource = User properties
-props.38.key = maven.style.warning
+props.38.key = maven.style.transfer
 props.38.configurationType = String
-props.38.description = Color style for warning messages.
-props.38.defaultValue = bold,f:yellow
+props.38.description = Color style for transfer messages.
+props.38.defaultValue = f:bright-black
 props.38.since = 4.0.0
 props.38.configurationSource = User properties
-props.39.key = maven.user.conf
+props.39.key = maven.style.warning
 props.39.configurationType = String
-props.39.description = Maven user configuration directory.
-props.39.defaultValue = ${user.home}/.m2
+props.39.description = Color style for warning messages.
+props.39.defaultValue = bold,f:yellow
 props.39.since = 4.0.0
 props.39.configurationSource = User properties
-props.40.key = maven.user.extensions
+props.40.key = maven.user.conf
 props.40.configurationType = String
-props.40.description = Maven user extensions.
-props.40.defaultValue = ${maven.user.conf}/extensions.xml
+props.40.description = Maven user configuration directory.
+props.40.defaultValue = ${user.home}/.m2
 props.40.since = 4.0.0
 props.40.configurationSource = User properties
-props.41.key = maven.user.settings
+props.41.key = maven.user.extensions
 props.41.configurationType = String
-props.41.description = Maven user settings.
-props.41.defaultValue = ${maven.user.conf}/settings.xml
+props.41.description = Maven user extensions.
+props.41.defaultValue = ${maven.user.conf}/extensions.xml
 props.41.since = 4.0.0
 props.41.configurationSource = User properties
-props.42.key = maven.user.toolchains
+props.42.key = maven.user.settings
 props.42.configurationType = String
-props.42.description = Maven user toolchains.
-props.42.defaultValue = ${maven.user.conf}/toolchains.xml
+props.42.description = Maven user settings.
+props.42.defaultValue = ${maven.user.conf}/settings.xml
 props.42.since = 4.0.0
 props.42.configurationSource = User properties
-props.43.key = maven.versionResolver.noCache
-props.43.configurationType = Boolean
-props.43.description = User property for disabling version resolver cache.
-props.43.defaultValue = false
-props.43.since = 3.0.0
+props.43.key = maven.user.toolchains
+props.43.configurationType = String
+props.43.description = Maven user toolchains.
+props.43.defaultValue = ${maven.user.conf}/toolchains.xml
+props.43.since = 4.0.0
 props.43.configurationSource = User properties
+props.44.key = maven.versionResolver.noCache
+props.44.configurationType = Boolean
+props.44.description = User property for disabling version resolver cache.
+props.44.defaultValue = false
+props.44.since = 3.0.0
+props.44.configurationSource = User properties
diff --git a/src/site/markdown/configuration.yaml 
b/src/site/markdown/configuration.yaml
index aea2d4470f..f225960935 100644
--- a/src/site/markdown/configuration.yaml
+++ b/src/site/markdown/configuration.yaml
@@ -171,6 +171,12 @@ props:
       description: ""
       defaultValue: ${maven.user.conf}/settings-security4.xml
       configurationSource: User properties
+    - key: maven.startInstant
+      configurationType: String
+      description: "User property used to store the build timestamp."
+      defaultValue: 
+      since: 4.1.0
+      configurationSource: User properties
     - key: maven.style.color
       configurationType: String
       description: "Maven output color mode. Allowed values are 
<code>auto</code>, <code>always</code>, <code>never</code>."
diff --git a/src/site/markdown/maven-configuration.md 
b/src/site/markdown/maven-configuration.md
index fbc6cda3cf..aa4eada9ed 100644
--- a/src/site/markdown/maven-configuration.md
+++ b/src/site/markdown/maven-configuration.md
@@ -51,21 +51,22 @@ under the License.
 | 24. | `maven.resolver.transport` | `String` | Resolver transport to use. Can 
be <code>default</code>, <code>wagon</code>, <code>apache</code>, 
<code>jdk</code> or <code>auto</code>. |  `default`  | 4.0.0 | User properties |
 | 25. | `maven.session.versionFilter` | `String` | User property for version 
filter expression used in session, applied to resolving ranges: a semicolon 
separated list of filters to apply. By default, no version filter is applied 
(like in Maven 3). <br/> Supported filters: <ul> <li>"h" or "h(num)" - highest 
version or top list of highest ones filter</li> <li>"l" or "l(num)" - lowest 
version or bottom list of lowest ones filter</li> <li>"s" - contextual snapshot 
filter</li> <li>"e(G:A:V)" [...]
 | 26. | `maven.settings.security` | `String` |  |  
`${maven.user.conf}/settings-security4.xml`  |  | User properties |
-| 27. | `maven.style.color` | `String` | Maven output color mode. Allowed 
values are <code>auto</code>, <code>always</code>, <code>never</code>. |  
`auto`  | 4.0.0 | User properties |
-| 28. | `maven.style.debug` | `String` | Color style for debug messages. |  
`bold,f:cyan`  | 4.0.0 | User properties |
-| 29. | `maven.style.error` | `String` | Color style for error messages. |  
`bold,f:red`  | 4.0.0 | User properties |
-| 30. | `maven.style.failure` | `String` | Color style for failure messages. | 
 `bold,f:red`  | 4.0.0 | User properties |
-| 31. | `maven.style.info` | `String` | Color style for info messages. |  
`bold,f:blue`  | 4.0.0 | User properties |
-| 32. | `maven.style.mojo` | `String` | Color style for mojo messages. |  
`f:green`  | 4.0.0 | User properties |
-| 33. | `maven.style.project` | `String` | Color style for project messages. | 
 `f:cyan`  | 4.0.0 | User properties |
-| 34. | `maven.style.strong` | `String` | Color style for strong messages. |  
`bold`  | 4.0.0 | User properties |
-| 35. | `maven.style.success` | `String` | Color style for success messages. | 
 `bold,f:green`  | 4.0.0 | User properties |
-| 36. | `maven.style.trace` | `String` | Color style for trace messages. |  
`bold,f:magenta`  | 4.0.0 | User properties |
-| 37. | `maven.style.transfer` | `String` | Color style for transfer messages. 
|  `f:bright-black`  | 4.0.0 | User properties |
-| 38. | `maven.style.warning` | `String` | Color style for warning messages. | 
 `bold,f:yellow`  | 4.0.0 | User properties |
-| 39. | `maven.user.conf` | `String` | Maven user configuration directory. |  
`${user.home}/.m2`  | 4.0.0 | User properties |
-| 40. | `maven.user.extensions` | `String` | Maven user extensions. |  
`${maven.user.conf}/extensions.xml`  | 4.0.0 | User properties |
-| 41. | `maven.user.settings` | `String` | Maven user settings. |  
`${maven.user.conf}/settings.xml`  | 4.0.0 | User properties |
-| 42. | `maven.user.toolchains` | `String` | Maven user toolchains. |  
`${maven.user.conf}/toolchains.xml`  | 4.0.0 | User properties |
-| 43. | `maven.versionResolver.noCache` | `Boolean` | User property for 
disabling version resolver cache. |  `false`  | 3.0.0 | User properties |
+| 27. | `maven.startInstant` | `String` | User property used to store the 
build timestamp. |  -  | 4.1.0 | User properties |
+| 28. | `maven.style.color` | `String` | Maven output color mode. Allowed 
values are <code>auto</code>, <code>always</code>, <code>never</code>. |  
`auto`  | 4.0.0 | User properties |
+| 29. | `maven.style.debug` | `String` | Color style for debug messages. |  
`bold,f:cyan`  | 4.0.0 | User properties |
+| 30. | `maven.style.error` | `String` | Color style for error messages. |  
`bold,f:red`  | 4.0.0 | User properties |
+| 31. | `maven.style.failure` | `String` | Color style for failure messages. | 
 `bold,f:red`  | 4.0.0 | User properties |
+| 32. | `maven.style.info` | `String` | Color style for info messages. |  
`bold,f:blue`  | 4.0.0 | User properties |
+| 33. | `maven.style.mojo` | `String` | Color style for mojo messages. |  
`f:green`  | 4.0.0 | User properties |
+| 34. | `maven.style.project` | `String` | Color style for project messages. | 
 `f:cyan`  | 4.0.0 | User properties |
+| 35. | `maven.style.strong` | `String` | Color style for strong messages. |  
`bold`  | 4.0.0 | User properties |
+| 36. | `maven.style.success` | `String` | Color style for success messages. | 
 `bold,f:green`  | 4.0.0 | User properties |
+| 37. | `maven.style.trace` | `String` | Color style for trace messages. |  
`bold,f:magenta`  | 4.0.0 | User properties |
+| 38. | `maven.style.transfer` | `String` | Color style for transfer messages. 
|  `f:bright-black`  | 4.0.0 | User properties |
+| 39. | `maven.style.warning` | `String` | Color style for warning messages. | 
 `bold,f:yellow`  | 4.0.0 | User properties |
+| 40. | `maven.user.conf` | `String` | Maven user configuration directory. |  
`${user.home}/.m2`  | 4.0.0 | User properties |
+| 41. | `maven.user.extensions` | `String` | Maven user extensions. |  
`${maven.user.conf}/extensions.xml`  | 4.0.0 | User properties |
+| 42. | `maven.user.settings` | `String` | Maven user settings. |  
`${maven.user.conf}/settings.xml`  | 4.0.0 | User properties |
+| 43. | `maven.user.toolchains` | `String` | Maven user toolchains. |  
`${maven.user.conf}/toolchains.xml`  | 4.0.0 | User properties |
+| 44. | `maven.versionResolver.noCache` | `Boolean` | User property for 
disabling version resolver cache. |  `false`  | 3.0.0 | User properties |
 


Reply via email to