- JAVA
DateTimeParseExceptionの原因と直し方|Java日付の解析
java.time.format.DateTimeParseException: Text '2026/10/10' could not be parsed at index 4 は、日付の文字列が、指定した(または既定の)形式と合わないときのエラーです。 メッセージの後ろの部分で、原因の種類が分かります。
| メッセージの後ろ | 意味 | よくある原因 |
|---|---|---|
could not be parsed at index N |
N 文字目(0 から数える)で形式と合わなくなった | 区切り文字の違い、T の有無、桁数の違い、全角数字・空白 |
unparsed text found at index N |
N 文字目以降に、余計な文字が残った | 時刻付きを LocalDate で読んだ、Z やオフセット付き、末尾の改行 |
Invalid date 'FEBRUARY 30' |
形式は合っているが、存在しない日付 | 2月30日、4月31日 |
Invalid value for ClockHourOfAmPm (valid values 1 - 12): 14 |
値が範囲の外 | HH(0〜23時)のつもりで hh(1〜12時)と書いた |
Unable to obtain LocalDate from TemporalAccessor |
読めた項目から日付を組み立てられない | yyyy と STRICT の組み合わせ |
確認環境:OpenJDK 21.0.10 と 8(確認日 2026年10月10日)。以下の結果はすべて実際に実行して確かめました。Java 8 と結果が違ったものは、表の中で示しています。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「DateTimeParseExceptionの原因と直し方|Java日付の解析」
「at index N」の読み方
N は、0 から数えて何文字目で読めなくなったかです。
LocalDate.parse("2026/10/10")
→ ... could not be parsed at index 4
2 0 2 6 / 1 0 / 1 0
0 1 2 3 4
↑ 4文字目の「/」で止まった
(既定の形式は「-」区切り)
DateTimeParseException の getErrorIndex() でも、同じ位置(4)が取れました。ログに出すときは、getParsedString()(元の文字列)と一緒に出すと原因が分かります。
再現:よくある失敗と直し方
既定の形式(ISO)と違う
LocalDate.parse(文字列) のように形式を指定しないと、2026-10-10(ISO の形式)しか読めません。
| 書き方 | 結果 | 直し方 |
|---|---|---|
LocalDate.parse("2026/10/10") |
at index 4 |
ofPattern("uuuu/MM/dd") を指定 |
LocalDate.parse("2026-1-5") |
at index 5(月が1桁) |
ofPattern("uuuu-M-d") |
LocalDateTime.parse("2026-10-10 09:30:00") |
at index 10(T ではなく空白) |
ofPattern("uuuu-MM-dd HH:mm:ss") |
LocalDateTime.parse("2026-10-10") |
at index 10(時刻が無い) |
LocalDate.parse(...) で読み、.atStartOfDay() |
LocalDate.parse("2026-10-10T09:30:00") |
unparsed text found at index 10 |
LocalDateTime.parse(...) で読み、.toLocalDate() |
ISO の日時は、日付と時刻の間が T です(2026-10-10T09:30:00)。DB や画面の 2026-10-10 09:30:00(空白区切り)は、形式を指定して読みます。
MM と M:桁数が決まっているか
| パターン | 2026/10/10 |
2026/1/5 |
|---|---|---|
yyyy/MM/dd |
読める | at index 5(2桁を期待) |
yyyy/M/d |
読める | 読める |
M・d(1文字)は、1桁でも2桁でも読めます。 入力が 1/5 と 01/05 のどちらもありうるなら、1文字で書きます。
落とし穴:2月30日が、エラーにならずに2月28日になる
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate.parse("2026/02/30", f); // → 2026-02-28(エラーにならない)
LocalDate.parse("2026/02/31", f); // → 2026-02-28
LocalDate.parse("2026/02/32", f); // → Invalid value for DayOfMonth (valid values 1 - 28/31): 32
ofPattern で作った形式は、既定で SMART という解釈のしかたになり、「その月の最終日を超える日」は最終日に直されます(Java の公式ドキュメント)。入力チェックで使うと、存在しない日付が通ってしまいます。
一方、形式を指定しない LocalDate.parse("2026-02-30") は Invalid date 'FEBRUARY 30' でエラーになりました。
STRICT にすると、yyyy で読めなくなる
厳密にしようとして .withResolverStyle(ResolverStyle.STRICT) を付けると、今度は正しい日付まで読めなくなりました。
ofPattern("yyyy/MM/dd").withResolverStyle(STRICT) "2026/10/10"
→ Unable to obtain LocalDate from TemporalAccessor: {DayOfMonth=10, MonthOfYear=10, YearOfEra=2026},ISO ...
y は「紀元の中の年(year-of-era)」、u は「年」です。STRICT では、紀元(西暦か紀元前か)が無いと y から年を決められません。STRICT と組み合わせるときは uuuu を使います。
| パターン | 解釈 | 2026/10/10 |
2026/02/30 |
2028/02/29 |
|---|---|---|---|---|
yyyy/MM/dd |
SMART(既定) | 読める | 2026-02-28 になる | 読める |
yyyy/MM/dd |
STRICT | エラー(TemporalAccessor) | エラー | エラー |
uuuu/MM/dd |
STRICT | 読める | Invalid date 'FEBRUARY 30' |
読める(うるう年) |
入力チェックに使うなら uuuu + STRICT です。
static final DateTimeFormatter YMD =
DateTimeFormatter.ofPattern("uuuu/M/d").withResolverStyle(ResolverStyle.STRICT);
hh と HH
ofPattern("uuuu-MM-dd hh:mm") "2026-10-10 14:30"
→ Invalid value for ClockHourOfAmPm (valid values 1 - 12): 14
hh は午前・午後の12時間制(1〜12)、HH は24時間制(0〜23)です。HH にすると 2026-10-10T14:30 と読めました。
Z・オフセット付きの文字列(API の日時)
| 書き方 | Java 21 | Java 8 |
|---|---|---|
LocalDateTime.parse("2026-10-10T09:30:00Z") |
unparsed text found at index 19 |
同じ |
OffsetDateTime.parse("2026-10-10T09:30:00Z") |
読める(2026-10-10T09:30Z) |
同じ |
OffsetDateTime.parse("2026-10-10T09:30:00+0900")(コロンなし) |
unparsed text found at index 22 |
at index 19 |
Instant.parse("2026-10-10T09:30:00+09:00") |
読める(2026-10-10T00:30:00Z) |
at index 19 |
Z や +09:00 が付いた文字列は OffsetDateTime(または Instant)で読みます。 コロンの無い +0900 は、パターンで XX を指定すると読めました。
OffsetDateTime.parse("2026-10-10T09:30:00+0900",
DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssXX")); // 2026-10-10T09:30+09:00
Z を無視して読むと、9時間ずれる
LocalDateTime.parse("2026-10-10T09:30:00Z", DateTimeFormatter.ISO_DATE_TIME) は、エラーにならずに 2026-10-10T09:30 になりました。Z(UTC)の情報が捨てられ、日本時間なら 18:30 のはずが 09:30 のままです。
OffsetDateTime.parse("2026-10-10T09:30:00Z")
.atZoneSameInstant(ZoneId.of("Asia/Tokyo"))
.toLocalDateTime(); // 2026-10-10T18:30
英語の月名とロケール
DateTimeFormatter f = DateTimeFormatter.ofPattern("dd MMM uuuu");
LocalDate.parse("10 Oct 2026", f);
| 実行環境のロケール | 結果 |
|---|---|
英語(en_US) |
読める |
日本語(ja_JP) |
at index 3(Oct を月名として読めない) |
MMM(月名)や E(曜日)は、実行環境の言語で読みます。 開発のパソコン(英語)では動き、日本語の環境で失敗する、ということが起きます。英語の文字列を読むなら、ロケールを固定します。
DateTimeFormatter.ofPattern("dd MMM uuuu", Locale.US)
HTTP のヘッダーなどの Sat, 10 Oct 2026 09:30:00 GMT は、用意されている DateTimeFormatter.RFC_1123_DATE_TIME で読めました。
和暦(令和8年10月10日)
static final DateTimeFormatter WAREKI = DateTimeFormatter.ofPattern("GGGGy年M月d日", Locale.JAPAN)
.withChronology(JapaneseChronology.INSTANCE)
.withResolverStyle(ResolverStyle.STRICT);
LocalDate.parse("令和8年10月10日", WAREKI); // 2026-10-10
LocalDate.parse("令和元年5月1日", WAREKI); // at index 2
LocalDate.parse("令和元年5月1日".replace("元年", "1年"), WAREKI); // 2019-05-01
「元年」はそのままでは読めません。 読む前に 1年 に置き換えます。和暦では G(元号)と組み合わせるので y を使います。
全角・空白・改行
| 入力 | 結果 |
|---|---|
"2026-10-10"(全角数字) |
at index 0 |
" 2026-10-10"(先頭に空白) |
at index 0 |
"2026-10-10\r"(末尾に改行) |
unparsed text found at index 10 |
画面の入力や CSV から読むときは、先にそろえます。
String norm = Normalizer.normalize(input, Normalizer.Form.NFKC).strip(); // 全角→半角、前後の空白・改行を除く
LocalDate.parse(norm, YMD); // " 2026/10/10\r\n" → 2026-10-10
at index 0 で、見た目は正しいなら、全角・空白・目に見えない文字(BOM など)を疑います。
入力チェックの書き方
static Optional<LocalDate> parseDate(String input) {
if (input == null) return Optional.empty();
String s = Normalizer.normalize(input, Normalizer.Form.NFKC).strip();
try {
return Optional.of(LocalDate.parse(s, YMD)); // uuuu/M/d + STRICT
} catch (DateTimeParseException e) {
return Optional.empty(); // 画面には「日付が正しくありません」
}
}
| 入力 | 結果 |
|---|---|
2026/2/28 |
2026-02-28 |
2026/02/30 |
エラー(Invalid date 'FEBRUARY 30') |
2026/13/1 |
エラー(Invalid value for MonthOfYear (valid values 1 - 12): 13) |
確認の手順(チェックリスト)
| 順番 | 確認すること | 方法 |
|---|---|---|
| 1 | 何文字目で止まったか | at index N(0 から数える) |
| 2 | 形式を指定しているか | 指定なしは ISO(2026-10-10、T 区切り)だけ |
| 3 | 型が合っているか | 時刻付きは LocalDateTime、Z・+09:00 付きは OffsetDateTime |
| 4 | yyyy か uuuu か |
STRICT なら uuuu |
| 5 | 2月30日が通ってしまわないか | SMART(既定)は最終日に直す。入力チェックは STRICT |
| 6 | ロケール | 月名・曜日は Locale.US などを指定 |
| 7 | 全角・空白・改行 | NFKC と strip() |
よくある質問
Q. SimpleDateFormat から移行したら、エラーが出るようになりました。
A. java.time は、形式と合わない文字列を厳しくエラーにします。上の表で、区切り・桁数・T・Z の違いを確認し、形式を指定します。
Q. yyyy と uuuu は、どちらを使えばよいですか。
A. 西暦の日付なら uuuu を使うと、STRICT と組み合わせても問題が起きません。和暦(元号の G と組み合わせる)では y を使います。
まとめ
at index Nは、0 から数えた N 文字目で止まったという意味- 形式を指定しないと ISO だけ。空白区切りの日時は形式を指定する
ofPatternは既定で SMART。2月30日が2月28日になる。入力チェックはuuuu+ STRICTZ・オフセット付きはOffsetDateTime。LocalDateTimeで読むと時差が捨てられる- 月名・曜日はロケールで変わる。
Locale.USを指定する - 和暦の「元年」は
1年に置き換える。全角・空白は NFKC とstrip()でそろえる
参考・出典
確認日はいずれも 2026年10月10日です。
- Java SE 21 API「DateTimeFormatter」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/format/DateTimeFormatter.html
- Java SE 21 API「ResolverStyle」:https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/time/format/ResolverStyle.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「DateTimeParseExceptionの原因と直し方|Java日付の解析」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。