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?
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