4

Python has a very handy package that can parse nearly any unambiguous date and provides helpful error messages on a parse failure, python-dateutil. Comparison to the SimpleDateFormat class is not favorable - AFAICT SimpleDateFormat can only handle one exact date format and the error messages have no granularity.

I've looked through the Joda API but it appears Joda is the same way - only one explicit format can be parsed at a time.

Is there any package or library that reproduces the python-dateutil behavior? Or am I missing something WRT Joda/SimpleDateFormat?

elhefe
  • 3,404
  • 3
  • 31
  • 45
  • See this answer: http://stackoverflow.com/questions/3307330/using-joda-date-time-api-to-parse-multiple-formats/3316679#3316679 – Ian McLaird Oct 18 '13 at 16:29
  • I'm not really looking for a solution where you can specify multiple parsers, one for each format - there's probably hundreds of possible formats. I'm looking for the functionality in the python dateutil class, where you feed it virtually any date string and it Just Works(TM). – elhefe Oct 18 '13 at 16:37
  • What are you parsing that's so inconsistent? Or is it that you just want to use exactly the same lines of code for every different application? – Ian McLaird Oct 18 '13 at 16:39
  • Uncontrolled user input. I'd like to make it as easy as possible for users to enter dates. – elhefe Oct 18 '13 at 16:58
  • Then sadly, I've got nothing. If it's a GUI or Web-based app, maybe use a calendar input control to coax the input into an understandable format. If it's console, I don't know of anything. – Ian McLaird Oct 18 '13 at 18:59

2 Answers2

3

If you really want to use the dateutils Python library, there is a way. It's a little bit inconvenient but at the end you can use the original and not a copy.

You can use Jython to run the dateutils library. The drawback is, that you cannot directly use the original datutils classes from Java. You have to implement a wrapper Jython class which implements a Java interface.

But first you have to prepare your own custom copy of jython.jar.

Jython can be used in different ways. The first way is to install Jython similar to Python in its own directory. With this you can use Jython like ordinary Python. You can run Jython scripts, install libraries via setup tools, etc.

The other way is to use the standalone jython.jar. The standalone jython.jar contains the whole Jython system. You can run Jython scripts with this JAR, too. But the main purpose to pack the whole Jython system into one JAR is to embed it into a Java application and call Jython/Python code from Java (or vice versa).

The dateutils library isn't part of the Python/Jython standard library. So you cannot use it as is. To create your own jython.jar you have to first install Jython into its own diretory.

1. Download Jython Got to the Jython download page and download the 2.5.3 Jython installer JAR. Do not use the higher version numbers with beta status. It is not sure that setup tools will work with these beta versions.

2. Install Jython in a directory Run java -jar jython-installer-2.5.3.jar and choose the standard installation type and install it in a directory, e.g. ~/jython2.5.3.

3. Install setup tools Download http://peak.telecommunity.com/dist/ez_setup.py and run ~/jython2.5.3/bin/jython ez_setup.py. See the Jython book appendix A.

4. Install dateutils library Download the 1.5 version of dateutils library: http://labix.org/download/python-dateutil/python-dateutil-1.5.tar.gz. This version is for Python < 3.0. Unpack the archive and build and install it:

~/jython2.5.3/bin/jython setup.py build
~/jython2.5.3/bin/jython setup.py install

After that your Jython installation should contain the datetuils library at ~/jython2.5.3/Lib/site-packages/python_dateutil-1.5-py2.5.egg/dateutil.

You can check the installation by running the Jython interpreter and parse an example date string.

~$ ~/jython2.5.3/bin/jython
Jython 2.5.3 (2.5:c56500f08d34+, Aug 13 2012, 14:48:36) 
[OpenJDK 64-Bit Server VM (Sun Microsystems Inc.)] on java1.6.0_27
Type "help", "copyright", "credits" or "license" for more information.
>>> from dateutil.parser import *
>>> parse('Oct 5, 2013')
datetime.datetime(2013, 10, 5, 0, 0)
>>> 

So it works so far. You are now sure that the dateutils library is compatible with Jython.

5. Create the default standalone jython.jar Run the Jython installer a second time and as installation type choose "Standalone (a callable .jar file)". After that unpack the resulting JAR file (you can use every ZIP unpack tool) in a directory, e.g. ~/jar.

6. Install the dateutils library in jython.jar My first attempt was to copy the ~/jython2.5.3/Lib/site-packages/python_dateutil-1.5-py2.5.egg directory to ~/jar/Lib/site-packages. But this doesn't work in embedded mode. You have to copy ~/jython2.5.3/Lib/site-packages/python_dateutil-1.5-py2.5.egg/dateutil to ~/jar/Lib so that the dateutil directory is directly under ~/jar/Lib.

7. Create custom jython.jar Now zip the resulting ~/jar and name it jython.jar. Try it by running java -jar jython.jar. This will start a Jython interpreter from your jython.jar. Try to parse a date with dateutils.

