System.currentTimeMillis() と System.nanoTime() の違い

# 概要

* [System.currentTimeMillis()](https://docs.oracle.com/javase/jp/18/docs/api/java.base/java/lang/System.html#currentTimeMillis%28%29)
    * 取得できる値は、**`1970-01-01T00:00:00Z` からのミリ秒（UNIX TIME）**
    * 時刻の**絶対値**を取得する際に使用する
* [System.nanoTime()](https://docs.oracle.com/javase/jp/18/docs/api/java.base/java/lang/System.html#nanoTime())
    * 取得できる値は、**任意の時点からのナノ秒**
    * 時刻の**相対値**を取得する際に使用する

あと、Java 8 で使えるようになった `Instant.now()` も併せて解説します。
* [Instant.now()](https://docs.oracle.com/javase/jp/18/docs/api/java.base/java/time/Instant.html)
    * 取得できる値は、**`1970-01-01T00:00:00Z` からの秒 + ナノ秒（UNIX TIME）**
    * 時刻の**絶対値**を取得する際に使用する

# `System.currentTimeMillis()` の使い方

あるタイミングで現在時刻を取得するために使います。
```java
long now = System.currentTimeMillis();
// ==> 1667115400695
```

一方で、以下のような相対時刻の取得には不向きです。
```java
long start = System.currentTimeMillis();
// … 処理 …
long end = System.currentTimeMillis();

long total = end - start;
```

`System.currentTimeMillis()` は **不連続な時間の変化（例えば、システム管理者が時間を手動で変更した場合など）の影響を受けるためです。**
例えば、上記の処理の途中で手動で時刻が変更されると、`total` が極端に大きくなったり負の値になったりします。

あくまで、 **ある時点の時刻をミリ秒で取得する**、という用途に限って使う API です。

# `System.nanoTime()` の使い方

ある二つの時点の相対時間を取得するために使います。
```java
long start = System.nanoTime();
// … 処理 …
long end = System.nanoTime();

long total = end - start;
// => 1180305853
```

`System.nanoTime()` は、**不連続な時間の変化の影響を受けません。**
上記の場合、処理の途中で時刻が変更されても、`total` が実際の処理時間を表す値になります。

一方で、あるタイミングの現在時刻を取得するのには不向きです。
例えば、`System.nanoTime()` を呼び出して `3761546426118` という値が返ってきても、これが何を意味するのか（いつからの経過時刻なのか）は不明です。
```java
long now = System.nanoTime();
// => 3761546426118
```

あくまで、 **ふたつの時刻の差分をナノ秒で取得する**、という用途に限って使う API です。

# `Instant.now()` の使い方

`System.currentTimeMillis()` と同様に、あるタイミングで現在時刻を取得するために使います。
```java
java.time.Instant.now();
// ==> 2022-10-30T07:39:35.003041Z
```

`Instant` 型なので、ミリ秒を直接扱うのよりも扱いやすいです。
例えば、システムのタイムゾーンに変換して、それをフォーマットして出力する、ということも簡単にできます。
```java
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](https://learn.microsoft.com/ja-jp/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtimeasfiletime)|[jdk/os_windows.cpp](https://github.com/openjdk/jdk/blob/b2878c904beb971da37b1da50a7294c460d6cef3/src/hotspot/os/windows/os_windows.cpp#L1062-L1066)|
|上記以外（Linux など）|[clock_gettime(CLOCK_REALTIME, &ts)](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/clock_getres.2.html)|[jdk/os_posix.cpp](https://github.com/openjdk/jdk/blob/b6b0317f832985470ccf4bc1e2abf9015ce5bd54/src/hotspot/os/posix/os_posix.cpp#L1370-L1376)|


## `System.nanoTime()`

|OS|API|コード|
|--|--|--|
|Windows|[QueryPerformanceCounter](https://learn.microsoft.com/ja-jp/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter)|[jdk/os_windows.cpp](https://github.com/openjdk/jdk/blob/b2878c904beb971da37b1da50a7294c460d6cef3/src/hotspot/os/windows/os_windows.cpp#L1077-L1084)|
|macOS|[mach_absolute_time](https://developer.apple.com/documentation/kernel/1462446-mach_absolute_time)|[jdk/os_bsd.cpp](https://github.com/openjdk/jdk/blob/b2878c904beb971da37b1da50a7294c460d6cef3/src/hotspot/os/bsd/os_bsd.cpp#L765-L787)|
|AIX|[mread_real_time](https://www.ibm.com/docs/ja/aix/7.3?topic=r-read-real-time-read-wall-timetime-base-time-mread-real-time-subroutine)|[jdk/os_aix.cpp](https://github.com/openjdk/jdk/blob/b2878c904beb971da37b1da50a7294c460d6cef3/src/hotspot/os/aix/os_aix.cpp#L949-L981)|
|上記以外（Linux など）|[clock_gettime(CLOCK_MONOTONIC, &ts)](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/clock_getres.2.html)|[jdk/os_posix.cpp](https://github.com/openjdk/jdk/blob/b6b0317f832985470ccf4bc1e2abf9015ce5bd54/src/hotspot/os/posix/os_posix.cpp#L1392-L1398)|

なお、 **Linux 等で使われる `CLOCK_MONOTONIC` は adjtime(3) や NTP が行う段階的な調整の影響を受けるとのこと。**
`CLOCK_MONOTONIC_RAW` のほうがいいのでは？という気がするのですが、こちらは Linux 固有（POSIX非対応）なので、Java では使っていないようです。

# Java 考古学者向けの情報

## System.currentTimeMillis() の分解能

Windows XP のころは、`System.currentTimeMillis()` が 16ms 程度の分解能しかないと言われていました。これは、過去 `GetSystemTimeAsFileTime` の分解能がそれぐらいだったせいです。
**現在は 1ms の分解能になっています。[^windows]**

[^windows]: たぶん、Windows Vista のタイミングで？ 調べたけど分からず…。

以下のコードで、1ms の分解能が得られることを確認できます。
（Windows 10 で確認）

```java
for(var i = 0; i < 1000; i++) {
    System.out.println(System.currentTimeMillis());
}
```
```text:実行結果
…（中略）…
1667107783815
1667107783815
1667107783816
1667107783816
…（中略）…
1667107783817
1667107783817
1667107783818
```

## Linux での実装の変化

昔は、 `System.currentTimeMillis()` の実装に [gettimeofday](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/gettimeofday.2.html) を使っていました。
しかし、 **Java 15 から `clock_gettime` が使える場合は、こちらが使われるようになりました。**
* チケット：[\[JDK-8242504\] Enhance the system clock to nanosecond precision - Java Bug System](https://bugs.openjdk.org/browse/JDK-8242504)
* コミット：[8242504: Enhance the system clock to nanosecond precision · openjdk/jdk@7228978](https://github.com/openjdk/jdk/commit/7228978b19784efa02bde72db747d583fe516cbd)

また、現在のJava がサポートしている環境は `clock_gettime` が使えることが保証されていることが確認できたので、 **Java 17 で `gettimeofday` を使う処理は削除されました。**
* チケット：[\[JDK-8246112\] Remove build-time and run-time checks for clock_gettime and CLOCK_MONOTONIC - Java Bug System](https://bugs.openjdk.org/browse/JDK-8246112?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&showAll=true)
* コミット：[8246112: Remove build-time and run-time checks for clock_gettime and … · openjdk/jdk@6f2be9c](https://github.com/openjdk/jdk/commit/6f2be9c6086cf2d45190c1c8b862a1e143a43de5)