无需指定格式的自动日期/时间解析器

7

我正在寻找一个可以将字符串解析为POJO的Java库,而不需要指定格式。我已经调研过POjava。是否有其他做类似事情的库?

DateTime dateTime = DateTimeParser.parse("21/02/13");

//If unclear use the cultural information passed
DateTime dateTime = DateTimeParser.parse("01/02/13", new Locale("en-us"));

//Should also work with time zones
DateTime dateTime = DateTimeParser.parse("2011/12/13T14:15:16+01:00");

我发现以下链接有相同的问题:智能日期/时间解析器(Java),但回答并不是很有用。无论是Joda还是JChronic都不能满足我的需求。如果我错了请纠正我。

更新:

我之所以说Joda不能解决我的问题是因为Joda需要将要解析的字符串格式化为ISO8601格式或者你指定的任何格式,比如"yyyyMMdd"。但我不能硬编码此格式,因为我需要处理多种格式。

对于消除与美国或欧洲日期格式模糊性的解决方案,即mm/dd/yy或dd/mm/yy,我有一个绕过的方法。假设我可以访问日期的时区,我能否确定它是美国还是欧洲格式?有人能告诉我如何做到这一点吗?我谷歌搜索但没有找到任何东西。


3
如果让你猜第一个版本与“06/05/2013”有关,你选择了什么,对于全球很大一部分人来说,无论你选什么都是错误的。 - Jon Skeet
你说的不完全对,但也不算错。除非你限制输入为非歧义格式,或者猜测输入,否则是做不到的。前者会让你不受欢迎,后者会让你显得无能。 :) - Tony Hopkinson
请查看此链接https://github.com/zoho/hawking - Heisenberg
显示剩余3条评论
7个回答

10
问题在于有一些格式是无法正确猜测的。
一个简单的例子是01/02/2013。这是2月1日还是1月2日?或者更糟糕的是:01/02/09
两种格式都存在。(谢谢你,英国和美国!)
因此,对于这些格式,任何格式猜测器都必须依靠运气,否则就会故意失败。
Python模块dateutil.parser可以作为最佳努力解析器的示例。很抱歉我不知道Java的等效方法。但你可能想看看Joda Time

http://labix.org/python-dateutil#head-b95ce2094d189a89f80f5ae52a05b4ab7b41af47

实际上它有参数dayfirstyearfirst

然后还有一个Perl模块:

https://metacpan.org/pod/Time::ParseDate

你可以尝试使用该模块的优先级列表。盲目地尝试一些模式并不是很快(一个优化了的词法分析器会更快),但如果你猜测的记录格式不超过百万条,那可能已经足够好了。

如果第一行存在歧义(请参考原问题中的代码),则使用默认的文化信息,除非在第二行代码中指定了特定的文化信息。我能从任何库中获得这种智能吗? - saravanan07

4
我找到了解决问题的方法。我使用了这个特定的库POjava。这个页面解释了如何在不指定任何格式的情况下格式化日期和时间字符串。但是,为了使库正常工作,您需要指定日期顺序,例如日-月或月-日。

1

由于我没有找到适用于我的情况的方便解决方案,因此我编写了一个简单的静态实用方法来帮助我。如果需要添加更多的格式,将其包装在集合中并迭代可能会使事情变得更容易。

public static Date returnDateFromDateString(String propValue) throws Exception {

    SimpleDateFormat sdfFormat1 = new SimpleDateFormat(IDateFConstants.DATE_STRING_FORMAT_1);
    SimpleDateFormat sdfFormat2 = new SimpleDateFormat(IDateFConstants.DATE_STRING_FORMAT_2);
    SimpleDateFormat sdfISO8601 = new SimpleDateFormat(IDateFConstants.DATE_STRING_ISO_8601);

    try {
        return sdfFormat1.parse(propValue);
    } catch (ParseException e) { }

    try {
        return sdfFormat2.parse(propValue);
    } catch (ParseException e) { }

    try {
        return sdfISO8601.parse(propValue);
    } catch (ParseException e) { }

    throw new Exception(IDateFConstants.DATE_FORMAT_ERROR);
}

其中IDateFConstants看起来像:

public interface IDateFConstants {

public static final String DATE_STRING_ISO_8601 = "yyyy-MM-dd'T'HH:mm:ss";
public static final String DATE_STRING_FORMAT_1 = "dd.MM.yyyy";
public static final String DATE_STRING_FORMAT_2 = "dd.MM.yyyy HH:mm:ss";

public static final String DATE_FORMAT_ERROR = "Date string wasn't" + 
                                            + "formatted in known formats";

}

