Stopwatch utility class in Java


Looking for input on my code for a Stopwatch utility written in Java.

/**  * File: Stopwatch.java  */ package org.me.mystuff;  import java.util.concurrent.TimeUnit;  public class Stopwatch {     /**      * The start timestamp, as determined by a call to {@code System.nanoTime}.      */     private long start;      /**      * The total elapsed time recorded so far in nanoseconds (raw value).      */     private long elapsed;      /**      * Whether or not the stopwatch is running.      */     private boolean running;      /**      * Constructs a new {@code Stopwatch} instance.      */     public Stopwatch() {         reset();     }      /**      * Stops time interval measurement, resets the elapsed time to zero, and      * starts measuring time again.      *       * <p>      * This is a convenience method equivalent to calling {@code reset} followed      * by {@code start}.      */     public void restart() {         reset();         start();     }      /**      * Stops time interval measurement and resets the elapsed time value to      * zero.      */     public void reset() {         running = false;         start = 0;         elapsed = 0;     }      /**      * Initializes a new {@code Stopwatch} instance, sets the elapsed time value      * to zero, and starts measuring elapsed time.      *       * @return a running {@code Stopwatch} instance      */     public static Stopwatch startNew() {         final var s = new Stopwatch();         s.start();         return s;     }      /**      * Starts, or resumes, measuring elapsed time for an interval.      */     public void start() {         // Calling start() on a running stopwatch is a no-op.         if (!isRunning()) {             running = true;             start = start <= 0 ? System.nanoTime() : System.nanoTime() - elapsed;         }     }      /**      * Stops measuring elapsed time for an interval.      */     public void stop() {         // Calling stop() on a stopped stopwatch is a no-op.         if (isRunning()) {             elapsed = System.nanoTime() - start;             running = false;         }     }      /**      * Returns the elapsed time measured so far by this stopwatch in the      * specified {@code TimeUnit}.      *       * @param timeUnit      *            the desired time unit      * @return the duration in the specified time unit      */     public double elapsed(TimeUnit timeUnit) {         // If the stopwatch is running, compute and return the current timestamp.         // Otherwise, just return the timestamp as of the last call to stop().         final long duration = isRunning() ? System.nanoTime() - start : elapsed;         return UnitConverter.convert(duration, timeUnit);     }      /**      * Returns {@code true} if the stopwatch is currently running.      *       * @return {@code true} if this stopwatch is running      */     public boolean isRunning() {         return running;     }      /**      * Returns a string representation of the elapsed time measured so far by      * this stopwatch, expressed in milliseconds using two decimal places.      *       * <p>      * Example: 1.2345 milliseconds would return a string equal to      * {@code "1.23 ms"}.      *       * <p>      * This is a convenience method that simplifies querying the stopwatch for      * reporting purposes.      */     @Override     public String toString() {     final double duration = elapsed(TimeUnit.MILLISECONDS);      final double rounded = Math.round(duration * 100d) / 100d;     return String.valueOf(rounded).concat(unit);     }      /**      * This class is an internal utility to supplement the stopwatch's unit      * conversion operations.      */     private static class UnitConverter {         /**          * The number of nanoseconds in a millisecond.          */         static final double NANOS_PER_MILLI = 1e6;          /**          * The number of nanoseconds in a second.          */         static final double NANOS_PER_SECOND = 1e9;          /**          * The number of nanoseconds in a minute.          */         static final double NANOS_PER_MINUTE = 6e10;          /**          * The number of nanoseconds in an hour.          */         static final double NANOS_PER_HOUR = 3.6e12;          /**          * Converts the specified nanosecond value to the specified time unit          * and returns the result.          *           * @param nanoTime          *            the value to convert (in nanoseconds)          * @param toUnit          *            the desired time unit          * @return the converted value          */         static double convert(long nanoTime, TimeUnit toUnit) {             switch (toUnit) {             case MILLISECONDS:                 return toMillis(nanoTime);             case SECONDS:                 return toSeconds(nanoTime);             case MINUTES:                 return toMinutes(nanoTime);             case HOURS:                 return toHours(nanoTime);             default:                 throw new IllegalArgumentException("no valid time unit specified");             }         }          static double toMillis(long nanos) {             return nanos / NANOS_PER_MILLI;         }          static double toSeconds(long nanos) {             return nanos / NANOS_PER_SECOND;         }          static double toMinutes(long nanos) {             return nanos / NANOS_PER_MINUTE;         }          static double toHours(long nanos) {             return nanos / NANOS_PER_HOUR;         }     } } 

The stopwatch uses System.nanoTime as its time source. In general it works similarly to the standard .NET version and is, as far as my unit tests go, correct. You can query it whether or not it is running, and it will give the most recent recorded value each time.

I am just curious to hear some thoughts on my code and suggestions for improvement, if any. It’s extremely lightweight so feel free to test it on your machine if you wish.