Javaの日付・時間API(java.time)を徹底解説!LocalDateとLocalDateTimeの使い方
生徒
「Javaで今日の日付を表示させたいんですけど、昔のやり方は難しいって聞きました。今はどうするのが正解なんですか?」
先生
「そうですね。昔はDateクラスやCalendarクラスを使っていましたが、現在はJava 8以降で導入された『Date-Time API(java.timeパッケージ)』を使うのが標準ですよ。」
生徒
「java.timeですか。LocalDateとかLocalDateTimeとか似たような名前がいっぱいあって、どれを使えばいいか迷っちゃいます。」
先生
「役割がしっかり分かれているので、一度整理すれば簡単です。基本から具体的なコードの書き方まで、詳しく解説していきましょう!」
1. Javaの日付・時間APIの基本とメリット
Javaで日付や時間を扱う際、かつてはjava.util.Dateやjava.util.Calendarが使われていました。しかし、これらは「設計が複雑」「スレッドセーフではない」「月のカウントが0から始まる」といった多くの課題を抱えていました。これらを解決するために登場したのが、java.timeパッケージに含まれる新しいAPIです。
この新しいAPIの大きな特徴は、不変性(イミュータブル)です。一度作成した日付オブジェクトは変更されず、変更を加える操作をすると新しいオブジェクトが生成されます。これにより、複数の処理が同時に動くプログラムでも安全に利用できるというメリットがあります。また、メソッド名が直感的になり、「今日の日付を取得するならnow()」「特定の日付を作るならof()」といったように、初心者でも理解しやすい設計になっています。
さらに、国際的な標準規格であるISO-8601に基づいているため、他システムとのデータ連携もスムーズに行えます。現代のJava開発において、日付や時間を扱うならこのjava.timeをマスターすることは必須と言えるでしょう。
2. LocalDateで日付のみを管理する方法
LocalDateは、時間やタイムゾーンの情報を含まず、「年・月・日」という日付情報のみを扱うクラスです。誕生日や記念日、有効期限のように、具体的な時刻を必要としないデータを扱う際に最適です。
例えば、システムが動いている当日の日付を取得したり、特定の日付(例:2025年12月25日)をプログラムの中で指定したりする場合に使用します。内部的には「2025-12-25」のような形式で保持されます。以下のコードで、基本的な使い方を確認してみましょう。
import java.time.LocalDate;
public class LocalDateExample {
public static void main(String[] args) {
// 現在の日付を取得
LocalDate today = LocalDate.now();
System.out.println("今日の日付: " + today);
// 特定の日付を指定して作成 (2025年12月25日)
LocalDate christmas = LocalDate.of(2025, 12, 25);
System.out.println("今年のクリスマス: " + christmas);
}
}
今日の日付: 2026-01-05
今年のクリスマス: 2025-12-25
このように、LocalDate.now()を使えば一瞬で実行時の日付が取得できます。月を指定する際も、古いCalendarクラスのように「1月を0とする」必要はなく、そのまま「12」と書けば12月として認識されるため、ミスが少なくなります。
3. LocalTimeで時刻のみを精密に扱う
日付は不要で、「何時何分何秒」という時刻だけが必要な場合には、LocalTimeクラスを使用します。営業開始時間や、アラームの設定、特定のスケジュールが開始されるタイミングなどを管理するのに適しています。
LocalTimeは、ナノ秒単位まで精度を持っており、非常に細かな時間計測も可能です。もちろん、秒やナノ秒を省略して「15時30分」といったシンプルな指定も可能です。まずは現在時刻を取得する方法と、任意の時刻を生成する方法を見ていきましょう。
import java.time.LocalTime;
public class LocalTimeExample {
public static void main(String[] args) {
// 現在の時刻を取得
LocalTime nowTime = LocalTime.now();
System.out.println("現在の時刻: " + nowTime);
// 指定した時刻を作成 (14時30分00秒)
LocalTime targetTime = LocalTime.of(14, 30, 0);
System.out.println("指定した時刻: " + targetTime);
}
}
現在の時刻: 14:36:18.123456789
指定した時刻: 14:30
出力結果の「14:36:18.123456789」のように、実行環境によっては非常に細かい単位まで表示されます。ビジネスロジックで「分」までしか使わない場合は、後述するフォーマット機能を使って見た目を整えるのが一般的です。
4. LocalDateTimeで日付と時刻を組み合わせて使う
実務で最も頻繁に利用されるのが、このLocalDateTimeです。名前の通り、LocalDateとLocalTimeの両方の情報を兼ね備えており、「2025年10月10日の午前10時」といった情報を一つのオブジェクトで保持できます。
予約システムの予約日時、SNSの投稿日時、ログの出力タイミングなど、日付と時刻をセットで記録する必要があるシーンでは、このクラスを選択すれば間違いありません。LocalDateとLocalTimeを組み合わせてLocalDateTimeを作ることも可能ですし、最初から「今」をまるごと取得することもできます。
import java.time.LocalDateTime;
import java.time.Month;
public class LocalDateTimeExample {
public static void main(String[] args) {
// 現在の日付と時刻を取得
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("現在日時: " + currentDateTime);
// 詳細な日時を指定して作成 (2025年1月1日 0時0分)
LocalDateTime newYear = LocalDateTime.of(2025, Month.JANUARY, 1, 0, 0);
System.out.println("2025年の元旦: " + newYear);
}
}
現在日時: 2026-01-05T14:36:18.123456789
2025年の元旦: 2025-01-01T00:00
出力の中に「T」という文字が入っていますが、これはISO-8601規格で日付と時刻の区切りを示す記号です。表示する際は、プログラミング側で適切に整形してユーザーに見せることが多いです。
5. 日付の計算や比較を簡単に行う便利なメソッド
Javaの新しい日付APIが便利な理由の一つに、日付の計算が非常に容易である点が挙げられます。「3日後」「1ヶ月前」「1週間後」といった計算を、直感的なメソッド名で実行できます。古いCalendarクラスで行っていた複雑な計算式はもう必要ありません。
代表的なメソッドには、加算を行うplusDays、plusMonths、plusYearsや、減算を行うminusWeeksなどがあります。また、日付の前後関係を判定するisBeforeやisAfterといったメソッドも用意されており、条件分岐もスムーズに記述できます。
import java.time.LocalDate;
public class DateCalculationExample {
public static void main(String[] args) {
LocalDate baseDate = LocalDate.of(2025, 5, 20);
// 1週間後の日付を取得
LocalDate nextWeek = baseDate.plusWeeks(1);
System.out.println("1週間後: " + nextWeek);
// 2ヶ月前の日付を取得
LocalDate twoMonthsAgo = baseDate.minusMonths(2);
System.out.println("2ヶ月前: " + twoMonthsAgo);
// 日付の比較
boolean isBefore = baseDate.isBefore(nextWeek);
System.out.println("2025/5/20は1週間後より前か?: " + isBefore);
}
}
1週間後: 2025-05-27
2ヶ月前: 2025-03-20
2025/5/20は1週間後より前か?: true
これらのメソッドは、元のオブジェクトを書き換えるのではなく、計算結果を持った「新しいオブジェクト」を返します。この不変性の性質により、計算の途中で意図せずデータが書き換わるバグを防ぐことができます。
6. 日付のフォーマットと文字列変換(DateTimeFormatter)
システム内部で保持している日付オブジェクトを、画面に表示したり帳票に出力したりする際には、「2025/05/20」や「2025年5月20日(火)」といった特定の形式に変換する必要があります。この変換を担うのがDateTimeFormatterクラスです。
逆に、ユーザーが入力した文字列(例:"2025-10-31")をJavaの日付オブジェクトに変換(パース)する場合も、このクラスを使用します。フォーマットのパターンを指定することで、自由自在な見た目にカスタマイズ可能です。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class FormatExample {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
// フォーマットを指定 (例:2025/12/31 15:30:45)
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
// 日時を文字列に変換
String formattedDate = now.format(formatter);
System.out.println("フォーマット後: " + formattedDate);
// 文字列から日時オブジェクトに変換
String dateStr = "2025-12-25 10:00:00";
LocalDateTime parsedDate = LocalDateTime.parse(dateStr, formatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println("解析後の日時: " + parsedDate);
}
}
フォーマット後: 2026/01/05 14:36:18
解析後の日時: 2025-12-25T10:00
ofPatternで指定する記号(yは年、Mは月、dは日、Hは24時間制の時など)は、大文字と小文字で意味が異なるため注意が必要です。例えば「mm」は「分」ですが、「MM」は「月」を意味します。ここを間違えると全く異なる日付として処理されてしまうため、公式ドキュメントを確認しながら正確に指定しましょう。
7. ZonedDateTimeでタイムゾーンを考慮する
グローバルなシステムを構築する場合、日本の時刻だけでなく、ニューヨークやロンドンの時刻も扱う必要があります。LocalDateTimeは「ローカル」な日時、つまりどこか特定の場所(タイムゾーン)を意識しない日時を扱いますが、ZonedDateTimeを使えば「東京の2025年1月1日」といった具体的な地域情報を持たせることができます。
タイムゾーンを跨ぐ計算(例:日本時間からロンドン時間を算出する)や、サーバーの設置場所に関係なく絶対的な時間を記録したい場合に非常に強力です。世界標準時(UTC)との時差も自動的に計算してくれるため、複雑な計算を自分で行う必要はありません。
import java.time.ZonedDateTime;
import java.time.ZoneId;
public class ZonedDateTimeExample {
public static void main(String[] args) {
// 日本の現在日時を取得
ZonedDateTime japanTime = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println("日本の現在時刻: " + japanTime);
// ニューヨークの現在日時を取得
ZonedDateTime nyTime = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println("ニューヨークの現在時刻: " + nyTime);
}
}
日本の現在時刻: 2026-01-05T14:36:18.123456+09:00[Asia/Tokyo]
ニューヨークの現在時刻: 2026-01-05T00:36:18.123456-05:00[America/New_York]
出力に含まれる「+09:00」は、世界標準時から9時間進んでいることを示しています。このように、場所による時間のズレを正確に管理できるのがZonedDateTimeの強みです。初心者の方は、まずはLocalDateとLocalDateTimeに慣れてから、必要に応じてこのクラスを学習すると良いでしょう。
8. 期間を測るためのクラス:PeriodとDuration
「特定の日付から特定の日付まで、何日間あるか?」や「ある処理に何秒かかったか?」といった「期間」を扱いたい場面があります。JavaのDate-Time APIでは、日付ベースの期間にはPeriod、時間ベースの期間にはDurationという2つのクラスを使い分けます。
Periodは「2年と3ヶ月と10日間」といったカレンダー上の差分を扱い、Durationは「48時間や3600秒」といった絶対的な時間の長さを扱います。これらを使いこなすことで、年齢の計算や有効期限の残り日数の算出が非常にシンプルになります。
import java.time.LocalDate;
import java.time.Period;
public class PeriodExample {
public static void main(String[] args) {
LocalDate start = LocalDate.of(2023, 1, 1);
LocalDate end = LocalDate.of(2025, 5, 20);
// 2つの日付の差を計算
Period period = Period.between(start, end);
System.out.println("期間: " + period.getYears() + "年 "
+ period.getMonths() + "ヶ月 "
+ period.getDays() + "日間");
}
}
期間: 2年 4ヶ月 19日間
このように、Period.betweenメソッドを使うだけで、複雑なうるう年などの計算を気にすることなく正確な期間を導き出せます。時間の差を求めたい場合は、同様にDuration.betweenを使用すれば、ナノ秒単位までの正確な経過時間を取得することが可能です。
9. 実務でよくある日付操作の逆引きリファレンス
最後に、実際のプログラミング現場でよく遭遇するニーズに対する解決策をまとめておきます。これらを知っておくと、コーディングのスピードが格段にアップします。
- 月末の日付を知りたい:
TemporalAdjusters.lastDayOfMonth()を使用します。 - 翌週の月曜日を取得したい:
TemporalAdjusters.next(DayOfWeek.MONDAY)を使用します。 - 日付の妥当性チェック:
LocalDate.parseをtry-catchで囲み、DateTimeParseExceptionが発生するかどうかで判定できます。 - エポックミリ秒(long型)への変換:
ZonedDateTime.toInstant().toEpochMilli()で、古いDateクラスや外部APIと連携するためのミリ秒形式を取得できます。
Java 8より前の時代には、こうした操作を行うために「その月の1日を求めてから1ヶ月足して1日引く」といったトリッキーなコードを書く必要がありました。しかし現在のAPIでは、目的をそのままコードに書けるようなメソッドが豊富に用意されています。積極的に活用して、読みやすくバグの少ないプログラムを目指しましょう。