DateTimeFormatter をざっくり一言でいうと
DateTimeFormatter は、
「LocalDate / LocalTime / LocalDateTime / ZonedDateTime などを
“文字列 ⇔ 日付時刻オブジェクト”に変換するための道具」
です。
画面に表示するときや、文字列から日時を読み込むときに必ず出てきます。
古い SimpleDateFormat と違って、
スレッドセーフ(マルチスレッドで共有しても壊れない)
不変(immutable)で安全java.time 系と綺麗に一体になっている
という、かなり頼れる存在です。
基本イメージ:format(→文字列)と parse(←文字列)
「日時 → 文字列」にする(format)
例えば、LocalDateTime を "2025/01/10 09:30:15" のように表示したいとします。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class FormatterBasicFormat {
public static void main(String[] args) {
LocalDateTime dt = LocalDateTime.of(2025, 1, 10, 9, 30, 15);
DateTimeFormatter fmt =
DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
String text = dt.format(fmt);
System.out.println(text); // 2025/01/10 09:30:15
}
}
JavaofPattern("パターン文字列") で「どういう形で表示するか」を決め、dt.format(fmt) で、そのパターンに沿った文字列を作ります。
「日時オブジェクトが、自分に渡されたフォーマッタを使って文字列に変換している」とイメージすると分かりやすいです。
「文字列 → 日時」にする(parse)
逆に、文字列から LocalDateTime を作りたい場合。
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class FormatterBasicParse {
public static void main(String[] args) {
String text = "2025/01/10 09:30";
DateTimeFormatter fmt =
DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm");
LocalDateTime dt = LocalDateTime.parse(text, fmt);
System.out.println(dt); // 2025-01-10T09:30
}
}
JavaLocalDateTime.parse(文字列, フォーマッタ) の形です。
format と parse は「逆方向」の操作ですが、
どちらも DateTimeFormatter を挟んでいるイメージを持ってください。
よく使う書式パターン(最低限これだけは覚えておく)
年・月・日(date 部分)
yyyy は西暦 4 桁です。MM は 2 桁の月(01〜12)、dd は 2 桁の日(01〜31)です。
代表例として:
"yyyy-MM-dd" → 2025-01-10"yyyy/MM/dd" → 2025/01/10
M や d を一桁にすると、「先頭ゼロなし」になります。
"yyyy-M-d" → 2025-1-2(1 月 2 日)
ブラウザや他システムとのやりとりでは、"yyyy-MM-dd" のような ISO 風の書式を使うことが多いです。
時・分・秒(time 部分)
HH は 0〜23 時の 2 桁mm は分ss は秒
代表的な例:
"HH:mm" → 09:30"HH:mm:ss" → 09:30:15
H にすると先頭ゼロなしで 9:5:3 のような表現も可能ですが、
人間向け表示なら "HH:mm" で良いことが多いです。
日付+時刻
組み合わせるだけです。
"yyyy-MM-dd HH:mm:ss" → 2025-01-10 09:30:15"yyyy/MM/dd HH:mm" → 2025/01/10 09:30
LocalDateTime や ZonedDateTime を扱うときは、この形が一番よく出てきます。
DateTimeFormatter の「スレッドセーフ」「不変」がなぜ大事か
SimpleDateFormat との違い
昔の SimpleDateFormat は、
インスタンスを複数スレッドで共有すると内部状態が壊れる
static で 1 個だけ作って全体で使う、が実は危険
という欠点がありました。
そのため、Web アプリや並列処理で日時のフォーマット/パースをするとき、
「たまにだけ変な日付になる」ようなバグを生みやすかったのです。
DateTimeFormatter は、
不変(immutable)
スレッドセーフ
なので、「static な定数として共有」して構いません。
public class FormatterHolder {
public static final DateTimeFormatter ISO_DATE_TIME =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
}
Javaこうしておいて、どこからでも
String s = dt.format(FormatterHolder.ISO_DATE_TIME);
Javaと安心して呼べます。
「フォーマット用オブジェクトをどこに持つか?」で悩まなくてよくなるのは、想像以上にストレスを減らしてくれます。
LocalDate / LocalTime / LocalDateTime / ZonedDateTime との組み合わせ
LocalDate と DateTimeFormatter
日付だけのフォーマット/パース。
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
LocalDate date = LocalDate.of(2025, 1, 10);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
String text = date.format(fmt);
System.out.println(text); // 2025年01月10日
Javaパースも同様です。
String input = "2025/01/10";
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate date = LocalDate.parse(input, fmt);
System.out.println(date); // 2025-01-10
JavaLocalTime と DateTimeFormatter
時刻だけ。
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
LocalTime time = LocalTime.of(9, 5, 3);
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("HH:mm:ss");
System.out.println(time.format(fmt)); // 09:05:03
JavaLocalDateTime / ZonedDateTime と DateTimeFormatter
日付+時刻(+タイムゾーン)も同様に扱えます。
import java.time.*;
import java.time.format.DateTimeFormatter;
LocalDateTime ldt = LocalDateTime.of(2025, 1, 10, 9, 30, 15);
ZonedDateTime zdt = ldt.atZone(ZoneId.of("Asia/Tokyo"));
DateTimeFormatter fmt1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter fmt2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss O (VV)");
System.out.println(ldt.format(fmt1)); // 2025-01-10 09:30:15
System.out.println(zdt.format(fmt2)); // 2025-01-10 09:30:15 GMT+9 (Asia/Tokyo)
JavaO はオフセット(GMT+9 など)、VV はゾーン ID を表します。
よく使う「定義済みフォーマッタ」も覚えておくと楽になる
ISO 系の定義済みフォーマッタ
DateTimeFormatter には、よく使うフォーマットがあらかじめ定義されています。
代表的なものを挙げると:
DateTimeFormatter.ISO_LOCAL_DATE
例: 2025-01-10
DateTimeFormatter.ISO_LOCAL_TIME
例: 09:30:15.123
DateTimeFormatter.ISO_LOCAL_DATE_TIME
例: 2025-01-10T09:30:15.123
DateTimeFormatter.ISO_OFFSET_DATE_TIME
例: 2025-01-10T09:30:15+09:00
などがあります。
例えば、
LocalDateTime dt = LocalDateTime.of(2025, 1, 10, 9, 30);
String s = dt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
System.out.println(s); // 2025-01-10T09:30:00
Javaブラウザや他言語と連携するときは、
こうした ISO 形式をそのまま使うことが多いので、覚えておくと便利です。
ありがちなハマりポイントと、安全な使い方のコツ
パターンと入力・出力の「形」を必ず一致させる
よくあるのが、
"2025-1-2" な文字列に対して"yyyy-MM-dd" のフォーマットで parse しようとして失敗する
というパターンです。
"yyyy-MM-dd" は「必ず 2 桁の月・日」を期待しているので、"2025-01-02" でなければ parse できません。
一方 "yyyy-M-d" なら "2025-1-2" も "2025-01-02" も parse できます。
「入力文字列の実際の形」と
「フォーマットパターンが期待している形」を
頭の中できっちり合わせるクセをつけてください。
ロケール(日本語の曜日・月名など)を使う場合
曜日や月名をロケールに応じて出したい場合は、withLocale を使います。
import java.time.*;
import java.time.format.*;
import java.util.Locale;
LocalDate date = LocalDate.of(2025, 1, 10);
DateTimeFormatter fmt =
DateTimeFormatter.ofPattern("uuuu年M月d日(E)", Locale.JAPANESE);
System.out.println(date.format(fmt)); // 2025年1月10日(金)
Javaフォーマットパターンに E(曜日)や MMM(短い月名)などを使うときは、
どのロケールで解釈するかを意識する必要があります。
共通フォーマッタを定数化する
同じフォーマットを何度も書いていると、
タイプミスやパターン変更忘れの温床になります。
ヘッダ部分や別クラスにまとめておくのがおすすめです。
public final class Formats {
private Formats() {}
public static final DateTimeFormatter DATE =
DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static final DateTimeFormatter DATE_TIME =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
}
Java使う側は、
String s = dt.format(Formats.DATE_TIME);
Javaと書くだけです。
まとめ:DateTimeFormatter を自分の中でどう位置づけるか
DateTimeFormatter を初心者向けに一言でまとめると、
「java.time の日付・時刻オブジェクトを、
人間や他システムとやり取りするための“文字列表現”に変換する、
スレッドセーフで信頼できるフォーマット/パース用クラス」
です。
特に意識しておきたいのは、
formatで日時 → 文字列、parseで文字列 → 日時ofPatternで、自分の好きな表示形式/入力形式を定義できるSimpleDateFormatと違って、不変でスレッドセーフなので、定数として共有してよい- LocalDate / LocalTime / LocalDateTime / ZonedDateTime と自然に組み合わせて使える
という点です。
