在Java中解析任何日期

90

我知道这个问题被问得很多,而且显然你不能解析任意日期。然而,我发现python-dateutil库能够解析我抛出的每个日期,而且完全不需要费力去确定日期格式字符串。Joda时间总是被宣传为一个很棒的Java日期解析器,但它仍然需要你在选择格式之前决定日期的格式(或创建自己的格式)。你不能只调用DateFormatter.parse(mydate)并神奇地获得一个日期对象。

例如,日期“Wed Mar 04 05:09:06 GMT-06:00 2009”可以通过python-dateutil正确解析:

import dateutil.parser
print dateutil.parser.parse('Wed Mar 04 05:09:06 GMT-06:00 2009')

但是以下的 Joda time 调用不起作用:

    String date = "Wed Mar 04 05:09:06 GMT-06:00 2009";
    DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
    DateTime dt = fmt.parseDateTime(date);
    System.out.println(date);

如果创建自己的DateTimeFormatter,那就失去了意义,因为这似乎与使用带有正确格式字符串的SimpleDateFormatter相同。

在Java中是否有类似于python-dateutil的可比较的方法来解析日期?我不关心错误,我只想让它尽可能地完美。

6个回答

122

你最好寻求正则表达式的帮助,以匹配日期格式模式和/或进行暴力破解。

几年前,我写了一个小而愚蠢的 DateUtil 来完成这项工作。这是相关部分的摘录:

private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {{
    put("^\\d{8}$", "yyyyMMdd");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
    put("^\\d{12}$", "yyyyMMddHHmm");
    put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
    put("^\\d{14}$", "yyyyMMddHHmmss");
    put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
    put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
    put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
    put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
    put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
    put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
    put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
}};

/**
 * Determine SimpleDateFormat pattern matching with the given date string. Returns null if
 * format is unknown. You can simply extend DateUtil with more formats if needed.
 * @param dateString The date string to determine the SimpleDateFormat pattern for.
 * @return The matching SimpleDateFormat pattern, or null if format is unknown.
 * @see SimpleDateFormat
 */
public static String determineDateFormat(String dateString) {
    for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
        if (dateString.toLowerCase().matches(regexp)) {
            return DATE_FORMAT_REGEXPS.get(regexp);
        }
    }
    return null; // Unknown format.
}

(咳嗽,双括号初始化,咳嗽,这只是为了让它全部适合100个字符的最大长度 ;) )

您可以轻松地使用新的正则表达式和日期格式模式进行扩展。


3
你如何处理模糊的日期?例如,03/04/2010 是指 2010 年 4 月 3 日还是 3 月 4 日? - Jesper
3
我猜想假设其中之一(可配置)。 - Bozho
4
@Jesper: /分隔符通常用来表示MM/dd/yyyy(主要在美国/英语环境中使用)。 -分隔符通常用来表示dd-MM-yyyy(主要在欧洲地区使用)。 - BalusC
3
是的,你需要在格式中选择是一个月还是一天,否则你永远无法取得进展。 - Max
3
没错,甚至更进一步说,这并不存在万无一失的方法 :) - BalusC
显示剩余9条评论

52

有一个很不错的库叫做Natty,我认为它非常适合你的目的:

Natty是一个用Java编写的自然语言日期解析器。给定一个日期表达式,natty会应用标准语言识别和翻译技术,以生成一组相应的日期,其中包括可选的解析和语法信息。

您也可以在线尝试


非常感谢!这似乎是一个非常好的选择。 - Raju Penumatsa
哇!我非常 impressed 这个库的能力,可以解析任何格式的日期。不过,它在解析时间方面需要一些帮助,但是我已经在 SoftwareRecs.SE 上的这篇文章中解决了这个问题:http://softwarerecs.stackexchange.com/questions/26556/is-there-a-library-that-automatically-parses-time-stamps-from-text/26577#26577 - Michael Plautz
1
这绝对是我尝试过的最好的库,我甚至试过类似于“2012年圣诞节前一天”的日期格式,它也能正确解析。 - jjj
6
它在“13/02/2002”上失败了,我得到的是2月22日,似乎不太国际化。 - Ricardo Freitas
5
是的,令人惊讶的是,Natty 无法处理日-月-年的格式。 - ConorD55
显示剩余3条评论

18
你可以尝试使用dateparser
它可以自动识别任何字符串,并将其正确快速地解析为DateCalendarLocalDateTimeOffsetDateTime(1us~1.5us)。
它不基于任何 自然语言分析器(natural language analyzer)SimpleDateFormatregex.Pattern
使用它,您无需准备任何适当的模式,例如yyyy-MM-dd'T'HH:mm:ss.SSSZyyyy-MM-dd'T'HH:mm:ss.SSSZZ:
Date date = DateParserUtils.parseDate("2015-04-29T10:15:00.500+0000");
Calendar calendar = DateParserUtils.parseCalendar("2015-04-29T10:15:00.500Z");
LocalDateTime dateTime = DateParserUtils.parseDateTime("2015-04-29 10:15:00.500 +00:00");

