Java 如何检查和分析不同的日期样式

Java 如何检查和分析不同的日期样式,java,date-parsing,date,Java,Date Parsing,Date,我从一个表中获取出生日期数据,Oracle列类型为varchar2,而不是date,主要原因是数据由CV解析公司解析,因为不同的CV具有不同的出生日期样式,如: 3-3-1986 11.04.1983 07/24/1969 December, 05, 1986 NOVEMBER 03, 1981 OCTOBER 06,1973 May 18th, 1984 Jan. 27th, 1967 Nov. 18, 1976 July 3,1989 27/02/1978 Place of birt

我从一个表中获取出生日期数据,Oracle列类型为
varchar2
,而不是
date
,主要原因是数据由CV解析公司解析,因为不同的CV具有不同的出生日期样式,如:

3-3-1986
11.04.1983
07/24/1969
December, 05, 1986
NOVEMBER 03, 1981
    OCTOBER 06,1973
May 18th, 1984
Jan. 27th, 1967
Nov. 18, 1976
July 3,1989
27/02/1978 Place of birthLisbon, Portugal
June,11,1979
以下是迄今为止我所写的方法:

public int getAge(String dob){
    int age = 0;
        if(dob==null || dob.equals("")){
        age = 0;
        }
        else{
            dob = dob.trim();
            String[] words = dob.split ("-|/");
            String day = words[0];
            String month = words[1];
            String year = words[2];             
            age = CalculateAge.AgeCalculator(day, month, year);
        }   

    return age;
}
但在这种方法中,我只能处理斜杠和破折号。请帮我整理一下如何从上述日期样本中准确获取日期、月份和年份。

你不能

以任何可能的格式解析任何字符串都是不可能的

举一个例子:1983年4月11日

那是4月11日还是11月4日?根本没有办法知道

你能做的最好的事情就是当你看到一个四位数的年份时提取年份,如果大于12,也许可以判断月份的哪一天



顺便说一句,跟踪出生日期和计算求职者的年龄似乎很奇怪。年龄通常是工作资格的一个很差的标准。在许多地方这样做是违法的

