「ミリ秒変換」で何をしたいのかイメージする
「処理時間をミリ秒で計測したい」「DBには long のミリ秒で保存されている」「外部APIが“1970年からのミリ秒”で送ってくる」。
こういうときに必要になるのが、日付・時間と「ミリ秒」の相互変換です。
Java では、昔から System.currentTimeMillis() がよく使われてきました。
これは「1970-01-01T00:00:00Z(UTC)」からの経過ミリ秒、いわゆる“エポックミリ秒”です。
今の Java 日付・時間API(Instant や LocalDateTime など)と、このエポックミリ秒をどう行き来するかを整理しておくと、業務コードがかなり書きやすくなります。
エポックミリ秒とは何か
1970-01-01T00:00:00Z を基準にした「経過時間」
エポックミリ秒は、「1970-01-01T00:00:00Z(UTC)」からの経過時間をミリ秒単位で表したものです。
例えば、0 はちょうど 1970-01-01T00:00:00Z。1_000 は 1 秒後。1_000 * 60 * 60 * 24 は 1 日後、という具合です。
System.currentTimeMillis() は、まさにこのエポックミリ秒を返します。
public class CurrentTimeMillisExample {
public static void main(String[] args) {
long millis = System.currentTimeMillis();
System.out.println("現在のエポックミリ秒: " + millis);
}
}
Javaここで重要なのは、「この値は“UTC基準”であり、タイムゾーンの影響を受けない」ということです。
ローカルタイム(JST など)に変換するかどうかは、あとで別途決めます。
Instant とミリ秒の相互変換
Instant → ミリ秒
Instant は「UTC 上の瞬間」です。
これをエポックミリ秒に変換するのはとても簡単で、toEpochMilli() を使います。
import java.time.Instant;
public class InstantToMillis {
public static void main(String[] args) {
Instant now = Instant.now();
long millis = now.toEpochMilli();
System.out.println("Instant : " + now);
System.out.println("ミリ秒 : " + millis);
}
}
Javaここで押さえておきたいのは、「内部では Instant を使い、保存や通信のときだけミリ秒にする」というスタイルです。Instant はナノ秒精度を持っていますが、toEpochMilli() するとミリ秒に丸められます。
ミリ秒 → Instant
逆に、エポックミリ秒から Instant を作るときは Instant.ofEpochMilli を使います。
import java.time.Instant;
public class MillisToInstant {
public static void main(String[] args) {
long millis = 1_742_950_000_000L; // 例
Instant instant = Instant.ofEpochMilli(millis);
System.out.println("ミリ秒 : " + millis);
System.out.println("Instant: " + instant);
}
}
Javaこの二つを覚えておけば、「ミリ秒 ⇔ Instant」の変換はほぼ怖くなくなります。
ミリ秒とローカル日時(JSTなど)の変換
ミリ秒 → 日本時間の LocalDateTime
DB にエポックミリ秒が入っていて、それを日本時間の日時として表示したい、というケースです。
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class MillisToJstDateTime {
public static void main(String[] args) {
long millis = 1_742_950_000_000L; // 例
Instant instant = Instant.ofEpochMilli(millis);
ZoneId jst = ZoneId.of("Asia/Tokyo");
ZonedDateTime jstZdt = instant.atZone(jst);
LocalDateTime jstLocal = jstZdt.toLocalDateTime();
System.out.println("ミリ秒 : " + millis);
System.out.println("Instant(UTC) : " + instant);
System.out.println("JST ZonedDateTime: " + jstZdt);
System.out.println("JST LocalDateTime: " + jstLocal);
}
}
Java流れとしては、
ミリ秒 → Instant → JST の ZonedDateTime → LocalDateTime
というステップです。
ここで深掘りしたいのは、「ミリ秒はあくまで UTC 基準の“瞬間”であり、JST かどうかは後から決める」という感覚です。
タイムゾーンを変えれば、同じミリ秒でも別の国の時刻として表示できます。
日本時間の LocalDateTime → ミリ秒
今度は逆に、「画面で入力された日本時間の日時をミリ秒で保存したい」ケースです。
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class JstDateTimeToMillis {
public static void main(String[] args) {
LocalDateTime jstLocal = LocalDateTime.of(2025, 3, 26, 10, 5, 30);
ZoneId jst = ZoneId.of("Asia/Tokyo");
ZonedDateTime jstZdt = jstLocal.atZone(jst);
long millis = jstZdt.toInstant().toEpochMilli();
System.out.println("JST LocalDateTime: " + jstLocal);
System.out.println("JST ZonedDateTime: " + jstZdt);
System.out.println("エポックミリ秒 : " + millis);
}
}
Javaここでのポイントは、「LocalDateTime にタイムゾーンをかぶせてから Instant にし、そのうえでミリ秒にする」ということです。
LocalDateTime 単体では「どこの国の時間か」が分からないので、必ず ZoneId を通してあげます。
処理時間計測とミリ秒
開始時刻と終了時刻の差をミリ秒で出す
「この処理に何ミリ秒かかったか」を測りたいときにも、ミリ秒変換はよく使われます。
public class ElapsedMillisExample {
public static void main(String[] args) {
long start = System.currentTimeMillis();
doHeavyWork();
long end = System.currentTimeMillis();
long elapsed = end - start;
System.out.println("処理時間(ミリ秒): " + elapsed);
}
private static void doHeavyWork() {
try {
Thread.sleep(500); // 0.5秒だけ待つ
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
JavaSystem.currentTimeMillis() は「壁時計の時間」なので、
OS の時刻調整の影響を受ける可能性があります。
純粋に経過時間だけを測りたい場合は、System.nanoTime() を使う方が安全です。
public class ElapsedNanosExample {
public static void main(String[] args) {
long start = System.nanoTime();
doHeavyWork();
long end = System.nanoTime();
long elapsedNanos = end - start;
long elapsedMillis = elapsedNanos / 1_000_000L;
System.out.println("処理時間(ミリ秒換算): " + elapsedMillis);
}
private static void doHeavyWork() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
Javaここで深掘りしたいのは、「時刻そのもの」と「経過時間」は別物だ、ということです。
時刻は Instant や System.currentTimeMillis()、経過時間は Duration や System.nanoTime() といった具合に、用途に応じて使い分けるとバグが減ります。
Duration とミリ秒の変換
Duration → ミリ秒
Duration は「時間の長さ」を表すクラスです。
これをミリ秒に変換するには toMillis() を使います。
import java.time.Duration;
public class DurationToMillis {
public static void main(String[] args) {
Duration d = Duration.ofMinutes(2).plusSeconds(30); // 2分30秒
long millis = d.toMillis();
System.out.println("Duration : " + d);
System.out.println("ミリ秒 : " + millis); // 150000
}
}
Javaミリ秒 → Duration
逆に、ミリ秒から Duration を作るときは Duration.ofMillis を使います。
import java.time.Duration;
public class MillisToDuration {
public static void main(String[] args) {
long millis = 150_000L;
Duration d = Duration.ofMillis(millis);
System.out.println("ミリ秒 : " + millis);
System.out.println("Duration: " + d); // PT2M30S
}
}
Java「処理時間をミリ秒で測るけれど、内部では Duration で扱う」といった設計にすると、
「秒に直したい」「分に直したい」といった変換も簡単になります。
セキュリティ・ログ設計とミリ秒
ログの“粒度”をどうするか
セキュリティインシデント調査では、ログの時刻が「どれくらい細かいか」が重要になります。
ミリ秒まで記録しておけば、イベントの順序をかなり正確に追えますが、
一方で「ユーザーの行動パターンを非常に細かく追跡できてしまう」という側面もあります。
設計としては、例えば次のような考え方があります。
内部的には Instant(ナノ秒精度)で持つ。
ログ出力はミリ秒までにする。
画面表示は秒まで、あるいは分までに丸める。
こうして「内部の精度」と「外に見せる精度」を分けておくと、
必要なときには高精度な情報を使いつつ、普段は過剰な情報を出さない、というバランスが取りやすくなります。
ミリ秒変換ユーティリティを用意しておき、「ログに出すときは必ずここを通す」と決めておくと、
粒度の統一や変更もやりやすくなります。
ミリ秒変換ユーティリティとしてまとめる
「Instant ⇔ ミリ秒」「JST ⇔ ミリ秒」を一か所に閉じ込める
ミリ秒変換も、あちこちで同じようなコードを書きがちなので、ユーティリティにまとめておくと実務でかなり楽になります。
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class MillisUtils {
private static final ZoneId JST = ZoneId.of("Asia/Tokyo");
public static long instantToMillis(Instant instant) {
return instant.toEpochMilli();
}
public static Instant millisToInstant(long millis) {
return Instant.ofEpochMilli(millis);
}
public static long jstLocalToMillis(LocalDateTime localJst) {
ZonedDateTime zdt = localJst.atZone(JST);
return zdt.toInstant().toEpochMilli();
}
public static LocalDateTime millisToJstLocal(long millis) {
Instant instant = Instant.ofEpochMilli(millis);
return instant.atZone(JST).toLocalDateTime();
}
}
Java呼び出し側は、例えばこう書けます。
LocalDateTime jst = LocalDateTime.of(2025, 3, 26, 10, 5, 30);
long millis = MillisUtils.jstLocalToMillis(jst);
LocalDateTime back = MillisUtils.millisToJstLocal(millis);
Java「ミリ秒を触るときは必ず MillisUtils を通す」と決めてしまえば、
エポックの基準やタイムゾーンの扱いを間違えるリスクが一気に減ります。
まとめ:ミリ秒変換で身につけてほしい感覚
ミリ秒変換は、「エポックミリ秒」と「日時オブジェクト(Instant や LocalDateTime)」を行き来することです。
エポックミリ秒は「1970-01-01T00:00:00Z からの経過時間」であり、UTC 基準である。
“瞬間”として扱うなら Instant を使い、toEpochMilli と ofEpochMilli でミリ秒と相互変換する。
ローカル時間(JST など)とは、ZoneId と ZonedDateTime を経由して変換する。
処理時間の計測には、System.currentTimeMillis() だけでなく System.nanoTime() や Duration も選択肢に入れる。
もしあなたのコードのどこかに、「1970年からのミリ秒らしい long を、直接割り算して日付を計算している」ような箇所があれば、
そこを一度 Instant や ZonedDateTime ベースのミリ秒変換に置き換えられないか眺めてみてください。
その小さな置き換えが、
“時間にもセキュリティにも強いエンジニア”への、かなり実務的で確かな一歩になります。
