Search code examples
javajava-timedate-parsinglocaldate

Overwrite DateTimeFormatter localized date style parsing strategy for century


I need to parse the date string dynamically based on the locale and format style.

For example, I have an Albanian locale which has pattern yy-MM-dd

I have following code which resolves this pattern basing on the current locale and format style

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .appendLocalized(FormatStyle.SHORT, null)
                .toFormatter()
                .withLocale(Locale.forLanguageTag("sq-AL"));
TemporalAccessor temporalAccessor = formatter.parseBest("92-07-09", LocalDateTime::from, LocalDate::from, LocalTime::from);
System.out.println(temporalAccessor);

The input string is parsed as 09/07/2092

But I need to parse this date as 09/07/1992

Adding the code for .appendValueReduced doesn't work

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                .appendLocalized(FormatStyle.SHORT, null)
                .appendValueReduced(ChronoField.YEAR, 2, 2, LocalDate.now().minusYears(80))
                .toFormatter()
                .withLocale(Locale.forLanguageTag("sq-AL"));

I've searched for answers on StackOverflow but did not find any that works without .appendPattern() and based on the locale and format style

Thanks in advance!


Solution

  • Building up on this answer, you can first extract the pattern from the input string like so:

    String shortPattern =
        DateTimeFormatterBuilder.getLocalizedDateTimePattern(
            FormatStyle.SHORT,
            null,
            IsoChronology.INSTANCE,
            Locale.forLanguageTag("sqi-AL")
        );
    System.out.println(shortPattern); //y-MM-d
    

    Now you can apply your formatter with specific year instructions. The pattern now is given explicitly with the ys removed since year is now being handled by appendValueReduced:

    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
                    .appendOptional(DateTimeFormatter.ofPattern(shortPattern.replaceAll("y","")))
                    .appendValueReduced(ChronoField.YEAR, 2, 4, LocalDate.now().minusYears(80))
                    .appendOptional(DateTimeFormatter.ofPattern(shortPattern.replaceAll("y","")))
                    .toFormatter();
    
    TemporalAccessor temporalAccessor = formatter.parseBest("92-07-09", LocalDate::from, LocalDateTime::from);
    System.out.println(temporalAccessor); //1992-07-09
    

    The reason for appendOptional is that if the locale pattern has year at the end, it could lead to parse error. For example, the pattern for the locale in your code(sq-AL) is actually d.M.yy. So we need to check for year at both ends.