Now you can include a whole Jython system including the dateutils library into your Java project only by put your custom jython.jar on the classpath.

8. Create a Java interface In your Java project create a Java interface. This will work as a bridge between the Java and the Jython world. Later you will create a Jython class implementing this interface. And from Java code you will use this Jython class via this interface.

In this example I call the interface DateutilParser and put it in the dateutilstest package.

package dateutiltest;

import java.util.Date;

public interface DateutilParser {
  Date parsedate(String s);
}

So the only method accepts a string as parameter and returns a java.util.Date. Nothing special for the Java side here.

9. Implement the interface with Jython Create the file MyDateParser.py in the root of your classpath (in Eclipse the root of the src directory).

import java.util.Date as Date
import java.util.GregorianCalendar as GregorianCalendar
import dateutiltest.DateutilParser as DateutilParser
from dateutil.parser import *


class MyDateParser(DateutilParser):
    def parsedate(self, s):
        d = parse(s)
        g = GregorianCalendar(d.year, d.month, d.day)
        return g.getTime()

At first we import the Java classes Date and GregorianCalendar. It is no problem and straight forward to use Java classes within Jython. Then we import our Java interface DateutilParser. The last import is for the dateutils parser classes. Then we create our Jython class MyDateParser implementing DateutilParser. In the parsedate method we first use the dateutil's parse() function to create a date out of the string and then convert it via a GregorianCalendar instance to a java.util.Date object.

10. Create a MyDateParser instance The last part in the puzzle is the DateutilParserFactory Java class, which is responsible to utilize the Jython interpreter to create a MyDateParser instance and provide access to it via the DateutilParser interface:

package dateutiltest;

import org.python.core.PyObject;
import org.python.util.PythonInterpreter;

public class DateutilParserFactory {
  private PyObject parserClass;

  /**
   * Create a new PythonInterpreter object, then use it to
   * execute some python code. In this case, we want to
   * import the python module that we will coerce.
   *
   * Once the module is imported than we obtain a reference to
   * it and assign the reference to a Java variable
   */
  public DateutilParserFactory() {
    final PythonInterpreter interpreter = new PythonInterpreter();
    interpreter.exec("from MyDateParser import MyDateParser");
    parserClass = interpreter.get("MyDateParser");
  }

  /**
   * The create method is responsible for performing the actual
   * coercion of the referenced python module into Java bytecode
   */
  public DateutilParser create() {
    PyObject buildingObject = parserClass.__call__(); 
    return (DateutilParser) buildingObject.__tojava__(DateutilParser.class);
  }
}

For details see the Jython book, chapter 10, Using Jython Within Java Applications.

11. Use it In this example I created a Main class to demonstrate the functionality:

package dateutiltest;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Main {
  public static void main(String[] args) {
    final DateutilParserFactory fac = new DateutilParserFactory();
    final DateutilParser parser = fac.create();
    final Date d = parser.parsedate("Oct 5, 2013");
    final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    System.out.println(sdf.format(d));
  }
}

And the output is:

2013-11-05
vanje
  • 10,180
  • 2
  • 31
  • 47
  • Wow, I hate to say this, but I considered using jython but rejected it immediately as too kludgy. I still feel like I don't want to add a dependency on jython and python libs in my system as it makes setup/deployment much harder. That being said, that's a really great answer irrespective of whether it's my preferred solution or not, and I wish I could upvote you several more times. – elhefe Oct 21 '13 at 23:43
  • Yes, adding Jython to a project only to be able to use a small library is a real drawback. I use embedded Jython for a small internal project mainly for the great text diff utility which is part of the standard library and for some things where Java reflection would be too complicated. But I always wanted to know, how to integrate a third party Python library in embedded. So your question made me curious. – vanje Oct 22 '13 at 11:47
0

There's DateUtils class in apache-commons-lang library, with method:

public static Date parseDate(String str, String[] parsePatterns) throws ParseException

According to JavaDocs:

Parses a string representing a date by trying a variety of different parsers.

The parse will try each parse pattern in turn. A parse is only deemed successful if it parses the whole of the input string. If no parse patterns match, a ParseException is thrown.

The parser will be lenient toward the parsed date.

Aneta Stępień
  • 704
  • 6
  • 20
  • yeah, sadly this has the same issue as discussed in the comments to the OP. – elhefe Oct 21 '13 at 19:29
  • Indeed, I haven't notice these comments. Taking one more look at the topic - given all the related questions and discussions (see: http://stackoverflow.com/questions/4024544/how-to-parse-dates-in-multiple-formats-using-simpledateformat, http://stackoverflow.com/questions/3389348/parse-any-date-in-java, http://stackoverflow.com/questions/1552643/parsing-ambiguous-dates-in-java) - I'd say there's no easier way to do it using standard libraries. – Aneta Stępień Oct 21 '13 at 19:38