使用Java 8 DateTimeFormatter和西班牙语月份名称进行解析

时间:2017-09-19 16:15:07

标签: java datetime java-8 java-time date-parsing

使用旧版',Java 8之前SimpleDateFormat我能做到:

new java.text.SimpleDateFormat("MMM yyyy", new java.util.Locale("es", "ES")).parse("Mayo 2017")

获取具有西班牙月份名称的日期的Date对象。

如何使用Java 8和DateTimeFormatter实现相同的目标?

我试过了:

DateTimeFormatter.ofLocalizedDateTime(FormatStyle.FULL).withLocale(new Locale("es", "ES")).ofPattern("MMM yyyy").parse("Mayo 2017")

但只获得java.time.format.DateTimeParseException

1 个答案:

答案 0 :(得分:9)

可以删除对ofLocalizedDateTime()的调用,因为最后调用ofPattern(),创建另一个格式不同的格式化程序(ofLocalizedDateTime(FormatStyle.FULL)返回的格式与只是month year,所以这不是你想要的。)

另一个细节是Mayo是完整的月份名称,因此模式必须为MMMMcheck the javadoc以获取更多详细信息)。此外,默认情况下DateTimeFormatter仅接受小写名称(至少在我使用西班牙语语言环境进行的测试中),因此您必须将格式化程序设置为不区分大小写。

您可以使用java.time.format.DateTimeFormatterBuilder

来实现
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // case insensitive
    .parseCaseInsensitive()
    // pattern with full month name (MMMM)
    .appendPattern("MMMM yyyy")
    // set locale
    .toFormatter(new Locale("es", "ES"));
// now it works
fmt.parse("Mayo 2017");

或者,您可以直接将其解析为java.time.YearMonth对象,因为它似乎是此案例的最佳选择(因为输入只有年份和月份):

YearMonth ym = YearMonth.parse("Mayo 2017", fmt);
System.out.println(ym); // 2017-05

默认值

如果输入没有所有字段,SimpleDateFormat只会使用一些默认值。在这种情况下,输入只有年和月,因此解析的Date将等同于解析的月/年,但是将该日设置为1,将时间设置为午夜(在JVM默认时区)

新API对此非常严格,除非您告诉它,否则不会创建默认值。配置它的一种方法是将parseDefaultingjava.time.temporal.ChronoField

一起使用
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    // case insensitive
    .parseCaseInsensitive()
    // pattern with full month name (MMMM)
    .appendPattern("MMMM yyyy")
    // default value for day of month
    .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
    // default value for hour
    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
    // default value for minute
    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
    // set locale
    .toFormatter(new Locale("es", "ES"));

使用此功能,您可以将其解析为LocalDateTime,并将缺少的字段分配给相应的默认值:

LocalDateTime dt = LocalDateTime.parse("Mayo 2017", fmt);
System.out.println(dt); // 2017-05-01T00:00

如果您需要获得与java.util.Date创建的值相同的SimpleDateFormat,则可以将此LocalDateTime转换为JVM默认时区,然后将其转换为{{ 1}}:

Date

请注意,我必须明确使用JVM默认时区(Date javaUtilDate = Date.from(dt.atZone(ZoneId.systemDefault()).toInstant()); ),这是ZoneId.systemDefault()使用的隐含时间。

另一种方法是手动设置SimpleDateFormat值中的值:

YearMonth

默认时区can be changed without notice, even at runtime,因此最好始终明确指出您正在使用的时区。

API使用IANA timezones names(始终采用// in this case, the formatter doesn't need the default values YearMonth ym = YearMonth.parse("Mayo 2017", fmt); ZonedDateTime z = ym // set day of month to 1 .atDay(1) // midnight at JVM default timezone .atStartOfDay(ZoneId.systemDefault()); Date javaUtilDate = date.from(z.toInstant()); 格式,例如Region/CityAmerica/New_York),因此您可以调用Europe/Berlin。 避免使用3个字母的缩写(例如ZoneId.of("America/New_York")CST),因为它们是ambiguous and not standard

您可以致电PST获取可用时区列表(并选择最适合您系统的时区)。