概要
-
System.currentTimeMillis()
- 取得できる値は、
1970-01-01T00:00:00Z
からのミリ秒(UNIX TIME) - 時刻の絶対値を取得する際に使用する
- 取得できる値は、
-
System.nanoTime()
- 取得できる値は、任意の時点からのナノ秒
- 時刻の相対値を取得する際に使用する
あと、Java 8 で使えるようになった Instant.now()
も併せて解説します。
-
Instant.now()
- 取得できる値は、
1970-01-01T00:00:00Z
からの秒 + ナノ秒(UNIX TIME) - 時刻の絶対値を取得する際に使用する
- 取得できる値は、
System.currentTimeMillis()
の使い方
あるタイミングで現在時刻を取得するために使います。
long now = System.currentTimeMillis();
// ==> 1667115400695
一方で、以下のような相対時刻の取得には不向きです。
long start = System.currentTimeMillis();
// … 処理 …
long end = System.currentTimeMillis();
long total = end - start;
System.currentTimeMillis()
は 不連続な時間の変化(例えば、システム管理者が時間を手動で変更した場合など)の影響を受けるためです。
例えば、上記の処理の途中で手動で時刻が変更されると、total
が極端に大きくなったり負の値になったりします。
あくまで、 ある時点の時刻をミリ秒で取得する、という用途に限って使う API です。
System.nanoTime()
の使い方
ある二つの時点の相対時間を取得するために使います。
long start = System.nanoTime();
// … 処理 …
long end = System.nanoTime();
long total = end - start;
// => 1180305853
System.nanoTime()
は、不連続な時間の変化の影響を受けません。
上記の場合、処理の途中で時刻が変更されても、total
が実際の処理時間を表す値になります。
一方で、あるタイミングの現在時刻を取得するのには不向きです。
例えば、System.nanoTime()
を呼び出して 3761546426118
という値が返ってきても、これが何を意味するのか(いつからの経過時刻なのか)は不明です。
long now = System.nanoTime();
// => 3761546426118
あくまで、 ふたつの時刻の差分をナノ秒で取得する、という用途に限って使う API です。
Instant.now()
の使い方
System.currentTimeMillis()
と同様に、あるタイミングで現在時刻を取得するために使います。
java.time.Instant.now();
// ==> 2022-10-30T07:39:35.003041Z
Instant
型なので、ミリ秒を直接扱うのよりも扱いやすいです。
例えば、システムのタイムゾーンに変換して、それをフォーマットして出力する、ということも簡単にできます。
ZonedDateTime dateTime = now.atZone(ZoneId.systemDefault())
String text = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(dateTime);
==> "2022-10-30T22:00:45.1861852+09:00"
また、System.currentTimeMillis()
はミリ秒までの精度ですが、 Instant.now()
はより細かい精度で時刻を扱います。
(Windows だと 100 ナノ秒です)
今となっては、System.currentTimeMillis()
よりも Instant
型を使った方がいいと思います。
OpenJDK での実装の詳細
System.currentTimeMillis()
, Instant.now()
OS | API | コード |
---|---|---|
Windows | GetSystemTimeAsFileTime | jdk/os_windows.cpp |
上記以外(Linux など) | clock_gettime(CLOCK_REALTIME, &ts) | jdk/os_posix.cpp |
System.nanoTime()
OS | API | コード |
---|---|---|
Windows | QueryPerformanceCounter | jdk/os_windows.cpp |
macOS | mach_absolute_time | jdk/os_bsd.cpp |
AIX | mread_real_time | jdk/os_aix.cpp |
上記以外(Linux など) | clock_gettime(CLOCK_MONOTONIC, &ts) | jdk/os_posix.cpp |
なお、 Linux 等で使われる CLOCK_MONOTONIC
は adjtime(3) や NTP が行う段階的な調整の影響を受けるとのこと。
CLOCK_MONOTONIC_RAW
のほうがいいのでは?という気がするのですが、こちらは Linux 固有(POSIX非対応)なので、Java では使っていないようです。
Java 考古学者向けの情報
System.currentTimeMillis() の分解能
Windows XP のころは、System.currentTimeMillis()
が 16ms 程度の分解能しかないと言われていました。これは、過去 GetSystemTimeAsFileTime
の分解能がそれぐらいだったせいです。
現在は 1ms の分解能になっています。1
以下のコードで、1ms の分解能が得られることを確認できます。
(Windows 10 で確認)
for(var i = 0; i < 1000; i++) {
System.out.println(System.currentTimeMillis());
}
…(中略)…
1667107783815
1667107783815
1667107783816
1667107783816
…(中略)…
1667107783817
1667107783817
1667107783818
Linux での実装の変化
昔は、 System.currentTimeMillis()
の実装に gettimeofday を使っていました。
しかし、 Java 15 から clock_gettime
が使える場合は、こちらが使われるようになりました。
- チケット:[JDK-8242504] Enhance the system clock to nanosecond precision - Java Bug System
- コミット:8242504: Enhance the system clock to nanosecond precision · openjdk/jdk@7228978
また、現在のJava がサポートしている環境は clock_gettime
が使えることが保証されていることが確認できたので、 Java 17 で gettimeofday
を使う処理は削除されました。
- チケット:[JDK-8246112] Remove build-time and run-time checks for clock_gettime and CLOCK_MONOTONIC - Java Bug System
- コミット:8246112: Remove build-time and run-time checks for clock_gettime and … · openjdk/jdk@6f2be9c
-
たぶん、Windows Vista のタイミングで? 調べたけど分からず…。 ↩