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

Parsing Date or DateTime Strings out of a .csv file as dynamically (many different formats) as possible?

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

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 fails但是 01-May-1999 通过了。

如何处理最不同类型的格式而不导致解析失败? 正如我所说,我实际上并没有生成.csv 文件(即数据工程师)所以我希望这个应用程序尽可能 robust/flexible 并能够解析这种 data/correctly 格式,以便数据可以被使用并相应地写入数据库.我认为我的方法不错,所以我希望不需要大量重写。

您可以使用 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