はじめに:なぜJavaの日付はこんなに複雑なのか?
はじめまして、学生エンジニアの@huyunokiです。
Javaの学習を始めた皆さんは日付や時刻を扱おうとしてDate、Calendar、LocalDateTimeと似たようなクラスがたくさん出てきて混乱しませんでしたか?
「とりあえず動けばいい」と適当に選ぶと実務で思わぬバグ(特に時差や月またぎ)の原因になります。
この記事は「なぜJavaには3つもクラスがあるのか?」という疑問を解決し「どの場面でどれを使えばいいか」を一瞬で判断できる最強の早見表を目的としています。
この記事を読めば、あなたはJavaのモダンな日付APIを使いこなし将来のバグを未然に防げるようになります。
結論:まずはこれだけ覚えよう!3クラスの立ち位置
まず結論から。現在のJava開発では、原則としてJava 8以降で導入された「新しいAPI」を使うべきです。
| 代表クラス | 主な用途 | なぜ新しいAPIを使うべきか |
|---|---|---|
LocalDate, LocalDateTime |
原則これを使う! 日付計算、表示、ロジック処理など、ほぼ全て。 | 不変でスレッドセーフ。直感的でバグが少ない。 |
Calendar |
過去の資産との連携が必要な時のみ。 | 可変でスレッドセーフではない。古いコードの修正時などに限定。 |
Date |
タイムスタンプ(ミリ秒)としてデータを受け渡しする時のみ。 | 可変で使いにくい。一部では非推奨という声も。 |
圧倒的早見表!3世代クラスの「使い分け」と「基本関数」
ここからは、各クラスの具体的な特徴と使い方を見ていきましょう。
1. モダンAPI (java.time.* パッケージ)
Java 8で導入された「不変(Immutable)」なクラス群です。一度値をセットすると変更できず、安全性が高いのが特徴です。
| クラス名 | 保持する情報 | 開発でよく使う関数(例) | 使い分けのポイント |
|---|---|---|---|
LocalDate |
日付のみ (年/月/日) |
now(): 今日の日付を取得plusDays(int): 日付を加算getDayOfWeek(): 曜日を取得 |
「何時何分」が不要な時(例:誕生日、締め切り日)。 |
LocalTime |
時刻のみ (時/分/秒/ナノ秒) |
now(): 現在時刻を取得of(h, m): 特定時刻を生成isBefore(LocalTime): 時刻の比較 |
「今日の日付」が不要な時(例:営業開始時間)。 |
LocalDateTime |
日付 + 時刻 |
now(): 現在の日付と時刻を取得format(DateTimeFormatter): 任意の形式で表示 |
「いつの何時何分」が必要で、 タイムゾーンが関係ない時(これはタイムゾーンを保持しないため、実際の瞬間を表せないからである) |
ZonedDateTime |
日付 + 時刻 + タイムゾーン |
now(ZoneId): ゾーンを指定して取得withZoneSameInstant(): タイムゾーン変換 |
国際的なシステムや、夏時間を扱う時(実務で非常に重要)。 |
処理例 (LocalDateTime)
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// 例として 2025年11月17日 15:30:00 の実行結果を想定
public class ModernDateTimeExample {
public static void main(String[] args) {
// 1. 現在の日時を取得
LocalDateTime now = LocalDateTime.now();
System.out.println("現在日時: " + now);
// -> 現在日時: 2025-11-17T15:30:00.123
// 2. 5日後を計算(不変なので、新しいインスタンスが生成される)
LocalDateTime nextWeek = now.plusDays(5);
System.out.println("5日後: " + nextWeek);
// -> 5日後: 2025-11-22T15:30:00.123
// 3. 5日後の曜日を取得
DayOfWeek day = nextWeek.getDayOfWeek();
System.out.println("5日後の曜日: " + day);
// -> 5日後の曜日: SATURDAY
// 4. フォーマットを指定して表示
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println("整形後: " + nextWeek.format(formatter));
// -> 整形後: 2025/11/22 15:30:00
}
}
2. レガシーAPI (Calendar / Date)
第1世代の欠点を補うために登場しましたが、可変であることや Calendarが月を0から数える(0月=1月)など直感的でない仕様がありあまり推奨されない。
| クラス名 | 欠点 | 実務でよく使う関数(例) | 使い分けのポイント |
|---|---|---|---|
Calendar |
可変(一度作ったオブジェクトが変更可能) スレッドセーフではない |
getInstance(): 現在時刻で取得add(フィールド, 量): 日付を加算/減算 |
古いフレームワークや外部APIがこの型を求めてくる場合のみ。 |
Date |
可変(非推奨メソッド多数) 保持するのはミリ秒のみ |
getTime(): 1970/1/1からのミリ秒を取得 |
データベースや外部APIとのタイムスタンプ(数値)の受け渡しに限定。 |
処理例(Calendar)
import java.util.Calendar;
import java.util.Date;
// 例として 2025年11月17日 15:30:00 の実行結果を想定
public class LegacyDateTimeExample {
public static void main(String[] args) {
// 1. Date型はミリ秒で表現される
Date now = new Date();
System.out.println("現在日時(Date): " + now);
// -> 現在日時(Date): Mon Nov 17 15:30:00 JST 2025 (※表示は環境依存)
// 2. Calendarの取得
Calendar cal = Calendar.getInstance();
System.out.println("現在日時(Calendar): " + cal.getTime());
// -> 現在日時(Calendar): Mon Nov 17 15:30:00 JST 2025
// 3. 5日後を計算
cal.add(Calendar.DAY_OF_MONTH, 5); // calオブジェクト自身が変更される(可変)
System.out.println("5日後(Calendar): " + cal.getTime());
// -> 5日後(Calendar): Sat Nov 22 15:30:00 JST 2025
// 4. 月を取得 (※0から始まることに注意!11月は10となる)
int month = cal.get(Calendar.MONTH);
System.out.println("5日後の月(0から始まる): " + month);
// -> 5日後の月(0から始まる): 10
}
}
実務で絶対避けるべき!日付処理の地雷
Java初心者が実務に入る前に知っておくべき、日付処理の大きな落とし穴を2つ伝えます。
1. 「==」で日付オブジェクトを比較してはいけない
文字列と同じで DateやLocalDateTimeなどのオブジェクトを比較する際は ==(参照の比較)ではなく、必ず equals() メソッド(値の比較)を使いましょう。
| 悪い例 | 良い例 |
|---|---|
| if (date1 == date2) | if (date1.equals(date2)) |
2. Date型をそのまま表示・計算に使ってはいけない
Date型は、内部的にミリ秒という数値を持っているだけで、System.out.println(date)で表示される文字列は実行環境のタイムゾーンに依存します。計算も難しいため、Date型を受け取ったらすぐにLocalDateTimeなどに変換してから使いましょう。
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
// 【推奨】変換してから使う
Date legacyDate = new Date(); // Date型で受け取った
LocalDateTime modernDate = legacyDate.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
// これで安全に計算や表示ができる
System.out.println(modernDate.plusMonths(1));
// -> 2025-12-17T15:30:00.123 (安全に翌月を計算できる)
まとめ:今日からあなたはモダンJava使い!
Javaの日付・時刻クラスの使い分けは現代的な開発においてコードの安全性(バグの少なさ)に直結します。
-
原則:
java.timeパッケージを使う! - 理由: 不変だから、意図しない書き換えのバグを防げる!
-
レガシー:
DateやCalendarは、古いコードや外部API連携時のみ使うと割り切る!
この早見表が、皆さんのJava学習を加速させるカンニングペーパーになれば嬉しいです。