虽然可以使用正则表达式来获取值,但您仍然需要对这些值进行一些决策/验证:

  • 模棱两可的情况(如所指出的)如
    11.04.1983
    ,当你不知道是4月11日还是11月4日:在这种情况下,你必须选择一个(或者尝试猜测)
  • 验证其他值:如果您在非闰年的第32天、2月29日或4月31日获得类似的值,则需要在使用前检查这些值
  • 还有计算年龄的问题
  • 对于案例1,好吧,除了猜测没有什么可做的。如果您可以以任何格式接收日期,就没有办法真正解决这种歧义(除非您认为某种格式更可取)

    但是,对于案例2和案例3,Java的API可以帮助您,因为它已经完成了您需要的所有验证

    如果使用<强> java 8 < />,请考虑使用这更容易


    如果您使用的是Java,那么@Populus可能是我同意的副本,您链接到的问题中可以找到一些灵感,但它很难是严格的副本。如果我们再搜索一段时间,可能会发现一个……似乎暗示了许多类似的问题。请注意,尽管大多数答案使用的是早已过时的
    SimpleDateFormat
    ,而今天我们更倾向于使用
    DateTimeFormatter
    。不过,这些答案的想法可以应用到现代课堂上,可能是重复的,因为没有一种方法可以将所有输入解析为一个日期。您必须映射所有可能的格式,为每种格式创建一个
    DateTimeFormatter
    ,并与这些格式化程序进行循环,尝试解析为
    LocalDate
    ,如果出现解析错误,则转到下一个。您可以在中阅读如何使用格式化程序,并查看和了解与日期解析相关的类似问题。我们可以使用正则表达式找到解析的日期格式,然后将其转换为日期吗?@Ghayel(a)我不理解您评论的问题。正则表达式不是魔法;从11月4日起,Regex将无法在4月11日做出决定。(B) 您的主题已在堆栈溢出上多次提到。在发布之前一定要彻底搜索。是否有任何正则表达式可以从上述日期样式中提取年份?不需要正则表达式。只需拆分字符串并查找四位数字。对于所有其他格式,您需要各种模式的各种字符串拆分器、正则表达式或
    DateTimeFormatter
    对象。再说一次,没有魔法。搜索堆栈溢出作为拆分字符串、使用正则表达式和使用java.tine类进行解析的主题已经被多次提到。@Ghayel正则表达式不是这个问题的最佳解决方案。如前所述,您应该为每个模式使用许多不同的
    DateTimeFormatter
    对象。国际海事组织称,虽然使用单一正则表达式是可能的,但它太复杂(无法执行和维护),不值得付出努力。
    // list of different formatters
    List<DateTimeFormatter> list = new ArrayList<>();
    
    // 3-3-1986 (assuming it's day-month-year)
    list.add(DateTimeFormatter.ofPattern("d-M-yyyy"));
    
    // 11.04.1983 (assuming it's day.month.year)
    list.add(DateTimeFormatter.ofPattern("dd.MM.yyyy"));
    
    // 07/24/1969 (month/day/year)
    list.add(DateTimeFormatter.ofPattern("MM/dd/yyyy"));
    
    // "December, 05, 1986", "NOVEMBER 03, 1981", "July 3,1989" and "June,11,1979"
    // for " OCTOBER 06,1973", I'll remove the spaces before parsing
    list.add(new DateTimeFormatterBuilder()
        // case insensitive for month name
        .parseCaseInsensitive()
        // optional "," after month and optional spaces (after month and before year)
        .appendPattern("MMMM[ ][','][ ]d','[ ]yyyy")
        // use English locale for month name
        .toFormatter(Locale.ENGLISH));
    
    // "May 18th, 1984", "Jan. 27th, 1967" and "Nov. 18, 1976"
    // append suffix for days (st, nd, rd and th)
    // add suffix to days
    Map<Long, String> days = new HashMap<>();
    for (int i = 1; i <= 31; i++) {
        String s;
        switch (i) {
            case 1:
            case 21:
            case 31:
                s = "st";
                break;
            case 2:
            case 22:
                s = "nd";
                break;
            case 3:
            case 23:
                s = "rd";
                break;
            default:
                s = "th";
        }
        days.put((long) i, i + s);
    }
    list.add(new DateTimeFormatterBuilder()
        // month name with optional "."
        .appendPattern("MMM[.] ")
        // optional day with suffix
        .optionalStart().appendText(ChronoField.DAY_OF_MONTH, days).optionalEnd()
        // optional day without suffix
        .optionalStart().appendValue(ChronoField.DAY_OF_MONTH, 1, 2, SignStyle.NEVER).optionalEnd()
        // year
        .appendPattern(", yyyy")
        // use English locale for month name
        .toFormatter(Locale.ENGLISH));
    
    // 27/02/1978 Place of birthLisbon, Portugal ("Place of birth etc" will be removed manually)
    list.add(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
    
    String[] inputs = new String[] { "3-3-1986", "11.04.1983", "07/24/1969", "December, 05, 1986", "NOVEMBER 03, 1981", "    OCTOBER 06,1973",
                    "May 18th, 1984", "Jan. 27th, 1967", "Nov. 18, 1976", "July 3,1989", "27/02/1978 Place of birthLisbon, Portugal", "June,11,1979" };
    LocalDate now = LocalDate.now(); // current date
    for (String s : inputs) {
        LocalDate d = parse(list, s);
        if (d != null) {
            // get age in years
            long years = ChronoUnit.YEARS.between(d, now);
        }
    }
    
    // auxiliary method
    public LocalDate parse(List<DateTimeFormatter> list, String s) {
        // remove the unnecessary stuff
        // you can customize it to remove whatever unnecessary stuff you have in the inputs
        String input = s.replaceAll("Place of birth.*", "").trim();
    
        for (DateTimeFormatter fmt : list) {
            try {
                return LocalDate.parse(input, fmt);
            } catch (Exception e) {
                // can't parse: do nothing and try the next DateTimeFormatter
            }
        }
    
        // can't parse, return null
        return null;
    }
    
    // assuming you've already got year, month, day from the regex
    LocalDate d = LocalDate.of(year, month, day);