本ページはプロモーションが含まれています

Java入門

Java Durationの使い方|時間計算の基本からコード例付きで解説

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

JavaのDurationクラスは、「5分」「10秒」といった時間の長さを型安全に扱うためのクラスです。

Java 8のDate and Time APIで導入され、従来の5 * 60 * 1000のようなミリ秒計算をDuration.ofMinutes(5)と直感的に書けるようになりました。

この記事では、Durationの基本(of系メソッド・between・変換メソッド)からPeriodとの使い分け、処理時間計測の実装パターン、よくあるエラーの対処法までをコード例付きで解説します。

JavaのDurationとは?仕組みと役割をわかりやすく解説

JavaのDurationの仕組みと役割を示す図解

JavaのDurationとは、2つの時刻の「間隔(期間)」を表すクラスです。

結論から言えば、数秒や数分といった「時間の長さ」を扱うなら、必ずこのクラスを使うべきです。

理由は、Durationを使うことで、人間が直感的に理解できる単位で時間をプログラムできるからです。

従来のように「5分」を表すために5 * 60 * 1000のような計算式をコードに書く必要がなくなります。

Durationが表すもの(時刻ではなく“時間量”)

Durationが保持しているのは「ある時点」ではなく「時間の量」です。例えば「10時00分」は時刻ですが、「30分間」は時間量です。

Durationはこの「30分間」や「5時間」「10秒」といった長さを管理します。

内部的には「秒(seconds)」と「ナノ秒(nanos)」の2つの値で時間を保持しています。

これにより、ミリ秒未満の短い計測から数千時間の長期間まで正確に表現できます。

LocalDateTimeとの違いは何か

LocalDateTimeDurationは、根本的に役割が異なります。

  • LocalDateTime: 「2026年1月1日 12時00分」というを表します。
  • Duration: 「5時間30分」という線(長さ)を表します。

この2つは混同されがちです。

カレンダー上の特定の日時を指したい場合はLocalDateTimeを使い、その日時までの残り時間や、処理にかかった時間を表す場合にはDurationを選びます。

役割を明確に分けることで、バグの少ないコードが書けるようになります。

Durationが使われる典型シーン

開発現場において、Durationは以下のような場面で頻繁に登場します。

  • タイムアウト設定: 「接続待ち受け時間は10秒まで」という制限時間を設定する場合。
  • 処理時間の計測: 「このバッチ処理に何分かかったか」をログに出力する場合。
  • トークンの有効期限: 「発行から1時間は有効」といった有効期間の計算。
  • リトライ間隔: 「エラーが起きたら500ミリ秒待ってから再試行する」という待機時間の制御。

これらのシーンでは、数値を直接扱うよりもDurationオブジェクトとして渡すほうが、単位の取り違えを防げます。

私の実務経験では、REST APIクライアントのタイムアウト設定でDurationを最も頻繁に使用しています。

Spring BootのRestTemplateWebClientでは、接続タイムアウトと読み取りタイムアウトをDuration型で指定するのが標準的な書き方です。

Durationの基本的な使い方|生成・変換・計算をコード例で解説

Durationの生成・変換・計算の基本的な使い方を示すコード例の図解

ここからは実際のコードを見ながら使い方を解説します。java.time.Durationをインポートして利用します。

基本的な操作は非常にシンプルで、直感的に記述できる設計になっています。

Duration.ofで時間を生成する(ofSeconds / ofMinutes など)

Durationオブジェクトを生成するには、静的メソッドであるofシリーズを使います。

// 10秒のDurationを生成
Duration tenSeconds = Duration.ofSeconds(10);

// 5分のDurationを生成
Duration fiveMinutes = Duration.ofMinutes(5);

// 2時間のDurationを生成
Duration twoHours = Duration.ofHours(2);

// 500ミリ秒のDurationを生成
Duration halfSecond = Duration.ofMillis(500);

メソッド名に単位が含まれているため、コードを読んだだけで「どのくらいの時間量なのか」がひと目でわかります。

数字の「10」だけが書かれているコードとは、可読性に雲泥の差があります。

betweenで2つの日時からDurationを算出する

2つの日時の差分を求めたい場合は、Duration.between()を使います。これは非常に強力なメソッドです。

開始時刻と終了時刻を渡すだけで、その差を自動的に計算してくれます。

