[Tiny] Elapsed Time in Java: currentTimeMillis vs nanoTime
Tags: java, tiny
Published: 2023-05-25 21:57:29

When you need to measure how long some code takes to execute, the first thing that usually comes to mind is `currentTimeMillis`:

```java
long start = System.currentTimeMillis();
doSomeWork();
long elapsedMillis = System.currentTimeMillis() - start;
```

However, there is a potential problem here - `elapsedMillis` may become negative in some unfortunate scenarios. Or it could be positive but not correct.

This is because `currentTimeMillis` measures wall-clock time, i.e. system clock. And the system clock is always automatically corrected because no computer's clock is perfect. Or you can just change the system date and time manually.

So, if the correction happens during `doSomeWork()`, the result of calculating `elapsedMillis` will be incorrect.

On the other hand, there is the `nanoTime` method which is meant to be exactly for elapsed time measurements. As Javadoc says:

> This method can only be used to measure elapsed time and is not related to any other notion of system or wall-clock time.  

The usage is the same as with `currentTimeMillis`:

```java
long start = System.nanoTime();
doSomeWork();
long elapsedNanos = System.nanoTime() - start;
```

But wait, you don't have to trust me! 

You can test it yourself to make sure that elapsed time can be negative with `currentTimeMillis`. Here is the code:

```java
public class ElapsedTimeTest {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Long> elapsedFuture = executor.submit(() -> {
            long start;
            long elapsed;
            while (true) {
                start = System.currentTimeMillis();

                // do some work
                Thread.sleep(1000);

                elapsed = System.currentTimeMillis() - start;
                if (elapsed < 0) {
                    return elapsed;
                }

                if (Thread.currentThread().isInterrupted()) {
                    return null;
                }
            }
        });

        try {
            Long elapsed = elapsedFuture.get(5, TimeUnit.MINUTES);
            System.out.println("Elapsed time: " + elapsed);
        } catch (TimeoutException e) {
            System.out.println("No luck so far");
        }

        executor.shutdownNow();
    }
}
```

Here we submit a calculation of elapsed time in a separate thread and wait for the result for 5 minutes. Just to have enough time to tweak the system clock later.

Elapsed time is calculated with `currentTimeMillis` in an infinite loop, and the "work" takes approximately 1 second. (`Thread.sleep(1000)`)

The loop will break, and the calculation will be completed if the elapsed time is negative or the thread is interrupted.

Now, run this class, open system clock settings, and update the time, say, by decrementing minutes. You will see that the negative elapsed time is printed, and the program completes.

If you change `currentTimeMillis` to `nanoTime` and repeat the experiment, nothing happens when you decrement minutes. The program runs until the `TimeoutException` on `elapsedFuture.get()`.

---

*Dream your code, code your dream.*