使用 DateTimeFormatterBuilder 创建自定义日期格式并忽略计时字段之间的原始文本

Create custom date format using DateTimeFormatterBuilder and ignore raw text between Chrono Fields

我尝试使用 DateTimeFormatterBuilder 创建一个强大的解决方案来支持不同的日期格式。

我希望能够通过只知道 day/month/year/hours/minutes/seconds.

的顺序来解析用户提供的几乎任何日期字符串

我想到了两个解决方案:

  1. 使用正则表达式将所有已知的分隔符替换为特定的分隔符(例如“?”),并使用 DateTimeFormatterBuilder 创建自定义日期格式。

  2. 基本上是一样的,但是我将提供一个“已知”分隔符列表,并将它们作为可选附加到 DateTimeFormatterBuilder

注意:我们遇到的问题之一是日期时间成分之间的空格不一致(例如:dd_MM YYYY 或 dd_____MM__YYYY)

注2:性能至关重要

我的问题:有什么方法可以避免这一切并使用一些内置方法忽略年、月、日等之间的文本(类似可选)

方案一(为简单起见,假设顺序为日-月-年):

private static final String[] DELIMITER= {"?"};

public static void main(String[] args) {
    DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().appendValue(ChronoField.DAY_OF_MONTH);
    appendDelims(builder);
    builder.appendValueReduced(ChronoField.MONTH_OF_YEAR,1,2,1);
    appendDelims(builder);
    builder.appendValueReduced(ChronoField.YEAR, 2, 4, LocalDate.ofEpochDay(0));
    DateTimeFormatter dateTimeFormatter = builder.toFormatter();
    System.out.println("dt = " + LocalDate.parse("23     2,,,,05".replaceAll("\s+|-+|,+","?"),dateTimeFormatter));
    System.out.println("dt2 = " + LocalDate.parse("09-----2      2015".replaceAll("\s+|-+|,+","?"), dateTimeFormatter));
}

private static void appendDelims(DateTimeFormatterBuilder dtb) {
    for(String d : DELIMITER) {
        dtb.appendOptional(DateTimeFormatter.ofPattern(d));
    }
}

输出:

dt = 2005-02-23
dt2 = 2015-02-09

解决方案 2:

private static final String[] DELIMITER= {"?","-"," ","  "};

public static void main(String[] args) {
    DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder().appendValue(ChronoField.DAY_OF_MONTH);
    appendDelims(builder);
    builder.appendValueReduced(ChronoField.MONTH_OF_YEAR,1,2,1);
    appendDelims(builder);
    builder.appendValueReduced(ChronoField.YEAR, 2, 4, LocalDate.ofEpochDay(0));
    DateTimeFormatter dateTimeFormatter = builder.toFormatter();
    System.out.println("dt = " + LocalDate.parse("23?2 05",dateTimeFormatter));
    System.out.println("dt2 = " + LocalDate.parse("9?2-2015", dateTimeFormatter));
}

private static void appendDelims(DateTimeFormatterBuilder dtb) {
    for(String d : DELIMITER) {
        dtb.appendOptional(DateTimeFormatter.ofPattern(d));
    }
}

输出:

dt = 2005-02-23
dt2 = 2015-02-09

不,没有合理的方法可以避免您的解决方案 1 或解决方案 2。DateTimeFormatterDateTimeFormatterBuilder 中唯一可用的忽略不同定界符的机制是那些构成格式的一部分的机制可选:

  1. appendOptional 您已经在使用
  2. optionalStartoptionalEnd
  3. 格式模式中的方括号 []

当然还有其他可能性,比如从头开始编写您自己的解析器,但是 none 我真的会推荐您已经在做的事情。

在您的解决方案 1 中,由于您要替换定界符,我相信您不需要调用 appendDelims。以下格式化程序适用于您的两个示例日期字符串:

        DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder().appendValue(ChronoField.DAY_OF_MONTH)
                .appendPattern("?M?")
                .appendValueReduced(ChronoField.YEAR, 2, 4, LocalDate.EPOCH)
                .toFormatter();

PS 不要用 appendValueReduced 表示月份,这只会造成混淆。使用单个模式字母 M 匹配 1 位或 2 位数字的月份数字,例如 80811.