使用Java 8 DateTimeFormatter和西班牙语月份名称进行解析 [英] Parsing with Java 8 DateTimeFormatter and Spanish month names

查看:891
本文介绍了使用Java 8 DateTimeFormatter和西班牙语月份名称进行解析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用'old',pre-Java 8 SimpleDateFormat 我可以这样做:

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

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



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



我试过:

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

但只得到 java.time.format.DateTimeParseException

解决方案

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



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

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


来做到这一点

  DateTimeFormatter fmt = new DateTimeFormatterBuilder()
//不区分大小写
.parseCaseInsensitive()
//具有完整月份名称的模式(MMMM)
.appendPattern(MMMM yyyy)
// set locale
.toFormatter(new Locale(es,ES));
//现在它起作用
fmt.parse(Mayo 2017);






可选择直接将其解析为 java.time.YearMonth object,因为它似乎是这种情况的最佳选择(因为输入只有年和月):

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






默认值



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



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

  DateTimeFormatter fmt = new DateTimeFormatterBuilder()
//不区分大小写
.parseCaseInsensitive()
//模式包含完整月份名称(MMMM)
.appendPattern(MMMM yyyy)
//月份默认值
.parseDefaulting(ChronoField.DAY_OF_MONTH,1)
//默认值小时值
.parseDefaulting(ChronoField.HOUR_OF_DAY,0)
//分钟的默认值
.parseDefaulting(ChronoField.MINUTE_OF_HOUR,0)
// set locale
.toFormatter(新的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默认时区,然后将其转换为日期

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

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






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

  //在这种情况下,格式化程序不需要默认值
YearMonth ym = YearMonth.parse(Mayo 2017,fmt);
ZonedDateTime z = ym
//将日期设置为1
.atDay(1)
// JVM默认时区午夜
.atStartOfDay(ZoneId.systemDefault ());
日期javaUtilDate = date.from(z.toInstant());






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



API使用 IANA时区名称(始终采用<$格式) c $ c>地区/城市,例如 America / New_York 欧洲/柏林) ,所以你可以打电话给 ZoneId.of(America / New_York)
避免使用3个字母的缩写(例如 CST PST ),因为它们



您可以获得可用时区列表(和通过调用 ZoneId.getAvailableZoneIds()来选择最适合您系统的那个。


With the 'old', pre-Java 8 SimpleDateFormat I can do:

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.

How can I achieve the same with Java 8 and 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.

解决方案

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).

Another detail is that Mayo is the full month name, so the pattern must be MMMM (check the javadoc for more details). 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.

You can do that by using a 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):

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. 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).

The new API is very strict about that and doesn't create default values unless you tell it to do so. One way to configure it is to use parseDefaulting with a java.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 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:

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.


Another alternative is to manually set the values in the YearMonth value:

// 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. Avoid using the 3-letter abbreviations (like CST or PST) because they are ambiguous and not standard.

You can get a list of available timezones (and choose the one that fits best your system) by calling ZoneId.getAvailableZoneIds().

这篇关于使用Java 8 DateTimeFormatter和西班牙语月份名称进行解析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