1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

uuuuって何だろうっと改めて思ったので調べたこと

1
Posted at

はじめに

ふと仕事で「uuuuMMdd」というフォーマットを見かけました。
あれ?「yyyyMMdd」じゃないんだ?なんだろう。

当時はあまり意識しなかったのですが、今更ながら興味が湧いて調べたので、整理の意味も含めて残します。

前提

Java8で「DateTimeFormatter」が出てきて、文字列を日付型に変えたい。日付を文字列に変えたい。等の際に使うようになりました。

文字列を日付型に変える

Geminiにサンプルを作ってもらう

String dateStr = "2026/02/15";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");        
LocalDate date = LocalDate.parse(dateStr, formatter);
System.out.println("変換後: " + date); // 2026-02-15

jshellの結果

jshell> String dateStr = "2026/02/15";
dateStr ==> "2026/02/15"

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
formatter ==> Value(YearOfEra,4,19,EXCEEDS_PAD)'/'Value(MonthOfYear,2)'/'Value(DayOfMonth,2)

jshell> LocalDate date = LocalDate.parse(dateStr, formatter);
date ==> 2026-02-15

jshell> System.out.println("変換後: " + date); // 2026-02-15
変換後: 2026-02-15

日付型を文字列に変える

Geminiにサンプルを作ってもらう

LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")
String text = now.format(formatter);
System.out.println("現在時刻: " + text); 
jshell> LocalDateTime now = LocalDateTime.now();
now ==> 2026-02-15T13:56:02.799040500

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss")
formatter ==> Value(YearOfEra,4,19,EXCEEDS_PAD)'/'Value(MonthOf ... ':'Value(SecondOfMinute,2)

jshell> String text = now.format(formatter);
text ==> "2026/02/15 13:56:02"

jshell> System.out.println("現在時刻: " + text);
現在時刻: 2026/02/15 13:56:02

uuuuとyyyyの違い

正確には、「u」と「y」の違い。

image.png

「u」は、「year」。「y」は「year-of-era」。

「era」?「時代」?
よくわからないので、動作が異なるパターンを知るには、「STRICT」を知る必要がある。

STRICTとは

変換時に正確性を問うモードかな?

日付変換時に存在する日付か?厳格に問う。

とはいえ、13月とかは「STRICT」を付けなくてもエラーにならない。

が、「うるう年」(2月29日)等は「STRICT」を付けないと「エラー」にならない

2026年13月の場合

「STRICT」を付けなくてもエラーになる!

jshell> String dateStr = "2026/13/15";
dateStr ==> "2026/13/15"

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd")
formatter ==> Value(YearOfEra,4,19,EXCEEDS_PAD)'/'Value(MonthOfYear,2)'/'Value(DayOfMonth,2)

jshell> LocalDate date = LocalDate.parse(dateStr, formatter);
|  例外java.time.format.DateTimeParseException: Text '2026/13/15' could not be parsed: Invalid value for MonthOfYear (valid values 1 - 12): 13
|        at DateTimeFormatter.createError (DateTimeFormatter.java:2079)
|        at DateTimeFormatter.parse (DateTimeFormatter.java:2014)
|        at LocalDate.parse (LocalDate.java:437)
|        at (#38:1)
|  原因: java.time.DateTimeException: Invalid value for MonthOfYear (valid values 1 - 12): 13
|        at ValueRange.checkValidIntValue (ValueRange.java:338)
|        at ChronoField.checkValidIntValue (ChronoField.java:740)
|        at IsoChronology.resolveYMD (IsoChronology.java:645)
|        at IsoChronology.resolveYMD (IsoChronology.java:127)
|        at AbstractChronology.resolveDate (AbstractChronology.java:437)
|        at IsoChronology.resolveDate (IsoChronology.java:587)
|        at IsoChronology.resolveDate (IsoChronology.java:127)
|        at Parsed.resolveDateFields (Parsed.java:372)
|        at Parsed.resolveFields (Parsed.java:278)
|        at Parsed.resolve (Parsed.java:265)
|        at DateTimeParseContext.toResolved (DateTimeParseContext.java:331)
|        at DateTimeFormatter.parseResolved0 (DateTimeFormatter.java:2114)
|        at DateTimeFormatter.parse (DateTimeFormatter.java:2010)
|        ...

jshell> System.out.println("変換後: " + date);
変換後: 2026-02-28

うるう年の場合(2026年02月29日の場合)

「STRICT」が無い場合は、エラーにならず「2026-02-28」になる。

jshell> String dateStr = "2026/02/29";
dateStr ==> "2026/02/29"

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd")
formatter ==> Value(YearOfEra,4,19,EXCEEDS_PAD)'/'Value(MonthOfYear,2)'/'Value(DayOfMonth,2)

jshell> LocalDate date = LocalDate.parse(dateStr, formatter);
date ==> 2026-02-28

jshell> System.out.println("変換後: " + date);
変換後: 2026-02-28

「STRICT」がある場合は、エラーになる!
「.withResolverStyle(ResolverStyle.STRICT);」を付ける。

jshell> String dateStr = "2026/02/29";
dateStr ==> "2026/02/29"

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd").withResolverStyle(ResolverStyle.STRICT);
formatter ==> Value(YearOfEra,4,19,EXCEEDS_PAD)'/'Value(MonthOfYear,2)'/'Value(DayOfMonth,2)

jshell> LocalDate date = LocalDate.parse(dateStr, formatter);
|  例外java.time.format.DateTimeParseException: Text '2026/02/29' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {DayOfMonth=29, YearOfEra=2026, MonthOfYear=2},ISO of type java.time.format.Parsed
|        at DateTimeFormatter.createError (DateTimeFormatter.java:2079)
|        at DateTimeFormatter.parse (DateTimeFormatter.java:2014)
|        at LocalDate.parse (LocalDate.java:437)
|        at (#109:1)
|  原因: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {DayOfMonth=29, YearOfEra=2026, MonthOfYear=2},ISO of type java.time.format.Parsed
|        at LocalDate.from (LocalDate.java:405)
|        at Parsed.query (Parsed.java:247)
|        at DateTimeFormatter.parse (DateTimeFormatter.java:2010)
|        ...

というところで、「STRICT」を付けたほうが安全かな。と。

「uuuu」の話に戻ると。

「yyyy/MM/dd」を「STRICT」付けた状態で変換すると。

jshell> String dateStr = "2026/02/15";
dateStr ==> "2026/02/15"

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd").withResolverStyle(ResolverStyle.STRICT);
formatter ==> Value(YearOfEra,4,19,EXCEEDS_PAD)'/'Value(MonthOfYear,2)'/'Value(DayOfMonth,2)

jshell> LocalDate date = LocalDate.parse(dateStr, formatter);
|  例外java.time.format.DateTimeParseException: Text '2026/02/15' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {DayOfMonth=15, YearOfEra=2026, MonthOfYear=2},ISO of type java.time.format.Parsed
|        at DateTimeFormatter.createError (DateTimeFormatter.java:2079)
|        at DateTimeFormatter.parse (DateTimeFormatter.java:2014)
|        at LocalDate.parse (LocalDate.java:437)
|        at (#120:1)
|  原因: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {DayOfMonth=15, YearOfEra=2026, MonthOfYear=2},ISO of type java.time.format.Parsed
|        at LocalDate.from (LocalDate.java:405)
|        at Parsed.query (Parsed.java:247)
|        at DateTimeFormatter.parse (DateTimeFormatter.java:2010)
|        ...

エラーになる。なんで?

と思い、Geminiに尋ねると、「西暦」と「G」を付ければよい。というお話。

今度は「G」を理解する。

「G」の「era」とは

「G」というフォーマットを確認すると、「era」。「y」は「year-of-era」。

image.png

もう少し詳しく理解すると、「西暦」、「紀元前」等の雰囲気。

西暦。を付けると通る!

jshell> String dateStr = "西暦2026/02/15";
dateStr ==> "西暦2026/02/15"

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("Gyyyy/MM/dd").withResolverStyle(ResolverStyle.STRICT)
;
formatter ==> Text(Era,SHORT)Value(YearOfEra,4,19,EXCEEDS_PAD)' ... r,2)'/'Value(DayOfMonth,2)

jshell> LocalDate date = LocalDate.parse(dateStr, formatter);
date ==> 2026-02-15

jshell> System.out.println("変換後: " + date); // 2026-02-15
変換後: 2026-02-15

「紀元前も通る!」

jshell> String dateStr = "紀元前0001/02/15";
dateStr ==> "紀元前0001/02/15"

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("Gyyyy/MM/dd").withResolverStyle(ResolverStyle.STRICT)
;
formatter ==> Text(Era,SHORT)Value(YearOfEra,4,19,EXCEEDS_PAD)' ... r,2)'/'Value(DayOfMonth,2)

jshell> LocalDate date = LocalDate.parse(dateStr, formatter);
date ==> 0000-02-15

jshell> System.out.println("変換後: " + date); // 0000-02-15
変換後: 0000-02-15

余談だけど、「令和」とか使いたければ、「withChronology(JapaneseChronology.INSTANCE)」を付ければ、イケる。

jshell> String dateStr = "令和8/02/15";
dateStr ==> "令和8/02/15"

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("Gy/MM/dd").withResolverStyle(ResolverStyle.STRICT).withChronology(JapaneseChronology.INSTANCE);
formatter ==> Text(Era,SHORT)Value(YearOfEra)'/'Value(MonthOfYear,2)'/'Value(DayOfMonth,2)

jshell> LocalDate date = LocalDate.parse(dateStr, formatter);
date ==> 2026-02-15

jshell> System.out.println("変換後: " + date); // 2026-02-15
変換後: 2026-02-15

「y」を使いたければ、「西暦」「紀元前」とかを意識する必要があると。

「year-of-era」の「era」はそういうことか。

で、「u」の出番。

「u」は、「year」。つまり、「era」というのが付与されていない。

なので、「西暦」「紀元前」等を意識せずに使える。ということか。

jshell> String dateStr = "2026/02/15";
dateStr ==> "2026/02/15"

jshell> DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu/MM/dd").withResolverStyle(ResolverStyle.STRICT);
formatter ==> Value(Year,4,19,EXCEEDS_PAD)'/'Value(MonthOfYear,2)'/'Value(DayOfMonth,2)

jshell> LocalDate date = LocalDate.parse(dateStr, formatter);
date ==> 2026-02-15

jshell> System.out.println("変換後: " + date); // 2026-02-15
変換後: 2026-02-15

まとめ

冒頭の通り、「u」は「year」、「y」は「year-of-era」。
「era」の違いがある。

「era」とは、フォーマット上、「G」となり「西暦」、「紀元前」等。

「y」を「厳密」に扱う場合は、「era」を意識する必要がある。
「u」は「厳密」に扱っても、「era」を意識しなくても良さそう。

image.png

余談

1つのことを理解するために、「era」、「STRICT」等を理解することがあり、お芋掘りの気分で大変だけど、知識が増えてなかなか楽しい。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?