FURYU Tech Blog - フリュー株式会社

フリュー株式会社の開発者が技術情報を発信するブログです。

Java8のDate and Time APIでハマった話

はじめましてこんにちは!コンテンツ・メディア第1事業部の西川です。ピクトリンクというアプリのサーバサイドの開発をしています。 今日はJava8のDate and Time APIでハマった話をします。

ある文字列を日付型に変換したい…よくあると思います。

package jp.nishikawa;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;

public class DateTimeFormaterTest {

    public static void main(String[] args) {
        LocalDate localDate = LocalDate
                        .parse("20160530", DateTimeFormatter.ofPattern("yyyyMMdd")
                        .withResolverStyle(ResolverStyle.STRICT));
        System.out.println(localDate);
    }

}

これを実行すると…DateTimeParseExceptionが。

Exception in thread "main" java.time.format.DateTimeParseException: Text '20160530' could not be parsed: Unable to obtain LocalDate from TemporalAccessor: {YearOfEra=2016, MonthOfYear=5, DayOfMonth=30},ISO of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1919)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1854)
at java.time.LocalDate.parse(LocalDate.java:400)
at jp.nishikawa.DateTimeFormaterTest.main(DateTimeFormaterTest.java:11)
Caused by: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {YearOfEra=2016, MonthOfYear=5, DayOfMonth=30},ISO of type java.time.format.Parsed
at java.time.LocalDate.from(LocalDate.java:368)
at java.time.LocalDate$$Lambda$7/980546781.queryFrom(Unknown Source)
at java.time.format.Parsed.query(Parsed.java:226)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1850)
... 2 more

ResolverStyleをSTRICT(厳密)にしているのでLENIENTかSMART(デフォルト)を指定すると動きます。

DateTimeFormatterのドキュメントを見ると

y year-of-era
u year

とあります。

eraは暦を表すのでSTRICTを指定した場合はどの暦かわからなくてエラーになっているということです。歴は日本で言うと平成、昭和などです。

単純に解決するには yyyyuuuu にすればOK。

どうしても yyyy を使いたい!という人は以下のように暦を指定させるようにする必要があります。

package jp.nishikawa;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.Locale;

public class DateTimeFormaterTest {

    public static void main(String[] args) {
        LocalDate localDate = LocalDate
                        .parse("20160530 AD", DateTimeFormatter.ofPattern("yyyyMMdd G")
                        .withLocale(Locale.ENGLISH)
                        .withResolverStyle(ResolverStyle.STRICT));
        System.out.println(localDate);
    }
    
}

おまけ:SimpleDateFormatで判定を厳密にしたい場合

以下のように setLenient(false) でOKです。

package jp.nishikawa;

import java.text.ParseException;
import java.text.SimpleDateFormat;

public class DateTimeFormaterTest {

    public static void main(String[] args) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        sdf.setLenient(false);
        System.out.println(sdf.parse("20141030"));
    }
    
}