有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

如果删除了空间,java DateTimeFormatter解析带有可选时间部分的字符串将失败

my other question on how to parse date-only strings as LocalDateTime之后,在尝试使用模式yyyyMMdd[HHmmss]解析字符串20120301122133时,我得到一个错误。奇怪的是,使用模式yyyyMMdd[HHmmss]解析20120301 122133非常有效

所以这个代码很好用

LocalDateTime.parse(
     "19940513 230000", 
     new DateTimeFormatterBuilder()
        .appendPattern("yyyyMMdd[ HHmmss]")
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)   
        .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
        .toFormatter()
)

这一次失败了

LocalDateTime.parse(
    "19940513230000", 
    new DateTimeFormatterBuilder()
        .appendPattern("yyyyMMdd[HHmmss]")
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) 
        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)         
        .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
        .toFormatter()
)

我应该如何解析格式为yyyyMMdd[HHmmss]的字符串,即使用java 8 time API解析格式为yyyyMMddHHmmss的字符串,该格式带有可选的时间部分

解析模式是一个可配置的选项,因此只有在运行时才知道。所以我不能用硬编码的DateTimeFormatterBuilder调用替换字符串模式


共 (2) 个答案

  1. # 1 楼答案

    之所以会出现这种情况,是因为年份没有固定的值,通常限制在19位

    如果创建以下格式化程序(不需要分钟和秒)并使用toString()方法:

    new DateTimeFormatterBuilder()
            .appendPattern("yyyyMMdd[ HHmmss]")
            .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
            .toFormatter()
            .toString();
    

    您可以看到以下内容:

    "Value(YearOfEra,4,19,EXCEEDS_PAD)Value(MonthOfYear,2)Value(DayOfMonth,2)[' 'Value(HourOfDay,2)Value(MinuteOfHour,2)Value(SecondOfMinute,2)]java.time.format.DateTimeFormatterBuilder$DefaultValueParser@32eff876"

    在这里你可以看到YearOfEra的最小宽度为4,最大宽度为19

    您可以使用Meno或Ole的其中一个答案

    但是,如果需要接收格式和日期作为参数,并且希望能够以更简单的方式指定日期格式(例如yyyyMMdd[HHmmSS]而不是[...][...]),则可以预处理其中一个to值(日期的格式)

    您可以创建格式化程序,因此每个yyyy仅被解释为4位数的年份

    自定义格式生成器可以是(可以改进的):

    public static DateTimeFormatter createFixed4DigitYearFormatter(String format) {
        DateTimeFormatterBuilder formatBuilder = new DateTimeFormatterBuilder();
        Arrays.stream(format.split("yyyy", -1))
                .flatMap(cur -> Stream.of("yyyy", cur)).skip(1)
                .filter(str -> !str.isEmpty())
                .forEach(pattern -> {
                    if ("yyyy".equals(pattern)) formatBuilder
                            .appendValue(ChronoField.YEAR_OF_ERA, 4);
                    else formatBuilder.appendPattern(pattern);
                });
        return formatBuilder.parseDefaulting(ChronoField.HOUR_OF_DAY, 0).toFormatter();
    }
    

    这个格式化程序用字符串"yyyy"拆分格式,然后每个非"yyyy"被添加为一个模式(使用appendPattern(..)),而"yyyy"被添加为一个固定4位的YEAR_OF_ERA类型的值(使用appendValue(..)

    最后,您可以将格式化程序用于多种格式:

    System.out.println(LocalDateTime.parse("19940513230000",
            createFixed4DigitYearFormatter("yyyyMMdd[HHmmss]"))); // 1994-05-13T23:00
    System.out.println(LocalDateTime.parse("19940513",
            createFixed4DigitYearFormatter("yyyyMMdd[HHmmss]"))); // 1994-05-13T00:00
    System.out.println(LocalDateTime.parse("1994-05-13 23:00:00",
            createFixed4DigitYearFormatter("yyyy-MM-dd[ HH:mm:ss]"))); // 1994-05-13T23:00
    System.out.println(LocalDateTime.parse("1994-05-13",
            createFixed4DigitYearFormatter("yyyy-MM-dd[ HH:mm:ss]"))); // 1994-05-13T00:00
    
  2. # 2 楼答案

        System.out.println(LocalDateTime.parse(
                "19940513230000",
                new DateTimeFormatterBuilder()
                    .appendPattern("[uuuuMMddHHmmss][uuuuMMdd]")
                    .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) 
                    .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)         
                    .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                    .toFormatter()
            ));
    

    这张照片是:

    1994-05-13T23:00

    相反,如果我试图解析一个只包含日期的字符串,"19940513",我会得到

    1994-05-13T00:00

    它与yyyy而不是uuuu一起工作。假设你所有的年龄都在这个时代(第一年或更晚),你用哪一年没有任何区别。通常uuuu也会接受负年份,0表示公元前1年,-1表示公元前2年,以此类推