使用Java 8 DateTimeFormatter和西班牙语月份名称进行解析 [英] Parsing with Java 8 DateTimeFormatter and Spanish month names
问题描述
使用'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屋!