首页 > 解决方案 > 尽可能动态地(许多不同的格式)从 .csv 文件中解析日期或日期时间字符串?

问题描述

我需要一些帮助,以便能够尽可能动态/灵活地从 .csv 文件中解析出字符串数据,这意味着用户可以输入一堆不同类型的格式(即我想处理dd-MMM-yyyy,但yyyy-MM-dd如果可能的话,还可以处理更多)日期或日期时间,我应该能够在不抛出异常或崩溃的情况下进行解析。.csv 文件的日期/日期时间字段的当前格式dd-MMM-yyyy类似于30-Apr-2020. 当然, time 可以添加并且是可选的(从模式中可以看出使用 [ ] 括号表示法,所以应该是30-Apr-2020 23:59:59)。我已经设置了日期/日期时间列的解析,如下所示:

DateTimeFormatter dtf = new DateTimeFormatterBuilder()
             .appendPattern("dd-MMM-yyyy[[ ]['T']HH:mm:ss]")
             .optionalStart()
             .appendFraction(ChronoField.MICRO_OF_SECOND, 1, 6, true)
             .optionalEnd()
             .toFormatter();

 TemporalAccessor temporalAccessor = dtf.parseBest(dateString, LocalDateTime::from, LocalDate::from);
                if (temporalAccessor instanceof LocalDateTime) {
                    // process here
                } else if (temporalAccessor instanceof LocalDate) {
                    // process here
                }

因此,基本上通过将模式设置为灵活,即"dd-MMM-yyyy[[ ]['T']HH:mm:ss]",然后我使用 TemporalAccessor 检查它是日期还是日期时间,并根据需要进行进一步处理。我可以处理许多不同类型的输入,并且不会让应用程序在这里抛出异常并失败。所以我可以消费:

01-Sep-2020 // just date
01-Sep-2099 18:59:59 // datetime
01-Apr-2033 18:59:59.123 // datetime with ms
01-Aug-2057 23:59:59.123456 // date time up to 6 ms decimal pts

但是,如果用户 .csv 包含类似2020-05-30日期的内容,我认为这是ISO格式标准,它将失败。另外,我现在刚刚注意到的不好的事情是.parseBest()方法,也失败了,因为它在月份区分大小写,所以像这样的事情01-MAY-1999 失败01-May-1999通过了。

如何处理最不同类型的格式而不会解析失败?正如我所说,我实际上并没有生成 .csv 文件(即数据工程师),所以我希望这个应用程序尽可能健壮/灵活,并且能够解析这些数据/正确格式化它,以便可以使用数据并相应地写入数据库。我认为我在这里的方法很不错,所以我希望不需要大量的重写。

标签: javacsvdatetimejava-timedatetimeformatter

解决方案


您可以使用DateTimeFormatterBuilder#parseDefaulting默认的可选字段,如下例所示:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtfInput = new DateTimeFormatterBuilder()
                                .parseCaseInsensitive()// For case-insensitive parsing
                                .appendPattern("[d-M-uuuu[ H[:m[:s]]]]")
                                .appendPattern("[uuuu-M-d[ H[:m[:s]]]]")
                                .appendPattern("[uuuu/M/d[ H[:m[:s]]]]")
                                .appendPattern("[d/M/uuuu[ H[:m[:s]]]]")
                                .appendPattern("[d-MMM-uuuu[ H[:m[:s[.SSSSSS]]]]]")
                                .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
                                .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
                                .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
                                .parseDefaulting(ChronoField.NANO_OF_SECOND, 0)
                                .toFormatter(Locale.ENGLISH);

        String[] arr = { 
                                "10-5-2020", 
                                "2020-5-10", 
                                "10/5/2020", 
                                "2020/5/10", 
                                "10-5-2020 10:20:30", 
                                "10-5-2020 10",
                                "10-5-2020 10:20", 
                                "10/5/2020 10:20", 
                                "01-May-1999", 
                                "01-MAY-1999", 
                                "01-Aug-2057 23:59:59.123456"
                        };

        for (String dt : arr) {
            System.out.println(LocalDateTime.parse(dt, dtfInput));
        }
    }
}

输出:

2020-05-10T00:00
2020-05-10T00:00
2020-05-10T00:00
2020-05-10T00:00
2020-05-10T10:20:30
2020-05-10T10:00
2020-05-10T10:20
2020-05-10T10:20
1999-05-01T00:00
1999-05-01T00:00
2057-08-01T23:59:59.123456

推荐阅读