Java8のDate and Time APIではyyyyじゃなくてuuuuを使う

2,3日前にこういうのを書いたんだけど

bufferings.hatenablog.com

irofさんと喋ってたら「yyyyじゃなくてuuuu使わなきゃとかもありますよねー」って言われて「え?そうなんだっけ・・・あれー?全然知らない!・・・ような、頭の隅にあるような・・・」ってなって、調べといた。

どういうこと?

https://docs.oracle.com/javase/jp/8/docs/api/java/time/format/DateTimeFormatter.html

yyyyは暦に対する年(year-of-era)、uuuuは暦とは無関係な年(year)。なんだって。

year-of-eraって何?

day-of-monthが、月始まりからの日にちだから、暦始まりの年ってことなんだろうな。ふむー。イマイチぴんとこない。から、やってみよう。

普通に書くとこうか。

  @Test
  public void 普通にyyyyとuuuuを使ったら同じ結果になる() {
    LocalDate target = LocalDate.of(2017, 10, 22);
    assertThat(target.format(DateTimeFormatter.ofPattern("yyyy/MM/dd", Locale.US)))
        .isEqualTo("2017/10/22");
    assertThat(target.format(DateTimeFormatter.ofPattern("uuuu/MM/dd", Locale.US)))
        .isEqualTo("2017/10/22");
  }

暦はGか。つけてみよう。

  @Test
  public void Gをつけてみたらどうなる() {
    LocalDate target = LocalDate.of(2017, 10, 22);
    assertThat(target.format(DateTimeFormatter.ofPattern("G yyyy/MM/dd", Locale.US)))
        .isEqualTo("AD 2017/10/22");
    assertThat(target.format(DateTimeFormatter.ofPattern("G uuuu/MM/dd", Locale.US)))
        .isEqualTo("AD 2017/10/22");
  }

ADよね。・・・そうか、BCやってみるか。

  @Test
  public void 紀元前を渡したらどうなる() {
    LocalDate target = LocalDate.of(-2017, 10, 22);
    assertThat(target.format(DateTimeFormatter.ofPattern("G yyyy/MM/dd", Locale.US)))
        .isEqualTo("BC 2018/10/22");
    assertThat(target.format(DateTimeFormatter.ofPattern("G uuuu/MM/dd", Locale.US)))
        .isEqualTo("BC -2017/10/22");
  }

なるほどなぁ。uuuuの方はGに関係ないってことか。すっきりした。だから、こう使うのが正しそうね。

  @Test
  public void こういう風に使うってことか() {
    LocalDate target = LocalDate.of(2017, 10, 22);
    assertThat(target.format(DateTimeFormatter.ofPattern("G yyyy/MM/dd", Locale.US)))
        .as("yyyy使うなら暦と一緒に")
        .isEqualTo("AD 2017/10/22");
    assertThat(target.format(DateTimeFormatter.ofPattern("uuuu/MM/dd", Locale.US)))
        .as("uuuu使うなら暦は使わない")
        .isEqualTo("2017/10/22");
  }

んで、和暦はこうなるってことか

  @Test
  public void 和暦はこうなるってことか() {
    JapaneseDate japaneseDate = JapaneseDate.of(2017, 10, 22);
    assertThat(japaneseDate.format(DateTimeFormatter.ofPattern("G yyyy/MM/dd", Locale.JAPAN)))
        .isEqualTo("平成 0029/10/22");
    assertThat(japaneseDate.format(DateTimeFormatter.ofPattern("uuuu/MM/dd", Locale.JAPAN)))
        .isEqualTo("2017/10/22");
  }

パースはこんな感じだね

  @Test
  public void STRICTモードで暦指定なしyyyyは例外発生() {
    assertThatExceptionOfType(DateTimeParseException.class)
        .isThrownBy(() -> LocalDate.parse("2017/10/22", DateTimeFormatter.ofPattern("yyyy/MM/dd")
            .withResolverStyle(ResolverStyle.STRICT)));
  }

  @Test
  public void yyyyを使うなら暦を指定する() {
    assertThat(LocalDate.parse("AD 2017/10/22",
        DateTimeFormatter.ofPattern("G yyyy/MM/dd").withResolverStyle(ResolverStyle.STRICT)))
        .isEqualTo(LocalDate.of(2017, 10, 22));
  }

  @Test
  public void でも西暦を扱うときには暦を指定したりしないだろうからuuuuを使うべきってことね() {
    assertThat(LocalDate.parse("2017/10/22",
        DateTimeFormatter.ofPattern("uuuu/MM/dd").withResolverStyle(ResolverStyle.STRICT)))
        .isEqualTo(LocalDate.of(2017, 10, 22));
  }

面白かったー!

昨日の記事にさらっと一行だけ追加しといた。

今日のテストはGistにはっといた(assertjの3.8.0を使ってます)
DateAndTimeTest.java · GitHub

頭の片隅にあった理由

は2015年に蓮沼さんに教えてもらってたからだった!ありがとうございますー!

https://www.slideshare.net/khasunuma/jsr310-3-75404431/32

まきさん

ほほー


せんでんー

来週28(土)にテックカンファで名古屋に行くからぜひ来て下さいー!セッション見たりお菓子食べたりしながらお話しましょうー。

bufferings.hatenablog.com