Choose the Right Clock on Android

Android framework provides three different time keeping facilities. This post discusses the main differences and usage of them.

System.currentTimeMillis

It returns the number of milliseconds elapsed since the epoch, which is 00:00:00 January 1, 1970 UTC.

Note that this clock is not guaranteed to be monotonic, because of the followings:

  • The clock can be changed by network time sync
  • The clock can be updated by user or program

The clock can jump backwards or forwards unpredictably. We can listen for ACTION_TIME_TICK,ACTION_TIME_CHANGED and ACTION_TIMEZONE_CHANGED Intent broadcasts to find out when the time changes.

When to use

  • Get the wall clock time
  • Inerval/elapsed time that needed to span across device reboot. This is not ideal, but a cheap and OK solution sometimes. Ideally we want to have a server provided timestamp to avoid all the unpredictably jump can happen to this clock.

When not to use

  • Interval or elapsed timing doesn’t need to span across device reboot. E.g.: memory cache expiration time

SystemClock.uptimeMillis

It returns the number of milliseconds since device boot. The clock stops when system enters deep sleep. The clock is guaranteed to be monotonic.

This clock is the basis for Thread.sleep(millls), Object.wait(millis), and System.nanoTime().

When to use

  • Interval or elapsed timing doesn’t need to span across device reboot, and we want to exclude device deep sleep

SystemClock.elapsedRealtime

It returns the number of milliseconds since device boot. The clock don’t stop even when system enters deep sleep. It’s also guaranteed to be monotonic.

elapsedRealtimeNanos(): Similar to method above, but returns number of nano seconds.

When to use

  • Interval or elapsed timing doesn’t need to span across device reboot, and we want to include device deep sleep

Caveats

1. When using System.nanoTime or elapsedRealtimeNanos, differences span greater than approximately 292 years (263 nanoseconds) will not be computed correctly due to numerical overflow.

2. When comparing two timestamps, we should use t2 – t1 > 0 instead of t2 > t1, because of possible numerical overflow.

In order to help us understand why t2 – t1 is better than t2 > t1 in case of numerical overflow, let’s assume that t1 and t2 are bytes within the range of [-128, 127].
When t1 = 126, t2 = t1 + 3 will result in numerical overflow, and t2 = -127. If we use t2 > t1, it will return false. But t2 – t1 = -127 – 126, which will again result in numerical overflow, and the result is 3.

This only works when the difference of t2 and t1 itself can be represented by the data type. In the byte example above, t2 – t1 must be less than or equal to 127.