简体   繁体   English

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

[英]Parsing with Java 8 DateTimeFormatter and Spanish month names

With the 'old', pre-Java 8 SimpleDateFormat I can do:使用“旧的”,Java 8 之前的SimpleDateFormat我可以做到:

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

to get the Date object of a date with Spanish month names.获取具有西班牙月份名称的日期的Date对象。

How can I achieve the same with Java 8 and DateTimeFormatter ?如何使用 Java 8 和DateTimeFormatter实现相同的目标?

I tried:我试过:

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

but only get a java.time.format.DateTimeParseException .但只得到一个java.time.format.DateTimeParseException

The call to ofLocalizedDateTime() can be removed, because in the end you call ofPattern() , creating another formatter with a totally different pattern (and the pattern returned by ofLocalizedDateTime(FormatStyle.FULL) is very different from just month year , so that's not really what you want).可以删除对ofLocalizedDateTime()的调用,因为最后你调用ofPattern() ,创建另一个具有完全不同模式的格式化程序(并且ofLocalizedDateTime(FormatStyle.FULL)返回的模式与仅month year非常不同,所以这是不是你想要的)。

Another detail is that Mayo is the full month name, so the pattern must be MMMM ( check the javadoc for more details).另一个细节是Mayo是完整的月份名称,因此模式必须是MMMM (有关更多详细信息, 请查看 javadoc )。 Also, the DateTimeFormatter by default accepts only lowercase names (at least in the tests I've made with Spanish locale), so you must set the formatter to be case insensitive.此外,默认情况下DateTimeFormatter仅接受小写名称(至少在我使用西班牙语言环境进行的测试中),因此您必须将格式化程序设置为不区分大小写。

You can do that by using a java.time.format.DateTimeFormatterBuilder :您可以通过使用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");

Optionally, you can directly parse it to a java.time.YearMonth object, as it seems to be the best choice for this case (because the input has only year and month):或者,您可以直接将其解析为java.time.YearMonth对象,因为它似乎是这种情况下的最佳选择(因为输入只有年和月):

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

Default values默认值

When the input doesn't have all the fields, SimpleDateFormat simply uses some defaults for them.当输入没有所有字段时, SimpleDateFormat只为它们使用一些默认值。 In this case, the input has only year and month, so the parsed Date will be equivalent to the parsed month/year, but the day will be set to 1 and the time to midnight (at the JVM default timezone).在这种情况下,输入只有年和月,因此解析的Date将等同于解析的月/年,但日将设置为 1,时间设置为午夜(在 JVM 默认时区)。

The new API is very strict about that and doesn't create default values unless you tell it to do so.新 API 对此非常严格并且不会创建默认值,除非您告诉它这样做。 One way to configure it is to use parseDefaulting with a java.time.temporal.ChronoField :配置它的一种方法是将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"));

With this, you can parse it to a LocalDateTime and the missing fields will be assigned to the respective default values:有了这个,您可以将其解析为LocalDateTime并将缺失的字段分配给相应的默认值:

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

If you need to get a java.util.Date with the same value as the one created by SimpleDateFormat , you can convert this LocalDateTime to the JVM default timezone and then convert it to Date :如果您需要获取与SimpleDateFormat创建的具有相同值的java.util.Date ,您可以将此LocalDateTime转换为 JVM 默认时区,然后将其转换为Date

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

Note that I had to explicity use the JVM default timezone ( ZoneId.systemDefault() ), which is implicity used by SimpleDateFormat .请注意,我必须明确使用 JVM 默认时区 ( ZoneId.systemDefault() ),它是SimpleDateFormat隐式使用的。


Another alternative is to manually set the values in the YearMonth value:另一种选择是手动设置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());

The default timezone can be changed without notice, even at runtime , so it's better to always make it explicit which one you're using.默认时区可以在没有通知的情况下更改,即使在运行时也是如此,因此最好始终明确您正在使用哪个时区。

The API uses IANA timezones names (always in the format Region/City , like America/New_York or Europe/Berlin ), so you can call ZoneId.of("America/New_York") for example. API 使用IANA 时区名称(始终采用Region/City格式,例如America/New_YorkEurope/Berlin ),因此您可以调用ZoneId.of("America/New_York")例如。 Avoid using the 3-letter abbreviations (like CST or PST ) because they are ambiguous and not standard .避免使用 3 个字母的缩写(如CSTPST ),因为它们含糊不清且不标准

You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds() .您可以通过调用ZoneId.getAvailableZoneIds()获取可用时区列表(并选择最适合您系统的时区ZoneId.getAvailableZoneIds()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM