使用 Java 8 DateTimeFormatter 和西班牙月份名称进行解析
Parsing with Java 8 DateTimeFormatter and Spanish month names
有了'old',pre-Java8SimpleDateFormat
我可以做到:
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
.
可以删除对 ofLocalizedDateTime()
的调用,因为最后你调用 ofPattern()
,创建另一个具有完全不同模式的格式化程序(ofLocalizedDateTime(FormatStyle.FULL)
返回的模式非常与 month year
不同,所以这不是您真正想要的)。
另一个细节是 Mayo
是完整的月份名称,因此模式必须是 MMMM
(check 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
将等同于解析的 month/year,但是日期将设置为 1,时间设置为午夜(在 JVM 默认情况下时区)。
新的 API 对此非常严格,除非您告诉它这样做,否则不会创建默认值。配置它的一种方法是使用 parseDefaulting
和 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"));
有了这个,您可以将其解析为 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 timezones names(格式总是Region/City
,如America/New_York
或Europe/Berlin
),所以你可以调用ZoneId.of("America/New_York")
例如。
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 ambiguous and not standard.
您可以通过调用 ZoneId.getAvailableZoneIds()
.
获取可用时区列表(并选择最适合您的系统的时区)
有了'old',pre-Java8SimpleDateFormat
我可以做到:
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
.
可以删除对 ofLocalizedDateTime()
的调用,因为最后你调用 ofPattern()
,创建另一个具有完全不同模式的格式化程序(ofLocalizedDateTime(FormatStyle.FULL)
返回的模式非常与 month year
不同,所以这不是您真正想要的)。
另一个细节是 Mayo
是完整的月份名称,因此模式必须是 MMMM
(check 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
将等同于解析的 month/year,但是日期将设置为 1,时间设置为午夜(在 JVM 默认情况下时区)。
新的 API 对此非常严格,除非您告诉它这样做,否则不会创建默认值。配置它的一种方法是使用 parseDefaulting
和 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"));
有了这个,您可以将其解析为 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 timezones names(格式总是Region/City
,如America/New_York
或Europe/Berlin
),所以你可以调用ZoneId.of("America/New_York")
例如。
避免使用 3 个字母的缩写(如 CST
或 PST
),因为它们是 ambiguous and not standard.
您可以通过调用 ZoneId.getAvailableZoneIds()
.