tags:

views:

132

answers:

3

I know this question is asked quite a bit, and obviously you can't parse any arbitrary date. However, I find that the python-dateutil library is able to parse every date I throw at it, all while requiring absolutely zero effort in figuring out a date format string. Joda time is always sold as being a great Java date parser, but it still requires you to decide what format your date is in before you pick a Format (or create your own). You can't just call DateFormatter.parse(mydate) and magically get a Date object back.

For example, the date "Wed Mar 04 05:09:06 GMT-06:00 2009" is properly parsed with python-dateutil:

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

but the following Joda time call doesn't work:

    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);

And creating your own DateTimeFormatter defeats the purpose, since that seems to be the same as using SimpleDateFormatter with the correct format string.

Is there a comparable way to parse a date in Java, like python-dateutil? I don't care about errors, I just want it to mostly perfect.

+1  A: 

Have you considered porting the Python utility to Java?

Jherico
I think this is the answer only because the other alternatives don't do the same things under the hood as python-dateutil, but they are still all good options. I guess there just isn't a comparable library for Java yet.
Max
+2  A: 

What I have seen done is a Date util class that contains several typical date formats. So, when DateUtil.parse(date) is called, it tries to parse the date with each date format internally and only throws exceptions if none of the internal formats can parse it.

It is basically a brute force approach to your problem.

Robert Diana
I think this is the most straight-forward and comprehensible approach. Since a date string of unknown format is ambigious by design, putting too much "intelligence" into the attempt to recognize the format probably results in more "surprising" results.
ammoQ
Yes, but I think there are a few assumptions you can make given a bit of starting information (order of day/month/year in a date) to correctly parse most sane dates without a big lookup table.
Max
Max, that is true, and most likely there is a limited set of date formats that you would be looking for. You can make very few assumptions about the order of day and month without writing a full blown date parsing engine. Is there a specific use case for this, because that could help point people in the right direction. For example, most date formats from various social media services fit into about 10 popular formats.
Robert Diana
Perhaps I'm more interested in the usability aspect. "Parse most dates without ever dealing with a format string again". I think I really just want to see a library like python-dateutil in Java, which I suppose would mean I should make it if I want it so bad!
Max
I guess our definitions of usability are different too. The date class I had seen was able to parse dates from around 30 different web services. Using the date class was as simple as parse(date), so as a user of the utility I did not have to worry about date formats. The writer of the utility did the worrying for me.
Robert Diana
+4  A: 

Your best bet is really asking help to regex to match the date format pattern and/or to do brute forcing.

Several years ago I wrote a little silly DateUtil class which did the job. Here's an extract of relevance:

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.
}

(cough, double brace initialization, cough, it was just to get it all to fit in 100 char max length ;) )

You can easily expand it yourself with new regex and dateformat patterns.

BalusC
What do you do with ambiguous dates? For example, what does `03/04/2010` mean - 3 April 2010 or 4 March 2010?
Jesper
I guess assume one or the other (configurable)
Bozho
@Jesper: the `/` separator is commonly used to denote `MM/dd/yyyy` (mainly used in US/English locales). The `-` separator is commonly used to denote `dd-MM-yyyy` (mainly used in European locales).
BalusC
@Jesper yea you have to decide between a month or day with the format otherwise you'll never get anywhere.
Max