javadatedatetime

How to maximize date according to the provided date format?


I need to maximize provided date according to the full ISO format using.

Suppose we have the predefined some input data as String inputDate and String inputDateFormat which maximizes to the maximizedDate as:

inputDate inputDateFormat maximizedDate
2025 yyyy 2025-12-31T23:59:59
2025-01 yyyy-MM 2025-01-31T23:59:59
2025-01-15 yyyy-MM-dd 2025-01-15T23:59:59
2025-01-15T09 yyyy-MM-dd'T'HH 2025-01-15T09:59:59
2025-01-15T09:15 yyyy-MM-dd'T'HH-mm 2025-01-15T09:15:59
2025-02 yyyy-MM 2025-02-28T23:59:59

In the last example date was maximized to 2025-02-28T23:59:59 because there are no 31 February. The same logic applies to any months with 30/31 days.

I know that for example 2025-01-15 formatted by the DateTimeFormatter.ISO_LOCAL_DATE_TIME is equals to 2025-01-15T00:00:00. But this won't help me because I can't be sure if the input data was 2025-01-15 or 2025-01-15T00 without the input format.

I think solution is to use the TemporalAdjuster, but I need to know what should be adjusted like: DateFormat.of(inputDateFormat).isYearPresented() or DateFormat.of(inputDateFormat).isMinutesPresented().

Any ideas how can I achieve this?


Solution

  • DateTimeFormatterBuilder.parseDefaulting()

    Perhaps surprising this formatter works for your task:

        private static final DateTimeFormatter formatter
                = new DateTimeFormatterBuilder()
                        .appendPattern("uuuu[-MM[-dd['T'HH[:mm[:ss]]]]]")
                        .parseDefaulting(ChronoField.MONTH_OF_YEAR, 12)
                        .parseDefaulting(ChronoField.DAY_OF_MONTH, 31)
                        .parseDefaulting(ChronoField.HOUR_OF_DAY, 23)
                        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 59)
                        .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 59)
                        .parseDefaulting(ChronoField.NANO_OF_SECOND, 999_999_999)
                        .toFormatter(Locale.ROOT);
    

    I have specified 31 as the day of month to pick when no day of month is in the string. For February 2025, which has 28 days, this gives — 2025-02-28T23:59:59.999999999, so the last day of February. It seems that java.time is smart enough to just pick the last day of the month.

    In my format pattern string I have used square brackets [] to enclose optional parts, and I have nested those. If the input format string is given to you, you may alternatively pass it to appendPattern() instead of mine; but it will require you to build a new formatter for each input format whereas my formatter handles all of your possible input formats.

    Full demonstration:

        String[] inputs = { "2025", "2025-01", "2025-02", "2025-01-15", "2025-01-15T09", "2025-01-15T09:15" };
        for (String input : inputs) {
            LocalDateTime localDateTime = LocalDateTime.parse(input, formatter);
            System.out.format(Locale.ENGLISH, "%-16s -> %s%n", input, localDateTime);
        }
    

    Output:

    2025             -> 2025-12-31T23:59:59.999999999
    2025-01          -> 2025-01-31T23:59:59.999999999
    2025-02          -> 2025-02-28T23:59:59.999999999
    2025-01-15       -> 2025-01-15T23:59:59.999999999
    2025-01-15T09    -> 2025-01-15T09:59:59.999999999
    2025-01-15T09:15 -> 2025-01-15T09:15:59.999999999
    

    You said in a comment:

    Yeah, but for simplification of the question lets truncate this Dates to the ChronoUnit.SECONDS and without zones :)

    So just leave out .parseDefaulting(ChronoField.NANO_OF_SECOND, 999_999_999).

    2025             -> 2025-12-31T23:59:59
    2025-01          -> 2025-01-31T23:59:59
    2025-02          -> 2025-02-28T23:59:59
    2025-01-15       -> 2025-01-15T23:59:59
    2025-01-15T09    -> 2025-01-15T09:59:59
    2025-01-15T09:15 -> 2025-01-15T09:15:59