はじめに
Javaで日付と時刻を扱うなら、文字列へのフォーマットと文字列からの解析を同じ設計で扱えるDateTimeFormatterを押さえると、ログ、CSV、APIレスポンス、画面表示の実装が整理しやすくなります。ただし、yyyyとYYYYの違い、Localeの有無、ZoneIdの扱いを曖昧にすると、日付のずれや解析エラーにつながります。
そのため、基本のLocalDate、LocalTime、LocalDateTimeから入り、時刻帯、ロケール、例外処理、DateTimeFormatterBuilderまで順に確認する構成にしているのが基本です。関連するJavaの基礎はJava List型完全ガイドやJavaでうるう年を判定する解説も合わせて参照できます。
- Java 21 LTS
- 標準ライブラリ java.time / java.util.Locale
- 外部ライブラリ例のみ Joda-Time 2.12系
DateTimeFormatterで日付と時刻を文字列へ整形する方法LocalDateTime.parseで文字列を解析するときの考え方LocaleとZoneIdを使った表示の切り替えDateTimeParseExceptionへの対処法DateTimeFormatterBuilderによるカスタマイズ方法
DateTimeFormatterとは
結果: 期待される出力は2026-06-21です。
これがDateTimeFormatterの最小構成です。Javaのjava.timeでは、日付を持つLocalDate、時刻を持つLocalTime、両方を持つLocalDateTimeに対して、同じ考え方でフォーマットを適用できます。
その設計は、公式ドキュメントでも確認できます。詳しい仕様はOracle公式 DateTimeFormatter APIとOracle公式 java.time パッケージ概要が一次情報になるのが目安です。
Javaでの日付と時刻の重要性
Javaアプリケーションでは、ログのタイムスタンプ、予約日時、決済期限、ファイル名、画面表示などに日付と時刻が含まれます。そのため、内部処理では型として安全に扱い、外部へ出す直前に文字列へ変換する流れが一般的です。
このとき、Stringだけで日付を持ち回ると、並び替え、比較、時刻帯変換、入力チェックが複雑になります。JavaのLocalDateやZonedDateTimeを使い、表示や通信の境界でDateTimeFormatterを使う方法にすると、処理の責務を分けられますし、ここがポイントです。
一方、フォーマット文字列を間違えると、年や月の意味が変わります。たとえばMMは月、mmは分なので、日付と時刻を同時に扱うコードでは大文字と小文字の違いを確認する必要があります。
DateTimeFormatterの基本
DateTimeFormatterは、Java 8以降の日時APIで使われるフォーマッタです。主な役割は、日時オブジェクトから文字列を作るフォーマットと、文字列から日時オブジェクトを作る解析になるのがポイントです。
具体的には、ofPatternでyyyy-MM-ddのようなパターンを作り、formatで文字列へ変換します。逆方向ではparseを使い、入力文字列が指定したパターンに合わない場合はDateTimeParseExceptionで扱います。
| 分類 | 主な要素 | 用途 | 注意点 |
|---|---|---|---|
| 日付 | LocalDate | 年月日だけを扱う | 時刻情報は持たない |
| 時刻 | LocalTime | 時分秒だけを扱う | 日付情報は持たない |
| 日時 | LocalDateTime | 日付と時刻をまとめる | 時刻帯は持たない |
| 時刻帯 | ZonedDateTime | 地域の時刻を扱う | ZoneIdが必要 |
| パターン | yyyy | 暦年を表す | YYYYと混同しない |
| パターン | MM | 月を表す | mmは分 |
| パターン | dd | 日を表す | 存在しない日付は解析で失敗し得る |
| パターン | HH | 24時間制の時を表す | hhは12時間制 |
| ロケール | Locale | 曜日や月名の言語を決める | 省略時は環境依存になる場合がある |
| 例外 | DateTimeParseException | 解析失敗を扱う | 入力値の検証と組み合わせる |
これらの要素を分けて考えると、DateTimeFormatterの方法は単なる文字列置換ではないと理解できます。Javaの型が日時の意味を持ち、フォーマットだけが表示形式を担当する構造になります。
DateTimeFormatterの使い方
基本の使い方は、対象の日時型を決め、パターンを作り、formatまたはparseを呼ぶ流れです。Javaの日時処理では、内部データと表示文字列を混ぜないことが保守性につながりますが、これは押さえたい点です。
そのため、画面やファイルに出す日付はStringへ変換し、計算や比較を行う日付はLocalDateなどの型で持つ方法が扱いやすくなります。Javaエスケープ処理の解説で扱う文字列処理と組み合わせる場面もあります。
サンプルコード1:基本的な日付のフォーマット
日付だけを表示する場合はLocalDateを使いるのが一般的です。この型は時刻を含まないため、年月日だけを扱うフォーム入力や期限表示に向いています。
結果: 期待される出力はフォーマット後の日付:2026-06-21のような形式です。実際の日付部分は実行日によって変わります。
このコードではLocalDate.now()で現在日付を取得し、DateTimeFormatter.ofPattern("yyyy-MM-dd")で表示形式を決めています。date.format(formatter)の戻り値はStringなので、以降は画面表示やログ出力へ渡せますし、これが一つの目安です。
サンプルコード2:基本的な時刻のフォーマット
時刻だけを扱う場合はLocalTimeを使います。日付を含まないため、毎日の開始時刻、締切時刻、バッチ起動時刻のような値に合います。
結果: 期待される出力はフォーマット後の時刻:14:30:15のような形式です。時刻部分は実行した瞬間により変わります。
このときHHは24時間制の時、mmは分、ssは秒を表します。特にMMとmmの混同は初心者がつまずきやすい箇所なので、日付の月と時刻の分を分けて覚えると安全です。
サンプルコード3:カスタムフォーマットを作成
日付と時刻をまとめて扱う場合はLocalDateTimeが使えるのが現実的です。曜日や午前午後を加えたい場合も、パターンを組み合わせればひとつの文字列にできます。
結果: 期待される出力はカスタムフォーマット後の日付と時刻:21/06/2026 03:15 PM Sunのような形式です。曜日や午前午後の表記はロケールの影響を受けます。
そのパターンでは、dd/MM/yyyyが日月年、hh:mmが12時間制の時刻、aが午前午後、Eが曜日を表します。日本語表記にしたい場合は、後続のロケール指定と合わせて考える必要があると整理できます。
サンプルコード4:ロケールによるフォーマットの変更
曜日名や月名を含むフォーマットでは、Localeが表示言語に関わります。Javaの実行環境に任せると想定と異なる言語になる場合があるため、外部向け表示では明示する方法が安定します。
結果: 期待される出力はフランスのロケールでの日付:dimanche 21 juin 2026のような形式です。実行日によって曜日、日、月、年は変わります。
この例ではLocale.FRANCEを渡すことで、曜日をEEEE、月名をMMMMとしてフランス語で出力します。多言語サイトや海外向けCSVでは、DateTimeFormatterのパターンだけでなくLocaleも仕様として決める必要があると理解できます。
サンプルコード5:時刻帯の指定
時刻帯を含む表示では、ZoneIdとZonedDateTimeの考え方が関係します。LocalDateTime自体は地域情報を持たないため、東京やUTCなどの基準を別に与えます。
結果: 期待される出力は東京の時刻帯での日付と時刻:2026-06-21 17:30:45 +0900のような形式です。日時部分は実行時点により変わります。
このコードではZoneId.of("Asia/Tokyo")を作り、withZoneでフォーマッタに時刻帯を渡しています。末尾のZはUTCとの差を表し、東京の場合は通常+0900になると覚えるとよいでしょう。
💡 Tips: APIの保存値にはInstantやUTC基準の日時を使い、表示時にZoneIdで利用者向けの時刻へ変換する設計がよく使われます。
サンプルコード6:テキストから日付・時刻への変換
文字列を日時型へ変換する処理は解析と呼ばれます。入力フォーム、CSV、外部APIの値をJavaの型へ戻すときに使う方法です。
結果: 期待される出力は解析された日付と時刻:2023-12-25T12:34:56です。
この例では入力文字列の2023-12-25 12:34:56と、パターンのyyyy-MM-dd HH:mm:ssが対応しています。解析後のLocalDateTimeをそのまま出力すると、標準のISO形式に近いT区切りで表示されます。
サンプルコード7:エラーハンドリング
解析では、入力の形が合わない文字列や存在しない日付が渡される可能性があると考えられます。その場合はDateTimeParseExceptionを捕捉し、利用者へ再入力を促すなどの対処を入れます。
結果: 期待される出力は不正な日付・時刻の形式です:に続いて解析エラーの詳細が表示される形です。
この入力には存在しない2月30日が含まれるため、解析時に例外が発生します。Javaでフォーム入力を扱う場合は、うるう年判定の考え方と合わせて、日付として成立するかを確認する設計が必要になります。
DateTimeFormatterの応用例
基本的なフォーマットと解析を押さえたら、日付計算、複数パターンの入力、既存ライブラリとの接続が応用の中心になると言えるでしょう。JavaのDateTimeFormatterは表示担当として使い、計算そのものはplusDaysやminusHoursなど日時型のメソッドに任せます。
サンプルコード8:日付と時刻の計算
日付や時刻の計算では、計算後の値を人が読める形へ変換する場面が多くあります。その出力部分でDateTimeFormatterを使うと、計算ロジックと表示形式を分離できるのが基本です。
結果: 期待される出力は計算後の日時: 2026-07-01 12:20:45のような形式です。現在日時を基準にするため、具体的な値は実行時点で変わります。
この例ではplusDays(10)で10日後へ進め、minusHours(5)で5時間戻しています。計算結果のLocalDateTimeに対してformatを呼ぶため、時刻計算とフォーマットの責務が混ざりません。
サンプルコード9:複数のフォーマットを扱う
外部から受け取る文字列では、日付だけ、分まで、秒までのように粒度が揺れることがあるのが目安です。その場合は、許可する入力形式を明確にして解析する必要があります。
結果: 期待される出力は、入力ごとに解析成功または解析失敗が表示される形です。日付だけの入力はLocalDateTimeに必要な時刻が不足するため、環境や解決ルールにより失敗する可能性があります。
このコードは複数の候補を角括弧で表す方法を示しています。ただし、日付だけの文字列をLocalDateTimeへ直接解析するには時刻の既定値が必要になるため、実務ではLocalDate.parseとatStartOfDayを組み合わせる方法も検討するのがポイントです。
サンプルコード10:外部ライブラリとの連携
Java 8より前のシステムでは、Joda-Timeが使われている場合があるのが一般的です。現在の新規実装では標準のjava.timeが選ばれますが、既存コードの移行では両者の表記差を確認する場面があります。
結果: 期待される出力はフォーマットされた日時: 2026-06-21 16:45:30のような形式です。日時部分は実行時点で変わります。
この例は移行時の発想を示すためのコードです。ただし、jodaFormatter.toString()が常にJava標準のパターンとして安全に再利用できるとは限らないため、実際の移行ではJoda-Time側のパターン文字列を棚卸しし、JavaのDateTimeFormatterへ明示的に置き換える方法が現実的です。
注意点と対処法
DateTimeFormatterを使うときに気をつけたい点は、存在しない日付、ロケール差、スレッドセーフ性、パターン文字の意味です。Javaの標準APIは多くのケースを支えますが、入力値や表示要件を曖昧にすると解析エラーや表示ずれが起きます。
そのため、対処法はエラーを握りつぶすことではなく、許可するフォーマット、対象のLocale、対象のZoneIdをコード上で明示することになるのが現実的です。アノテーションで入力検証を整理する場合はJavaアノテーションの解説も関連します。
年月日の範囲とバリデーション
入力された日付がカレンダー上で成立するかは、解析時に確認されます。特に2月29日、2月30日、月末日、うるう年は、単純な文字数チェックだけでは判断できません。
結果: 期待される出力は失敗:に続いて、2023-02-30を解析できない理由が表示される形です。
この例では、文字列の形はyyyy-MM-ddに合っていますが、値としての2月30日が成立しません。日付の解析では、見た目のフォーマットとカレンダー上の妥当性を分けて考える必要があります。
ロケールに依存する問題
月名や曜日名を含む文字列は、ロケールに依存します。日本語、英語、フランス語などで表記が変わるため、解析とフォーマットの両方で同じLocaleを使う方法が安定すると整理できます。
結果: 期待される出力はJune 21, 2026のような米国英語の月名を含む形式です。実行日によって日付は変わります。
このコードではLocale.USを渡すため、MMMMが英語の月名になります。逆に日本語表示にしたい場合はLocale.JAPANを検討し、外部仕様で言語を固定するのが基本になると理解できます。
スレッドセーフ性
DateTimeFormatterはイミュータブルに設計されており、複数スレッドで共有できます。この性質により、従来のSimpleDateFormatで問題になりやすかった共有時の状態破壊を避けやすくなります。
結果: 期待される出力はスレッドセーフ: 2026-06-21 12:00:00のような行が複数表示される形です。各行の時刻はスレッドの実行タイミングで変わります。
この例では同じformatterをRunnable内で共有しています。DateTimeFormatter自体は安全に共有できますが、周辺で扱う可変オブジェクトや出力先の制御は別の問題として確認すると覚えるとよいでしょう。
SimpleDateFormatを既存コードで使っている場合、新規追加部分からjava.timeへ寄せると、日付処理の設計を段階的に整理できます。カスタマイズ方法
JavaのDateTimeFormatterは、単純なパターン文字列だけでなく、組み立て式のDateTimeFormatterBuilderでも作成できます。表示仕様が固定であればofPatternで足りることが多く、任意項目や細かい制御が必要ならビルダーを検討すると考えられます。
具体的には、yyyy、MM、dd、HH、mm、ss、SSS、E、a、Z、XXXなどを組み合わせます。日時の仕様が複雑になるほど、パターンの意味をコメントや定数名で補う方法が読みやすくなります。
フォーマットパターンの詳細
パターン文字は、見た目が似ていても意味が異なると言えるでしょう。たとえばyyyyは暦年、MMは月、ddは日、HHは24時間制の時、mmは分、ssは秒です。
結果: 期待される出力は2026/06/21 15:45:30のような形式です。現在日時を使うため、具体的な値は実行時点で変わります。
このパターンは、スラッシュ区切りの日付とコロン区切りの時刻をひとつの文字列にします。Web画面ではこの表示が読みやすい場合がありますが、API連携ではISO形式のISO_LOCAL_DATE_TIMEなどを使うほうが仕様を合わせやすいこともあるのが基本です。
DateTimeFormatterBuilderを使った高度なカスタマイズ
DateTimeFormatterBuilderは、日時の部品を順番に追加してフォーマッタを作るクラスです。固定文字、特定フィールド、既存フォーマッタを組み合わせられるため、細かい仕様をコードとして表現できます。
結果: 期待される出力は2026-06-21 15:45のような形式です。分までを表示し、秒は含みません。
このコードではappend、appendLiteral、appendValueを使い、ChronoField.HOUR_OF_DAYとChronoField.MINUTE_OF_HOURを2桁で出しています。ビルダーの詳細はOracle公式 DateTimeFormatterBuilder APIで確認できます。
💡 Tips: 画面表示用、ログ用、API用でDateTimeFormatterを分けると、後から表示形式を変えるときの影響範囲を追いやすくなるのが目安です。
同様に、フォーマッタを定数として共有する場合は、用途が分かる名前を付けます。たとえばDISPLAY_DATE_FORMATTER、LOG_DATE_TIME_FORMATTER、CSV_DATE_FORMATTERのように分けると、Javaコード上で意図が読み取りやすくなります。
まとめ
Javaで日付と時刻を扱うときは、内部ではLocalDate、LocalTime、LocalDateTime、ZonedDateTimeなどの型を使い、外部表示や入力解析の境界でDateTimeFormatterを使う構成が分かりやすくなるのがポイントです。フォーマットと解析の方法を分けて理解すると、画面表示、ログ、CSV、API連携の実装に応用できます。
その中でも特に押さえたいのは、ofPatternの文字の意味、Localeによる月名や曜日名の違い、ZoneIdによる時刻帯の扱い、DateTimeParseExceptionによる解析失敗の制御です。これらをコード上で明示すると、環境差や入力値の揺れに強いJava実装になります。
一方、複雑なフォーマットをひとつのパターン文字列へ詰め込むと、後から読む人が意図を追いにくくなるのが一般的です。DateTimeFormatterBuilder、用途別の定数、入力値ごとの解析分岐を使い分ける方法を取ると、日付と時刻の処理を保守しやすくできます。
Javaの周辺知識を広げるなら、コレクション処理はList型の使い方、入力検証の設計はアノテーションの活用、継承を含む設計はオーバーライドの解説も関連します。日時処理は単独の知識ではなく、文字列、例外、型設計と組み合わせて使う技術です。
関連記事
- Java List型完全ガイド!初心者でもマスターできる7つのステップ
- Javaアノテーションの12選!初心者から上級者まで徹底ガイド
- Javaでうるう年を判定!初心者でも分かる9ステップ解説
- Javaエスケープ処理の10ステップマスターガイド
- Javaでマスターする!オーバーライドのたった7つのステップ
※本記事は実在のエンジニア複数名で構成される Japanシーモア編集部が、AI支援を活用して作成・校正・公開しています。


