解析Java中的任何日期


78

我知道这个问题已经问了很多,显然您不能解析任何日期。但是,我发现python-dateutil库能够解析我向它抛出的每个日期,而同时,在找出日期格式字符串方面绝对需要零的工作。Joda time始终被当作是出色的Java日期解析器而出售,但是它仍然需要您在选择格式(或创建自己的格式)之前确定日期的格式。您不能只调用DateFormatter.parse(mydate)并神奇地获取Date对象。

例如,日期“ 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时间呼叫无法正常工作:

    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?我不在乎错误,我只是希望它几乎是完美的。

Answers:


107

最好的选择是向正则表达式求助,以匹配日期格式模式和/或进行强行强制。

几年前,我写了一个愚蠢的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个字符的长度;))

您可以使用新的regex和dateformat模式轻松地自己扩展它。


3
您如何处理不明确的日期?例如,什么03/04/2010意思-2010年4月3日或2010年3月4日?
杰斯珀,2010年

3
我想假设其中一个(可配置)
Bozho 2010年

3
@Jesper:/分隔符通常用于表示MM/dd/yyyy(主要在美国/英语语言环境中使用)。该-分离器是常用来表示dd-MM-yyyy(主要用于欧洲语言环境)。
BalusC,2010年

3
@Jesper是的,您必须在一个月或一天之间决定格式,否则您将无处可寻。
最多

3
@kittylyst:是的。更重要的是,还没有一种防弹方法:)
BalusC,

52

有一个很好的库叫做Natty,我认为它适合您的目的:

Natty是用Java编写的自然语言日期解析器。给定日期表达式后,natty将应用标准语言识别和翻译技术来生成具有可选解析和语法信息的相应日期列表。

您也可以在线尝试


非常感谢!看来确实是个不错的选择。
Raju Penumatsa 2015年

哇!这个库以任何格式解析任何日期的能力给我留下了深刻的印象。它需要在解析时间,但是,我已经在这个岗位上SoftwareRecs.SE解决这一点帮助:softwarerecs.stackexchange.com/questions/26556/...
迈克尔Plautz

1
这是最好的图书馆,我什至尝试过类似的操作:“ 2012年圣诞节前一天”,它可以正确解析
jjj

5
它以“ 13/02/2002”失败,我到2月22日,似乎不太国际化。
里卡多·弗雷塔斯

3
是的,令人惊讶的是Natty无法处理日-月-年格式。
ConorD55 '17

7

我所看到的是一个Date util类,它包含几种典型的日期格式。因此,当调用DateUtil.parse(date)时,它将尝试在内部解析每种日期格式的日期,并且仅当没有内部格式可以解析它时才抛出异常。

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


我认为这是最简单明了的方法。由于格式未知的日期字符串在设计上是模棱两可的,因此在试图识别格式的过程中投入太多“智能”可能会导致更多“令人惊讶”的结果。
Erich Kitzmueller

是的,但是我认为有一些假设,您可以给出一些初始信息(日期中的天/月/年的顺序),以正确解析大多数合理的日期,而无需使用大的查找表。
马克斯

最多,是对的,很可能您会寻找一组有限的日期格式。您无需编写完整的日期解析引擎即可对日和月的顺序进行很少的假设。是否有特定的用例,因为这可以帮助人们指出正确的方向。例如,来自各种社交媒体服务的大多数日期格式适合大约10种流行格式。
罗伯特·戴安娜

也许我对可用性方面更感兴趣。“解析大多数日期,而无需再次处理格式字符串”。我想我真的只是想看看Java中的python-dateutil之类的库,我想这意味着如果我想要的那么好,我应该这样做!
马克斯

我想我们对可用性的定义也有所不同。我看到的日期类能够解析大约30种不同的Web服务中的日期。使用date类就像parse(date)一样简单,因此作为实用程序的用户,我不必担心日期格式。该实用程序的作者为我烦恼。
罗伯特·戴安娜

6

您可以尝试dateparser

它可以自动识别任何字符串,并将其正确,快速地解析为DateCalendarLocalDateTimeOffsetDateTime1us~1.5us)。

它不基于任何natural language analyzerSimpleDateFormatregex.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

0

我不知道如何在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中。您可以按照此方法。此方法将String日期解析为Sql Date(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) {
            }
        }
     }
} 

执行这个!


1
请不要教年轻人使用已经过时且臭名昭著的SimpleDateFormat课程。至少不是第一选择。也并非毫无保留。今天,我们在java.timeJava的现代日期和时间API及其上有了很多改进DateTimeFormatter
Ole VV

如果我们知道如何解决问题,那么我们将调查最新更新。现在我们有了一个解决方案,我们将尝试获得更好的解决方案。无论如何,感谢您的更新!
Shashidhar Reddy,

1
毫米有一个错字,代表分钟。我们应该使用代表月份的MM。
Shashidhar Reddy

0
//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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.