一切正常,尽情享受吧。

刚才看了一下,好像覆盖了很多种格式。 - Sankalp
适用于我的使用情况。 - prodigy4440
InputDate: "04/26/2022 12:00:00.000" - ExpectedDate: "04-26-2022" 我刚刚查看了README.md,发现我的日期格式已经被提及并得到支持。谢谢。 以下代码解决了我的问题: LocalDateTime localDateTime = DateParserUtils.parseDateTime("04/26/2022 12:00:00.000"); System.out.println("Parsed/Converted LocalDateTime form:: "+localDateTime.toString()); System.out.println("Target/Expected date:: "+DateTimeFormatter.ofPattern("MM-dd-yyyy").format(localDateTime)); - Aniket

7

我见过的做法是创建一个包含几种常见日期格式的日期工具类。因此,当调用DateUtil.parse(date)时,它会在内部尝试使用每个日期格式解析日期,并且只有在没有内部格式可以解析它时才抛出异常。

这基本上是一种暴力解决问题的方法。


我认为这是最直接和易于理解的方法。由于设计上未知格式的日期字符串是不明确的,试图将太多的“智能”投入到识别格式的尝试中可能会导致更多“意外”的结果。 - Erich Kitzmueller
是的,但我认为你可以根据一些起始信息(日期中日/月/年的顺序)做出一些假设,以正确解析大多数合理的日期,而无需使用大型查找表。 - Max
Max,这是真的,你很可能正在寻找一组有限的日期格式。没有编写完整的日期解析引擎,几乎不能对日和月的顺序做出太多假设。是否有针对此特定问题的使用情况,因为这可以帮助指导人们朝着正确的方向前进。例如,来自各种社交媒体服务的大多数日期格式都适合于约10种流行格式。 - Robert Diana
也许我对可用性方面更感兴趣。“在不再涉及格式字符串的情况下解析大多数日期”。我想我真的只是想看到Java中类似于python-dateutil的库,这意味着如果我非常需要它,我应该自己动手开发! - Max
我猜我们对可用性的定义也不同。我看到的日期类能够从大约30个不同的Web服务中解析日期。使用日期类就像是parse(date)一样简单,所以作为实用程序的用户,我不必担心日期格式。实用程序的编写者替我担心了这个问题。 - Robert Diana
@RobertDiana 感谢您的建议。 您能否指定 DateUtil 的包,因为我只能找到需要传递模式的 DateUtil.parse 方法。 - NameNotFoundException

1
//download library:   org.ocpsoft.prettytime.nlp.PrettyTimeParser
String str = "2020.03.03";
Date date = new PrettyTimeParser().parseSyntax(str).get(0).getDates().get(0);
System.out.println(date)

1
请务必在回答中加入上下文,而不仅仅是粘贴代码。更多详细信息请参见这里 - gehbiszumeis
@Mahdi,那个prettytimeparser在Maven中央库里吗?我们如何在我们的maven pom.xml文件中引入它? - ennth
2020.03.03 打印出来的是 "Thu Feb 18 20:20:03 CST 2021",看起来不对。 - ennth
好的库,谢谢。 - sdykae
@ennth 是的。请查看 https://www.ocpsoft.org/prettytime/nlp/ - Mahdi

-3

我对在Python中如何进行此解析毫无头绪。在Java中,我们可以这样做

SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
  java.util.Date normalDate = null;
  java.sql.Date sqlDate = null;
  normalDate = sdf1.parse(date);
  sqlDate = new java.sql.Date(normalDate.getTime());
  System.out.println(sqlDate);

我认为像Java一样,Python中也有一些预定义函数。您可以使用以下方法。 这些方法将字符串日期解析为SQL日期(dd-MM-yyyy);

import java.text.SimpleDateFormat;
import java.text.ParseException;
public class HelloWorld{
     public static void main(String []args){
        String date ="26-12-2019";
         SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yyyy");
        java.util.Date normalDate = null;
        java.sql.Date sqlDate = null;
        if( !date.isEmpty()) {
            try {
                normalDate = sdf1.parse(date);
                sqlDate = new java.sql.Date(normalDate.getTime());
                System.out.println(sqlDate);
            } catch (ParseException e) {
            }
        }
     }
} 

执行这个!

3
请不要教年轻人使用早已过时且臭名昭著的SimpleDateFormat类。至少不要将其作为首选项。而且不要毫无保留地使用它。今天,我们有更好的选择——java.time,这是现代Java日期和时间API,以及它的DateTimeFormatter - Ole V.V.
如果我们知道如何解决问题,那么我们将查看最新的更新。现在我们有了一个解决方案,我们将尝试获得更好的解决方案。无论如何,感谢您的更新! - Shashidhar Reddy
这并没有回答问题,因为发帖者问的是如何在不指定格式的情况下自动解析日期。 你只是展示了一种使用格式的方法,并且还使用了过时的API。 - undefined

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