如何使用DateTimeFormatter

时间:2015-06-13 07:05:27

标签: java datetime localization java-8 java-time

我需要解析(德语)以下列形式的日期:

10. Jan. 18:14
8. Feb. 19:02
1. Mär. 19:40
4. Apr. 18:55
2. Mai 21:55
5. Juni 08:25
5. Juli 20:09
1. Aug. 13:42
[...]

如您所见,如果月份超过4个字符,则会删除月份名称。即使更奇怪,也不要问我为什么,虽然整个名字都是Mär.,但3月份缩短为März。如何使用java.time解析此问题? (日期的格式基于创建日期列表的Android设备的本地化。但是,我没有在Android上解析它)

我的方法是创建一个DateTimeFormatter,如下所示:

DateTimeFormatter.ofPattern("d. MMMM HH:mm").withLocale(Locale.GERMAN);
// or
DateTimeFormatter.ofPattern("d. MMMMM HH:mm").withLocale(Locale.GERMAN);

MMMMMMMMM模式都不适合缩短的日期。当然,我可以使用以下模式d. MMM. HH:mm来匹配缩短的月份,但是我无法匹配3个月和4个月的月份。我知道我可以有两个格式化程序(MMM. and MMMMM),但我宁愿有一个解决方案,我只有一个格式化程序,可能还有一个自定义语言环境或类似的东西。

4 个答案:

答案 0 :(得分:9)

问题的答案是DateTimeFormatterBuilder类和appendText(TemporalField, Map)方法。它允许任何文本在格式化或解析时与值相关联,从而有效和优雅地解决问题:

Map<Long, String> monthNameMap = new HashMap<>();
map.put(1L, "Jan.");
map.put(2L, "Feb.");
map.put(3L, "Mar.");
DateTimeFormatter fmt = new DateTimeFormatterBuilder()
    .appendPattern("d. ")
    .appendText(ChronoField.MONTH_OF_YEAR, monthNameMap)
    .appendPattern(" HH:mm")
    .parseDefaulting(ChronoField.YEAR, 2016)
    .toFormatter();

System.out.println(LocalDateTime.parse("10. Jan. 18:14", fmt));
System.out.println(LocalDateTime.parse("8. Feb. 19:02", fmt));

一些注意事项:

  • monthNameMap必须填写所有12个月
  • 格式化程序通常应分配给静态最终常量,而不是始终创建
  • 已添加parseDefaulting(YEAR, 2016),以便LocalDateTime.parse(String, DateTimeFormatter)可以直接使用。没有它,就没有年份,因此只能解析TemporalAccessor(如果解析2月29日,那年必须是闰年)

答案 1 :(得分:6)

您可以使用DateTimeFormatterBuilder

private static final DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendOptional(DateTimeFormatter.ofPattern("d. MMM. HH:ss"))
            .appendOptional(DateTimeFormatter.ofPattern("d. MMMM HH:ss"))
            .toFormatter(Locale.GERMAN);

在此上运行:

Stream.of(("10. Jan. 18:14\n" +
           "8. Feb. 19:02\n" +
           "1. Mär. 19:40\n" +
           "4. Apr. 18:55\n" +
           "2. Mai 21:55\n" +
           "5. Juni 08:25\n" +
           "5. Juli 20:09\n" +
           "1. Aug. 13:42").split("\n"))
       .map(formatter::parse)
       .forEach(System.out::println);

你得到:

{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=10, MonthOfYear=1, MilliOfSecond=0, SecondOfMinute=14, HourOfDay=18},ISO
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=8, MonthOfYear=2, MilliOfSecond=0, SecondOfMinute=2, HourOfDay=19},ISO
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=1, MonthOfYear=3, MilliOfSecond=0, SecondOfMinute=40, HourOfDay=19},ISO
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=4, MonthOfYear=4, MilliOfSecond=0, SecondOfMinute=55, HourOfDay=18},ISO
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=2, MonthOfYear=5, MilliOfSecond=0, SecondOfMinute=55, HourOfDay=21},ISO
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=5, MonthOfYear=6, MilliOfSecond=0, SecondOfMinute=25, HourOfDay=8},ISO
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=5, MonthOfYear=7, MilliOfSecond=0, SecondOfMinute=9, HourOfDay=20},ISO
{NanoOfSecond=0, MicroOfSecond=0, DayOfMonth=1, MonthOfYear=8, MilliOfSecond=0, SecondOfMinute=42, HourOfDay=13},ISO

答案 2 :(得分:2)

正如所指出的那样,使用标准和一致的格式会更容易 - 在这里你要混合长短月份名称。

一个选项(不使用DateTimeFormatterBuilder)是分别处理两种情况:

private static final DateTimeFormatter SHORT_MONTH = DateTimeFormatter.ofPattern("d. MMM. HH:ss", Locale.GERMAN);
private static final DateTimeFormatter LONG_MONTH = DateTimeFormatter.ofPattern("d. MMMM HH:ss", Locale.GERMAN);
private static TemporalAccessor parse(String s) {
  try {
    return SHORT_MONTH.parse(s);
  } catch (DateTimeParseException e) {
    return LONG_MONTH.parse(s);
  }
}

答案 3 :(得分:1)

你可以正则表达式替换月份部分,因此在使用“d.MMM HH:mm”解析它之前它总是3个字符长度

text = text.replaceFirst("(\\S+\\s\\S{3})\\S", "$1")

正则表达式部分的说明:找到1个或多个非空格(\ S +)后跟1个空格(\ s),后跟三个非空格(\ S {3})后跟一个非空格,并替换它与第一个括号内的部分($ 1)

10. Jan. 18:14将成为10. Jan 18:145. Juni 08:25将成为5. Jun 08:25