我从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;
}
但是在这种方法中,我只能处理斜线和破折号。请帮我理清如何从上述日期样本中准确地获得日,月和年。
答案 0 :(得分:4)
你不能。
以任何可想到的格式解析任何字符串是不可能的。
举一个例子:11.04.1983
是4月11日还是11月4日?根本没有办法知道。
你能做的最好的事情就是当你看到一个四位数的年份时提取年份,并且可能在大于12的时候判断一个月的日期。
顺便说一句,跟踪出生日期并计算求职者年龄这么麻烦似乎很奇怪。年龄通常是工作资格的不良标准。在许多地方这样做是违法的。
答案 1 :(得分:0)
虽然可以使用正则表达式来获取值,但您仍然需要对这些值做出一些决定/验证:
11.04.1983
>:在这种情况下,你必须选择一个(或者可能尝试猜测)
对于案例1,嗯,除了猜测之外没什么可做的。如果您能够以任何格式接收日期,则无法真正解决这种歧义(除非您假设某种格式是首选)。
对于案例2和案例3,Java的API可以帮助您,因为它已经完成了您需要的所有验证。
如果您使用的是 Java 8 ,请考虑使用new java.time API。它更容易,less bugged and less error-prone than the old APIs。
如果您使用的是 Java< = 7 ,则可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端。对于 Android ,有ThreeTenABP(更多关于如何使用它here)。
以下代码适用于两者。
唯一的区别是包名称(在Java 8中为java.time
,在ThreeTen Backport(或Android的ThreeTenABP)中为org.threeten.bp
),但类和方法名称是相同的
首先,我创建了一个DateTimeFormatter
个对象列表,每个对象都能解析一个(或多个)格式。
对于某些情况,我使用可选部分(由[]
分隔),因为某些模式只有空格或逗号区别,所以保持它们是可选的允许我在两种情况下使用相同的格式化程序。
其他情况比较棘手,需要使用DateTimeFormatterBuilder
更复杂的方法(参见代码中的注释)。
之后,我删除了一些不必要的东西(比如Place of birth
,开头和结尾的空格),然后我尝试用所有格式化程序解析日期,直到它工作(或得到{{1}如果无效的话。)
然后我使用日期来计算年龄,使用null
类。
ChronoUnit
当然,此代码不接受任何可能的模式,因为这是不可能的。您必须映射您可以接收的所有可能模式,并在出现后将新的模式添加到列表中。 (即使你使用正则表达式,你可能不得不改变它来处理新案例,但使用格式化程序可以保证它将解析并验证日期 - 并且还可以正确计算年龄)。
如果您需要更多不同的模式,还可以查看javadoc以获取有关所有现有模式的更多信息。
如果您真的愿意使用正则表达式并找到提取值的方法,那么您也可以这样做:
// 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);
。如果日期有效,您可以使用它来计算年龄,如上所示。
无论如何,我不认为一个正则表达式是可能的(即使它是,我认为这将是如此复杂,将成为维护的噩梦) - 你可能必须创建许多不同的并循环使用它们。
不要误会我的意思,正则表达式很酷,但它们并不是解决所有问题的最佳解决方案。