LocalDateTime start = LocalDateTime.of(2026, 1, 1, 10, 0, 0);
LocalDateTime end = LocalDateTime.of(2026, 1, 1, 12, 30, 0);

// startとendの差(2時間30分)を算出
Duration diff = Duration.between(start, end);

このメソッドのおかげで、日を跨ぐ計算でも、時刻間の正確な差分を自前で実装する必要がなくなりました。

ただし、Durationは物理的な時間差(秒・ナノ秒)を計算するため、うるう年や月の日数差を考慮した「カレンダー上の期間」が必要な場合はPeriodを使います。

toMillis / toMinutes など変換メソッドの使い方

生成したDurationを、特定の単位(ミリ秒や分など)の数値に戻したい場合も多々あります。その際はtoシリーズのメソッドを使います。

Duration duration = Duration.ofMinutes(2);

// 全体をミリ秒に変換(120000が表示される)
long millis = duration.toMillis();

// 全体を秒に変換(120が表示される)
long seconds = duration.toSeconds();

// 全体を分に変換(2が表示される)
long minutes = duration.toMinutes();

ここで注意すべき点は、計算結果が整数(long型)で返されることです。

例えば「90秒」のDurationに対してtoMinutes()を実行すると、1.5ではなく「1」が返されます。

端数は切り捨てられる仕様を理解して使いましょう。

Duration.parseでISO-8601文字列からDurationを生成する

設定ファイルやAPIレスポンスから時間の値を文字列で受け取る場合、Duration.parse()が役立ちます。

ISO-8601形式の文字列をDurationオブジェクトに変換できます。

// "PT5M30S" → 5分30秒のDuration
Duration parsed = Duration.parse("PT5M30S");

// "PT2H" → 2時間のDuration
Duration twoHours = Duration.parse("PT2H");

// "PT0.5S" → 500ミリ秒のDuration
Duration halfSec = Duration.parse("PT0.5S");

「P」はISO-8601の期間指定子(period designator)、「T」は時間部分の開始を示す区切り文字です。

H(時間)、M(分)、S(秒)を組み合わせて表現します。

不正な形式を渡すとDateTimeParseExceptionがスローされるため、外部入力を受け取る場合はtry-catchで囲みましょう。

plus / minusで時間の加算・減算を行う

Durationはイミュータブル(不変)なオブジェクトです。加算や減算を行うと、元のオブジェクトは変更されず新しいDurationが返されます。

Duration base = Duration.ofMinutes(10);

// 5分を加算 → 15分
Duration added = base.plusMinutes(5);

// 3秒を減算 → 9分57秒
Duration subtracted = base.minusSeconds(3);

// 2倍にする → 20分
Duration doubled = base.multipliedBy(2);

// 半分にする → 5分
Duration halved = base.dividedBy(2);

特にmultipliedBy()dividedBy()は、リトライ間隔の指数バックオフ(1秒→2秒→4秒と倍々で増やす)を実装する際に便利です。

DurationとPeriodの違い

DurationとPeriodの違いを示す比較図解

JavaのDate and Time APIには、時間の量を表すクラスがもう一つあります。それがPeriodです。

「どちらを使えばいいのか」と迷う開発者は少なくありません。それぞれの特性を理解して使い分ける必要があります。

時間単位(秒・ナノ秒)を扱うDuration

Durationは、物理的な時間の長さを扱います。「1日は必ず24時間であり、1時間は必ず60分である」という前提に基づいた計算を行います。

そのため、PCの内部時計やログのタイムスタンプ、ストップウォッチのような計測にはDurationが最適です。

ナノ秒単位の精密な計算が必要な場合は、迷わずこちらを選んでください。

日・月・年を扱うPeriod

一方、Periodはカレンダー上の期間(年月日)を扱います。「1か月」という期間は、28日の場合もあれば31日の場合もあります。

Periodはこのような「カレンダー特有の曖昧さ」を吸収するためのクラスです。

「3か月後の日付を求めたい」「誕生日まであと何日か知りたい」という場合は、Periodを使います。

これをDurationで計算しようとすると、月ごとの日数の違いでズレが生じる可能性があります。

使い分けの基準と判断ポイント

使い分けのルールはシンプルです。

  1. 秒、分、時間単位の計算ならDurationを使う。
  2. 日、月、年単位の計算ならPeriodを使う。

