Search code examples
javakotlinparsinglocaldatekotest

LocalDate.parse fails for DateTimeFormatter with multiple patterns


You can create a DateTimeFormatter with multiple patterns using the DateTimeFormatterBuilder, but LocalDate.parse doesn't seem to like it. It works with a single pattern and fails with mutliple ones.

    test("can parse dates") {
        val formatter =
            DateTimeFormatterBuilder()
                .appendPattern("dd/MM/yy")
                .appendPattern("dd/MM/yyyy") // <-- fine when commented out
                .appendPattern("yyyy/MM/dd") // <-- fine when commented out
                .toFormatter()
        // error: Text '12/12/21' could not be parsed at index 8
        LocalDate.parse("12/12/21", formatter) shouldBe LocalDate.of(2021, 12, 12)
        
        // works just fine
        LocalDate.parse("12/12/21", DateTimeFormatter.ofPattern("dd/MM/yy")) shouldBe LocalDate.of(2021, 12, 12)
    }

Is there a way to make it work or do I have to use Regex to first figure out which pattern I need and then use it for parsing?


Solution

  • As a comment already explained, appendPattern adds to what the date format string wants. So, your date formatter would format today (november 18th 2023) as:

    18/11/2318/11/20232023/11/18
    

    And would be identical to:

    DateTimeFormatterBuilder()
                    .appendPattern("dd/MM/yydd/MM/yyyyyyyy/MM/dd");
    

    In general you can't just have DTF represent multiple conflicting alternatives. It does have some functionality for this - patterns can be placed in [] which marks them as optional. For example:

    DateTimeFormatterBuilder()
                    .appendPattern("yyyy[-]MM[-]dd");
    

    will both parse 20231118 and 2023-11-18 (and formats as 2023-11-18).

    But you can't use that to parse the 3 different formats you pasted. Because your formats are ambiguous.

    This:

    23/11/18 - is that 23rd of november 2018, or is it 23rd of november 18 (yes, 18 - as in, over 2000 years ago. It's still a valid date!), or is it November 18th in the year 23 (Still a valid date?)

    You presumably consider any date prior to the year 1000 to be 'invalid', or at least as something that must be rendered as 0001 for example, so you might be able to do something nutty with optional markers, but that's not really what it is for.

    Regexp it is. Regexp to figure out what format it is in, then DTF to parse that into a LocalDate object.