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

使用’old’,Java 8之前的SimpleDateFormat我可以做到:

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

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

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

我试过了:

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

但只获得java.time.format.DateTimeParseException

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

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

您可以使用java.time.format.DateTimeFormatterBuilder执行此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 

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

 Date javaUtilDate = Date.from(dt.atZone(ZoneId.systemDefault()).toInstant()); 

请注意,我必须明确使用JVM默认时区( ZoneId.systemDefault() ),这是SimpleDateFormat所使用的。


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

 // 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()); 

即使在运行时 , 也可以在不事先通知的情况下更改默认时区,因此最好始终明确指出您正在使用的时区。

API使用IANA时区名称 (总是采用Region/City格式,如America/New_YorkEurope/Berlin ),因此您可以调用ZoneId.of("America/New_York") 。 避免使用3个字母的缩写(如CSTPST ),因为它们不明确且不标准 。

您可以通过调用ZoneId.getAvailableZoneIds()获取可用时区列表(并选择最适合您系统的时区ZoneId.getAvailableZoneIds()