我遇到这种情况,我正在阅读包含存储为字符串字段的日期的130K记录。有些记录包含空格(nulls),有些包含这样的字符串:'dd-MMM-yy',有些包含'dd / MM / yyyy'。
我写过这样的方法:
public Date parsedate(String date){
if(date !== null){
try{
1. create a SimpleDateFormat object using 'dd-MMM-yy' as the pattern
2. parse the date
3. return the parsed date
}catch(ParseException e){
try{
1. create a SimpleDateFormat object using 'dd/MM/yyy' as the pattern
2. parse the date
3. return parsed date
}catch(ParseException e){
return null
}
}
}else{
return null
}
}
所以你可能已经发现了这个问题。 我正在使用try .. catch作为我逻辑的一部分。最好是我可以事先确定String实际上包含某种格式的可解析日期然后尝试解析它。
那么,是否有一些API或库可以帮助解决这个问题?我不介意编写几个不同的Parse类来处理不同的格式,然后创建一个工厂来选择正确的6,但是,我该如何确定哪一个?
感谢。
答案 0 :(得分:7)
在逻辑中使用try-catch不要太费劲:这是Java强迫你这样做的情况之一,因此你无法做很多事情。
但在这种情况下,您可以使用DateFormat.parse(String, ParsePosition)
。
答案 1 :(得分:7)
有关如何使用Option
类型消除try / catch块的概述,请参阅Lazy Error Handling in Java。
Functional Java是你的朋友。
本质上,您要做的是将日期解析包装在不抛出任何内容的函数中,但在其返回类型中指示解析是否成功。例如:
import fj.F; import fj.F2;
import fj.data.Option;
import java.text.SimpleDateFormat;
import java.text.ParseException;
import static fj.Function.curry;
import static fj.Option.some;
import static fj.Option.none;
...
F<String, F<String, Option<Date>>> parseDate =
curry(new F2<String, String, Option<Date>>() {
public Option<Date> f(String pattern, String s) {
try {
return some(new SimpleDateFormat(pattern).parse(s));
}
catch (ParseException e) {
return none();
}
}
});
好的,现在你有一个可重用的日期解析器,它不会抛出任何东西,但通过返回类型Option.None
的值来指示失败。以下是您使用它的方式:
import fj.data.List;
import static fj.data.Stream.stream;
import static fj.data.Option.isSome_;
....
public Option<Date> parseWithPatterns(String s, Stream<String> patterns) {
return stream(s).apply(patterns.map(parseDate)).find(isSome_());
}
这将为您提供使用匹配的第一个模式解析的日期,或者类型为Option.None的值,这是类型安全的,而null则不是。
如果您想知道Stream
是什么...... it's a lazy list。这可确保您在第一次成功之后忽略模式。不需要做太多工作。
按照以下方式调用您的函数:
for (Date d: parseWithPatterns(someString, stream("dd/MM/yyyy", "dd-MM-yyyy")) {
// Do something with the date here.
}
或者...
Option<Date> d = parseWithPatterns(someString,
stream("dd/MM/yyyy", "dd-MM-yyyy"));
if (d.isNone()) {
// Handle the case where neither pattern matches.
}
else {
// Do something with d.some()
}
答案 2 :(得分:6)
您可以利用正则表达式来确定字符串所处的格式,以及它是否与任何有效格式匹配。这样的事情(未经测试):
(哎呀,我在检查你正在使用的语言之前用C#编写过这个。)
Regex test = new Regex(@"^(?:(?<formatA>\d{2}-[a-zA-Z]{3}-\d{2})|(?<formatB>\d{2}/\d{2}/\d{3}))$", RegexOption.Compiled);
Match match = test.Match(yourString);
if (match.Success)
{
if (!string.IsNullOrEmpty(match.Groups["formatA"]))
{
// Use format A.
}
else if (!string.IsNullOrEmpty(match.Groups["formatB"]))
{
// Use format B.
}
...
}
答案 3 :(得分:3)
如果您只有两种已知格式,则看起来有三个选项:
-
或/
,然后从解析该格式开始。后者似乎没必要。
答案 4 :(得分:3)
如果格式是准确的(1999年6月7日将是07年6月7日或07/06/1999:你确定你有前导零),那么你可以只检查长度字符串在尝试解析之前。
请注意第一个版本中的短月份名称,因为Jun可能不是6月份的另一种语言。
但是如果您的数据来自一个数据库,那么我只会将所有日期转换为通用格式(它是一次性的,但是您可以控制数据及其格式)。 < / p>
答案 5 :(得分:3)
在这种有限的情况下,最好的(也是最快的方法)可以解析当天,然后根据下一个字母“/”或“ - ”尝试解析其余的问题。如果在任何时候有意外数据,则返回NULL。
答案 6 :(得分:2)
使用正则表达式来解析字符串。确保你保留两个正则表达式的预编译(不是在每个方法调用上创建新的,而是将它们存储为常量),并比较它实际上是否比你使用的try-catch
更快。
如果两个版本都失败而不是抛出异常,我仍然觉得奇怪的是你的方法会返回null
。
答案 7 :(得分:2)
您可以使用拆分来确定要使用的格式
String[] parts = date.split("-");
df = (parts.length==3 ? format1 : format2);
假设它们都是一种或另一种格式,如果需要,可以改进检查
答案 8 :(得分:2)
假设您提供的模式是唯一可能的选择,我会查看传入的字符串以查看要应用的格式。
public Date parseDate(final String date) {
if (date == null) {
return null;
}
SimpleDateFormat format = (date.charAt(2) == '/') ? new SimpleDateFormat("dd/MMM/yyyy")
: new SimpleDateFormat("dd-MMM-yy");
try {
return format.parse(date);
} catch (ParseException e) {
// Log a complaint and include date in the complaint
}
return null;
}
正如其他人所提到的,如果你能保证你将永远不会以多线程方式访问DateFormat
,你可以上课 - 级别或静态实例。
答案 9 :(得分:2)
每次迭代创建SimpleDateFormat(或两个)的替代方法是为这些格式延迟填充ThreadLocal容器。这将解决线程安全问题和对象创建性能方面的问题。
答案 10 :(得分:1)
我为我的项目编写的一个简单的实用工具类。希望这有助于某人。
用法示例:
DateUtils.multiParse("1-12-12");
DateUtils.multiParse("2-24-2012");
DateUtils.multiParse("3/5/2012");
DateUtils.multiParse("2/16/12");
public class DateUtils {
private static List<SimpleDateFormat> dateFormats = new ArrayList<SimpleDateFormat>();
private Utils() {
dateFormats.add(new SimpleDateFormat("MM/dd/yy")); // must precede yyyy
dateFormats.add(new SimpleDateFormat("MM/dd/yyyy"));
dateFormats.add(new SimpleDateFormat("MM-dd-yy"));
dateFormats.add(new SimpleDateFormat("MM-dd-yyyy"));
}
private static Date tryToParse(String input, SimpleDateFormat format) {
Date date = null;
try {
date = format.parse(input);
} catch (ParseException e) {
}
return date;
}
public static Date multiParse(String input) {
Date date = null;
for (SimpleDateFormat format : dateFormats) {
date = tryToParse(input, format);
if (date != null) break;
}
return date;
}
}
答案 11 :(得分:0)
一方面,我认为您为此目的使用try / catch并没有错,这是我会使用的选项。另一方面,还有其他选择:
在我的演示中,我使用的是现代Java日期和时间API java.time,因为在问题中使用的Date
类的设计总是很差,现在已经过时了。对于没有时间的日期,我们需要一个java.time.LocalDate
。
在java.time中使用try-catch看起来像这样:
DateTimeFormatter ddmmmuuFormatter = DateTimeFormatter.ofPattern("dd-MMM-uu", Locale.ENGLISH);
DateTimeFormatter ddmmuuuuFormatter = DateTimeFormatter.ofPattern("dd/MM/uuuu");
String dateString = "07-Jun-09";
LocalDate result;
try {
result = LocalDate.parse(dateString, ddmmmuuFormatter);
} catch (DateTimeParseException dtpe) {
result = LocalDate.parse(dateString, ddmmuuuuFormatter);
}
System.out.println("Date: " + result);
输出为:
日期:2009-06-07
假设我们将字符串定义为:
String dateString = "07/06/2009";
然后输出仍然相同。
如果您希望避免使用try-catch构造,可以轻松地进行简单的检查来确定您的字符串符合哪种格式。例如:
if (dateString.contains("-")) {
result = LocalDate.parse(dateString, ddmmmuuFormatter);
} else {
result = LocalDate.parse(dateString, ddmmuuuuFormatter);
}
结果与以前相同。
这是我最不喜欢的选项,但它简短易懂,旨在提供某种程度的完整性。
DateTimeFormatter dateFormatter
= DateTimeFormatter.ofPattern("[dd-MMM-uu][dd/MM/uuuu]", Locale.ENGLISH);
LocalDate result = LocalDate.parse(dateString, dateFormatter);
方括号表示格式的可选部分。因此,Java首先尝试使用dd-MMM-uu
进行解析。无论是否成功,它都会尝试使用dd/MM/uuuu
解析字符串的其余部分。给定两种格式,其中一种尝试将成功,并且您已经解析了日期。结果仍然与上面相同。
Oracle tutorial: Date Time解释了如何使用java.time。