例えば「イベント開催まであと3日」でも、厳密に「72時間」として管理するならDuration、「カレンダー上で3マス先」という意味ならPeriodです。

要件が物理時間かカレンダー時間かで使い分けます。使い分けを図にすると、次のようになります。

flowchart TD A["扱いたい時間の単位は?"] --> B{"日・月・年<br>(カレンダー上の期間)?"} B -->|"はい"| C["Period<br>例: 3か月後の日付、誕生日まであと何日"] B -->|"いいえ"| D{"秒・分・時間<br>(物理的な時間量)?"} D -->|"はい"| E["Duration<br>例: タイムアウト、処理時間計測"]

longではなくDurationを使うべき理由と注意点

longではなくDurationを使うべき理由と注意点を示す図解

long型の変数で時間を管理することも可能ですが、Durationクラスをあえて使うことには明確なメリットがあります。

可読性の高い時間計算ができる

最大のメリットはコードの読みやすさです。以下のようなコード比較を見てみましょう。

悪い例(long型で管理)

long timeout = 300000; // これが5分だと即座にわかりますか?
long startTime = System.currentTimeMillis();
if (System.currentTimeMillis() - startTime > timeout) {
    // タイムアウト処理
}

良い例(Durationで管理)

Duration timeout = Duration.ofMinutes(5);
// 直感的で誤解の余地がない

ofMinutes(5)と書かれていれば、誰が読んでも「5分」だと理解できます。

ゼロの数を数えて「3万ミリ秒か、30万ミリ秒か」と悩む時間がなくなります。コードの意図を明確に伝えるためにDurationは有効です。

longではなくDurationを使うべきケース

メソッドの引数に時間を渡す場合は、必ずDurationを使うべきです。

例えば、void wait(long time)というメソッドがあったとします。このtimeは秒でしょうか、ミリ秒でしょうか。

ドキュメントを読まないとわかりません。しかし、void wait(Duration time)であれば、単位の曖昧さは完全に消滅します。

呼び出し側でDuration.ofSeconds(10)と渡せば、10秒待機することが確定するからです。

型安全性を高め、単位の勘違いによるバグを防ぐために、API設計では積極的にDurationを採用しましょう。

私が過去に関わったプロジェクトでは、メソッド引数のlong timeoutが秒なのかミリ秒なのかをめぐって実際にバグが発生しました。

呼び出し側が「秒」のつもりで30を渡したのに、受け取り側は「ミリ秒」として処理し、わずか30ミリ秒でタイムアウトしていたのです。

Duration型であれば、この種のバグは原理的に発生しません。

マイクロ秒・ナノ秒の扱いで注意すべきポイント

Durationはナノ秒まで扱えますが、すべてのシステムやデータベースがナノ秒に対応しているわけではありません。

Javaプログラム内で完結する計算なら問題ありませんが、データベースに保存したり、JSONとして外部APIに送信したりする際に、桁落ちが発生することがあります。

MySQLは5.6.4以降でマイクロ秒(6桁)まで対応していますが、カラム定義でDATETIME(6)のように精度を明示する必要があります。

PostgreSQLもマイクロ秒までの対応です。ナノ秒が必要な場合はアプリケーション側で文字列やBIGINTに変換して保存する設計が必要です。

システム全体でどの精度まで許容するかを事前に設計段階で決めておく必要があります。

実践例:処理時間(実行時間)の計測にDurationを使う

Durationを使った処理時間の計測の実践例を示す図解

実際の開発現場で最もよく使うパターンの一つが、プログラムの実行速度計測です。

パフォーマンスチューニングや障害調査において、正確な時間計測は欠かせません。

System.nanoTimeとの組み合わせ

処理時間を計測する場合、System.currentTimeMillis()ではなくSystem.nanoTime()を使うのが定石です。

OSの時計変更の影響を受けないからです。

long start = System.nanoTime();

// 計測したい重い処理を実行
doHeavyTask();

long end = System.nanoTime();

// 差分をDurationに変換
Duration executionTime = Duration.ofNanos(end - start);

計測したナノ秒の差分をDuration.ofNanos()に渡すことで、後の扱いが非常に楽になります。

なお、System.currentTimeMillis()System.nanoTime()の使い分けは、計測する時間のスケールによって判断します。