1

这个问题没有什么神奇的解决方案。请记住,日期/时间格式也可能取决于您的语言环境。

实际上,您最好能做的是定义一个格式列表,并逐一尝试,直到找到符合要求的格式(或者没有找到)。

private static final FORMAT_1 = "MM/dd/yyyy'T'HH:mm:ss.SSS"
private static final FORMAT_2 = "MM/dd/yyyy'T'HH:mm:ss"
private static final FORMAT_3 = "MM/dd/yyyy"

在处理Java中的日期/时间对象时,请记得考虑线程安全性。我有一个名为“ThreadSafeDateTimeFormatter”的类来处理这种情况。

祝你好运!


0
你至少需要一个有序的模式候选列表。一旦你有了这个,Apache DateUtils 提供了一个 parseDate(String dateString, String[] patterns) 方法,让你可以轻松地在日期字符串上尝试模式列表,并由第一个匹配的模式进行解析:
public static Date parseDate(String str,
                         String[] parsePatterns)
                  throws ParseException
Parses a string representing a date by trying a variety of different parsers.

解析器将依次尝试每个解析模式。只有当解析器解析整个输入字符串时,才被视为成功解析。如果没有匹配的解析模式,则会抛出ParseException异常。 解析器将对解析的日期宽容处理。

0

这个日期/时间解析器支持20多种日期格式,用户可以将日期格式设置为输入的配置。查看完整文档,它比其他日期时间库做得更多。

Github链接:https://github.com/zoho/hawking。 由ZOHO ZIA团队开发。

Hawking Parser是一个基于Java的NLP解析器,用于解析日期和时间信息。像Heidel Time、SuTime和Natty Date time parser等最受欢迎的解析器都是基于规则的。因此,它们通常很难处理需要考虑更复杂因素(如上下文、时态、多个值等)的日期/时间信息。

考虑到这一点,Hawking Parser旨在解决许多这些挑战,并且与其他可用的日期/时间解析器相比具有许多独特的优势。

它是一个遵循GPL v3协议的开源库,也是最好的一个。要了解为什么它是最好的,请查看这篇详细说明的博客:https://www.zoho.com/blog/general/zias-nlp-based-hawking-date-time-parser-is-now-open-source.html

附言:我是这个项目的开发人员之一


我注意到您写了几个答案,链接到您的网站。到目前为止,它们都与问题相关,而且只有很少的答案,所以没关系。但我应该提醒您,即使披露了与链接资源的关联,继续重复或不必要地推广您的内容可能被认为是垃圾邮件。请参阅“如何不成为垃圾邮件发送者”以获取更多信息。 - cigien
谢谢让我知道。在发布答案之前会记住这件事。 - Heisenberg

0
        public static String detectDateFormat(String inputDate, String requiredFormat) {
        String tempDate = inputDate.replace("/", "").replace("-", "").replace(" ", "");
        String dateFormat;

        if (tempDate.matches("([0-12]{2})([0-31]{2})([0-9]{4})")) {
            dateFormat = "MMddyyyy";
        } else if (tempDate.matches("([0-31]{2})([0-12]{2})([0-9]{4})")) {
            dateFormat = "ddMMyyyy";
        } else if (tempDate.matches("([0-9]{4})([0-12]{2})([0-31]{2})")) {
            dateFormat = "yyyyMMdd";
        } else if (tempDate.matches("([0-9]{4})([0-31]{2})([0-12]{2})")) {
            dateFormat = "yyyyddMM";
        } else if (tempDate.matches("([0-31]{2})([a-z]{3})([0-9]{4})")) {
            dateFormat = "ddMMMyyyy";
        } else if (tempDate.matches("([a-z]{3})([0-31]{2})([0-9]{4})")) {
            dateFormat = "MMMddyyyy";
        } else if (tempDate.matches("([0-9]{4})([a-z]{3})([0-31]{2})")) {
            dateFormat = "yyyyMMMdd";
        } else if (tempDate.matches("([0-9]{4})([0-31]{2})([a-z]{3})")) {
            dateFormat = "yyyyddMMM";
        } else {
//add your required regex
            return "";
        }
        try {
            String formattedDate = new SimpleDateFormat(requiredFormat, Locale.ENGLISH).format(new SimpleDateFormat(dateFormat).parse(tempDate));

            return formattedDate;
        } catch (Exception e) {

            return "";
        }

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接