Hakanai
Hakanai

Reputation: 12670

How do I ignore the time zone when parsing with Joda's DateTimeFormatter?

I'm trying to parse rubbish date values out of IPTC metadata. The format is supposed to be yyyyMMdd but in some situations it isn't. A particular value I have found is "Tue Jan 05 00:00:00 AEDT 2016".

If I try to parse this using Joda's DateTimeFormatter:

DateTimeFormat.forPattern("EEE MMM dd HH:mm:ss Z yyyy")
              .parseLocalDate("Tue Jan 05 00:00:00 AEDT 2016");

This gives me an error:

java.lang.IllegalArgumentException: Invalid format: "Tue Jan 05 00:00:00 AEDT 2016" is malformed at "AEDT 2016"

I have tried the following zone symbols:

I realise that the docs do say that DateTimeFormatter can't parse time zones. I also realise that these short time zone names are ambiguous. But in this situation I'm only trying to get out a LocalDate, so all I really want is the month, day and year. (Notice how the hour, minute and second are also zero?)

I would rather not have to regex crap out of the middle of the string before passing it off if possible, because the thing I'm passing this formatter off to expects a DateTimeFormatter at present.

Is there a way to specify some kind of arbitrary pattern of junk to throw away when parsing? I can't seem to find it in the API, but that doesn't necessarily mean it isn't there.

Upvotes: 2

Views: 2039

Answers (3)

weston
weston

Reputation: 54781

So the problem is the huge interface of DateTimeFormatter which prevents you writing your own implementation or decorator easily.

...the thing I'm passing this formatter off to expects a DateTimeFormatter at present.

Is it out of the question to refactor the framework (the "thing") like so:

Step 1: Create a new interface:

public interface LocalDateTimeParser {
    LocalDate parseLocalDate(String text);
}

Step 2: Create an adaptor:

public final class DateTimeFormatterAdapter implements LocalDateTimeParser {

    private final DateTimeFormatter adaptee;

    public DateTimeFormatterAdapter(DateTimeFormatter adaptee){
        this.adaptee = adaptee;
    }

    public LocalDate parseLocalDate(String text){
        return adaptee.parseLocalDate(text);
    }
}

Step 2: Reference LocalDateTimeParser in your framework where only local parsing is required and wrap the existing DateTimeFormatter in an adapter.

Step 3: Now you are free to write your own LocalDateTimeParser implementation with regex pre processing to strip the timezone and pass the rest to a DateTimeFormatter.

Upvotes: 0

Hakanai
Hakanai

Reputation: 12670

I'll post my own message workaround in the meantime, which uses a mix of existing format patterns plus a custom parser to throw away the time zone.

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
    .appendPattern("EEE MMM dd HH:mm:ss ")
    .append(DateTimeFormat.forPattern("Z").getPrinter(), new DiscardTimeZoneSymbolParser())
    .appendPattern(" yyyy")
    .toFormatter();

LocalDate localDate = formatter.parseLocalDate("Tue Jan 05 00:00:00 AEDT 2016");

And then:

public class DiscardTimeZoneSymbolParser implements DateTimeParser {
    @Override
    public int estimateParsedLength() {
        return 4;
    }

    @Override
    public int parseInto(DateTimeParserBucket bucket, @NonNls String text, int position) {
        for (int positionFromStart = 0; positionFromStart < 4; positionFromStart++, position++) {
            boolean match;
            if (position >= text.length()) {
                match = false;
            } else {
                @NonNls
                char ch = text.charAt(position);
                match = ch >= 'A' && ch <= 'Z';
            }

            if (!match) {
                if (positionFromStart >= 3) { // require 3 characters
                    return position;
                } else {
                    return ~position;
                }
            }
        }

        return position;
    }
}

Upvotes: 3

Raffaele
Raffaele

Reputation: 20885

You can use the parsing facilities in java.text:

DateFormat df = new SimpleDateFormat("EEE MMM dd HH:mm:ss Z yyyy", Locale.ENGLISH);
df.setLenient(false);
Date date = df.parse("Tue Jan 05 00:00:00 AEDT 2016");

and then transform the java.util.Date in the Joda LocalDate. Note that:

  • neither Date nor LocalDate have a timezone: Date is UTC, and LocalDate refers to a Chronology, not to an instant
  • I specified the Locale for the parser, but you may need to use the default locale of the JMV, or any other locale depending on your input

Upvotes: 0

Related Questions