はじめましてこんにちは!コンテンツ・メディア第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(デフォルト)を指定すると動きます。
| y | year-of-era |
| u | year |
とあります。
eraは暦を表すのでSTRICTを指定した場合はどの暦かわからなくてエラーになっているということです。歴は日本で言うと平成、昭和などです。
単純に解決するには yyyy を uuuu にすれば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"));
}
}