タイトル: 日時API
SEOタイトル: Java 日時 API 完全ガイド(java.time / LocalDate / Instant / Duration / 旧 Date と比較)
| この記事の要点 |
|
Java の日時 API 全体像
Java の日時を扱う API はJava 8 を境に大きく変わりました。古い java.util.Date / Calendar / SimpleDateFormat は使いづらく、スレッドセーフでない問題もあり、Java 8 でjava.time パッケージ (JSR-310) が導入されました。
| 世代 | 主要クラス | 用途 |
|---|---|---|
| 旧 (Java 1.0〜) | Date / Calendar | レガシーコード互換 |
| 旧フォーマット | SimpleDateFormat | スレッドアンセーフ |
| 新 (Java 8〜) | java.time.* | 新規コードは全部こちら |
java.time の主要クラス
| クラス | 意味 | 例 |
|---|---|---|
LocalDate | 日付(年月日) | 2026-06-11 |
LocalTime | 時刻(時分秒) | 14:30:00 |
LocalDateTime | 日時(TZ なし) | 2026-06-11T14:30:00 |
ZonedDateTime | タイムゾーン付き日時 | 2026-06-11T14:30+09:00[Asia/Tokyo] |
OffsetDateTime | UTC オフセット付き | 2026-06-11T14:30+09:00 |
Instant | UTC タイムスタンプ | 2026-06-11T05:30:00Z |
Duration | 時間量(秒・ナノ秒) | PT2H30M |
Period | 期間(年月日) | P1Y2M3D |
Year / Month / YearMonth / MonthDay | 部分日付 | 2026 / JUNE / 2026-06 / 06-11 |
DayOfWeek | 曜日 | THURSDAY |
ZoneId | タイムゾーン | Asia/Tokyo |
DateTimeFormatter | 整形・パース | "yyyy/MM/dd" |
基本操作
import java.time.*;
import java.time.format.DateTimeFormatter;
// 現在時刻
LocalDate today = LocalDate.now(); // 2026-06-11
LocalTime nowTime = LocalTime.now(); // 14:30:25.123
LocalDateTime now = LocalDateTime.now();
ZonedDateTime zoned = ZonedDateTime.now();
Instant instant = Instant.now(); // UTC ベース
// 任意日時を作る
LocalDate date = LocalDate.of(2026, 6, 11);
LocalTime time = LocalTime.of(14, 30, 0);
LocalDateTime dt = LocalDateTime.of(2026, 6, 11, 14, 30);
// 文字列からパース (ISO-8601)
LocalDate d = LocalDate.parse("2026-06-11");
LocalDateTime dt2 = LocalDateTime.parse("2026-06-11T14:30:00");
// プロパティ取得
int year = date.getYear(); // 2026
Month month = date.getMonth(); // JUNE
int day = date.getDayOfMonth(); // 11
DayOfWeek dow = date.getDayOfWeek(); // THURSDAY
加減算 (immutable)
java.time のクラスは不変オブジェクトです。plus / minus は新しいインスタンスを返します。
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
LocalDate nextWeek = today.plusWeeks(1);
LocalDate nextMonth = today.plusMonths(1);
LocalDate nextYear = today.plusYears(1);
LocalDate yesterday = today.minusDays(1);
// withXxx で値を差し替え
LocalDate firstOfMonth = today.withDayOfMonth(1);
LocalDate endOfYear = today.withMonth(12).withDayOfMonth(31);
// TemporalAdjusters で柔軟に
import static java.time.temporal.TemporalAdjusters.*;
LocalDate nextMonday = today.with(next(DayOfWeek.MONDAY));
LocalDate lastDayOfMonth = today.with(lastDayOfMonth());
LocalDate firstDayOfYear = today.with(firstDayOfYear());
比較
LocalDate a = LocalDate.of(2026, 1, 1);
LocalDate b = LocalDate.of(2026, 6, 11);
boolean before = a.isBefore(b); // true
boolean after = a.isAfter(b); // false
boolean equal = a.isEqual(b); // false
int cmp = a.compareTo(b); // 負の値
差分 (Duration / Period)
// 日数の差 (Period: 年月日単位)
LocalDate start = LocalDate.of(2026, 1, 1);
LocalDate end = LocalDate.of(2026, 6, 11);
Period p = Period.between(start, end);
System.out.println(p.getMonths() + "ヶ月" + p.getDays() + "日");
// 5ヶ月10日
long days = ChronoUnit.DAYS.between(start, end); // 161
// 時間の差 (Duration: 秒・ナノ秒)
LocalTime t1 = LocalTime.of(9, 0);
LocalTime t2 = LocalTime.of(17, 30);
Duration d = Duration.between(t1, t2);
System.out.println(d.toHours() + "時間" + (d.toMinutes() % 60) + "分");
// 8時間30分
フォーマット (DateTimeFormatter)
LocalDateTime now = LocalDateTime.now();
// ISO 標準
String iso = now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
// 2026-06-11T14:30:00
// カスタムパターン
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String s = now.format(fmt); // 2026/06/11 14:30:00
// 日本語ロケール
DateTimeFormatter jp = DateTimeFormatter.ofPattern("yyyy年M月d日(E)", Locale.JAPAN);
String s2 = now.format(jp); // 2026年6月11日(木)
// パース
LocalDateTime parsed = LocalDateTime.parse("2026/06/11 14:30:00", fmt);
パターン記号
| 記号 | 意味 | 例 |
|---|---|---|
| y | 年 | 2026 / 26 |
| M | 月 (数値) | 06 / 6 |
| MMM | 月 (短縮名) | Jun / 6月 |
| d | 日 | 11 |
| H | 時 (24時間) | 14 |
| h | 時 (12時間) | 02 |
| m | 分 | 30 |
| s | 秒 | 00 |
| E | 曜日 (短縮) | Thu / 木 |
| EEEE | 曜日 (完全) | Thursday / 木曜日 |
| a | AM/PM | PM |
| z | タイムゾーン | JST |
タイムゾーン
// 日本時間
ZoneId tokyo = ZoneId.of("Asia/Tokyo");
ZonedDateTime ztokyo = ZonedDateTime.now(tokyo);
// UTC
ZonedDateTime utc = ZonedDateTime.now(ZoneOffset.UTC);
// タイムゾーン変換
ZonedDateTime ny = ztokyo.withZoneSameInstant(ZoneId.of("America/New_York"));
// 同じ瞬間を NY 時間で表現
// 利用可能なタイムゾーン一覧
Set<String> zones = ZoneId.getAvailableZoneIds();
旧 API との変換
// java.util.Date ⇔ Instant
Date d = new Date();
Instant i = d.toInstant();
Date d2 = Date.from(i);
// Date ⇔ LocalDateTime
LocalDateTime ldt = d.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
Date d3 = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
// java.sql.Date / Time / Timestamp ⇔ LocalDate / LocalTime / LocalDateTime
LocalDate ld = new java.sql.Date(System.currentTimeMillis()).toLocalDate();
LocalDateTime ldt2 = new Timestamp(System.currentTimeMillis()).toLocalDateTime();
java.time vs 旧 API 比較
| 観点 | 旧 (Date/Calendar) | 新 (java.time) |
|---|---|---|
| イミュータブル | × | ○ |
| スレッドセーフ | × | ○ |
| 月の起点 | 0 始まり (Calendar) | 1 始まり |
| API 直感性 | 低い | 高い (流暢な API) |
| タイムゾーン処理 | 煩雑 | 明示的・型安全 |
| 期間表現 | なし | Period / Duration |
FAQ
Q: SimpleDateFormat はなぜスレッドアンセーフ?
A: 内部状態を持ち、複数スレッドで使うと壊れる。新 API の DateTimeFormatter はスレッドセーフ。
Q: 月初・月末を取りたい
A: LocalDate.now().withDayOfMonth(1) または TemporalAdjusters.firstDayOfMonth() / lastDayOfMonth()。
Q: 営業日計算したい
A: 標準 API は祝日を知らない。Holiday カレンダーや独自テーブルが必要。DayOfWeek で土日判定のみなら標準で可能。