javadatetimejodatime2-digit-year

Joda Time & parsing two digit year correctly based on pivot


I have the following (somewhat abbreviated) code to parse String input into a Joda Time DateTime object. I need to properly handle multiple formats, including four & two digit years.

setupValidDateFormats();
DateTime date = convertToDateTime(thisField.getText());
if (date != null) {
    thisField.setText(date.toString("MM/dd/yyyy"));
} else {
    System.out.println("Invalid date");
}

private void setupValidDateFormats() {
    DateTimeParser[] formats = {
            DateTimeFormat.forPattern("MM/dd/yyyy").getParser(),
            DateTimeFormat.forPattern("MM/dd/yy").getParser(),
            DateTimeFormat.forPattern("MM-dd-yyyy").getParser(),
            DateTimeFormat.forPattern("MM-dd-yy").getParser(),
            DateTimeFormat.forPattern("MMddyyyy").getParser(),
            DateTimeFormat.forPattern("MMddyy").getParser()
    };
    formatter = new DateTimeFormatterBuilder().append(null, formats).appendTwoDigitYear(1950, true).toFormatter()
            .withLocale(Locale.US);
}

private DateTime convertToDateTime(String input) {
    if (isNullOrEmpty(input)) {
        return null;
    }

    DateTime date;
    try {
        // When you parse a date, it'll throw an exception for not only invalid formats,
        // but for invalid values such as 09/31/2013 or 02/30/2013. Leap year is included as well.
        date = formatter.parseDateTime(input);
    } catch (Exception e) {
        // User input a date in the incorrect format, or an invalid value.
        // Example: 02/30/2013 is invalid.
        date = null;
    }

    return date;
}

The issue I'm having is that when the user enters 120100, I expect it to be able to parse correctly and ultimately output at 12/01/1900. However, formatter.parseDateTime(input); in convertToDateTime throws an IllegalArgumentException instead, and the method returns null.

Probably worth noting that if I remove the appendTwoDigitYear from the DateTimeFormatterBuilder chain, the input does parse successfully, but 120100 becomes 12/01/0000, but that's not what I need.

Am I misunderstanding the Joda Time API? Shouldn't appendTwoDigitYear with the 1950 pivot handle the 00 two year digit as 1900?


Solution

  • When you use a pattern like

    DateTimeFormat.forPattern("MMddyyyy")
    

    it can read a date like "120100". What it does is read the 12 for MM, the 01 for dd, and 00 as a literal value for yyyy, ie. 0 starting at year 0, so 0000.

    Because that pattern appears before the MMddyy in your parsers array, it will be used to parse your date string.

    What you'll want to do, along with reordering or removing that format, is use a pivot year.

    public static void main(String[] args) throws Exception {
        String str = "120100";
    
        DateTimeParser[] formats = {
                DateTimeFormat.forPattern("MM/dd/yyyy").getParser(),
                DateTimeFormat.forPattern("MM/dd/yy").getParser(),
                DateTimeFormat.forPattern("MM-dd-yyyy").getParser(),
                DateTimeFormat.forPattern("MM-dd-yy").getParser(),
                DateTimeFormat.forPattern("MMddyy").getParser()
        };
    
        DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(null, formats).toFormatter()
                .withPivotYear(1950).withLocale(Locale.US);
    
        DateTime dateTime = formatter.parseDateTime(str);
        System.out.println(dateTime);       
    }
    

    prints

    1900-12-01T00:00:00.000-05:00
    

    I'm still trying to figure out how appendTwoDigitYear works.