メソッド精度用途OS時計変更の影響
System.currentTimeMillis()ミリ秒日時の取得・ログ出力受ける
System.nanoTime()ナノ秒経過時間の計測受けない
Instant.now()ナノ秒タイムスタンプ+Duration.between()受ける

ログに出すためのフォーマット変換

計測した時間をログ出力する際、単にtoString()を呼ぶと「PT5M10.345S」のようなISO-8601形式の文字列が出力されます。

これは機械には読みやすいですが、人間には少し読みづらい場合があります。

日本人が読みやすい形式に変換するユーティリティメソッドを用意すると便利です。

long min = executionTime.toMinutes();
long sec = executionTime.toSecondsPart();
long millis = executionTime.toMillisPart();

String logMessage = String.format("処理時間: %d分 %d秒 %d", min, sec, millis);
// 出力例: 処理時間: 5分 10秒 345

Java 9以降であれば、toSecondsPart()のように、単位変換後の「端数部分」だけを取得するメソッドが使えます。

2026年現在のLTSであるJava 21・Java 25にも含まれているため、新規プロジェクトでは迷わず使えます。

Java 8環境の場合は、割り算の余り(%)を使って計算する必要があります。

処理時間の比較ロジックにDurationを使う

「処理Aが処理Bより長かった場合」といった比較ロジックも、Durationならスマートに書けます。

if (durationA.compareTo(durationB) > 0) {
    // Aの方が時間がかかっている
}

数値を直接比較するよりも、意図が明確になります。

また、isZero()isNegative()といった状態判定メソッドも用意されているため、条件分岐がすっきりと記述できます。

Durationを扱うときによくあるエラーと対処法

Durationでよくあるエラーと対処法を示す図解

便利に見えるDurationですが、使い方を誤ると予期せぬエラーに遭遇することがあります。事前に落とし穴を知っておけば、回避可能です。

Overflow系エラーが起きるケース

Durationは内部でlong型を使用していますが、扱える時間の長さには上限があります。具体的には約292年分のナノ秒までです。

通常のWebアプリケーション開発で上限に達することはまずありません。

ただし、テストコードで極端な値を渡すとArithmeticExceptionが発生する可能性があるため注意してください。

ChronoUnitとの併用時に注意する点

Durationから値を取得する際、get(TemporalUnit unit)メソッドを使うことができますが、ここで指定できる単位には制限があります。

// これはエラーになります
long months = duration.get(ChronoUnit.MONTHS);

Durationはあくまで「物理的な時間」を扱うため、「月」や「年」といった不定の長さを持つ単位には変換できません。

このコードを実行するとUnsupportedTemporalTypeExceptionがスローされます。

「月」や「年」を扱いたい場合は、前述のPeriodクラスを使ってください。

時間計算の罠(負のDurationなど)

Durationは負の値(マイナスの時間)を持つことができます。

Duration negative = Duration.between(end, start); // 終了時刻と開始時刻を逆にする

計算順序を間違えると、意図せず負の値が生成されます。その後のロジックで「時間が正の数であること」を前提にしていると、バグの原因になります。

計算結果を受け取った後は、必要に応じてabs()メソッドを呼び出し、絶対値に変換する防御的プログラミングを推奨します。

また、Java 18以降ではisPositive()メソッドが追加されており、isNegative()と組み合わせて正負のチェックがより明確に書けるようになっています。

// 常に正の値にする
Duration safeDuration = duration.abs();

まとめ:Java Durationで時間計算を安全かつ簡潔に

Durationクラスは、Javaにおける時間計算の常識を変えました。

理由は、開発者が行うべき面倒な単位変換や計算ロジックを、APIがすべて肩代わりしてくれるからです。

今回紹介した内容を振り返ります。

  • Durationは「時間の量(長さ)」を表すクラスであり、時刻を表すものではありません。
  • ofMinutesbetweenメソッドを使うことで、直感的に時間を生成・算出できます。
  • カレンダー計算(年月日)にはPeriodを使い、物理時間(時分秒)にはDurationを使います。
  • long型の変数で時間を持ち回るよりも、圧倒的に可読性と安全性が向上します。

かつての私のように、ミリ秒の計算でバグを出して頭を抱える必要はもうありません。

明日からのコーディングでは、変数の型をlongからDurationに変えてみてください。

それだけで、あなたのコードは驚くほど美しく、堅牢になるはずです。

  • この記事を書いた人
  • 最新記事

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

-Java入門