Search code examples
javajava-timedatetimeformatter

Java 8 DateTimeFormatter's symbol "F"


In java.time.format.DateTimeFormatterBuilder:

/** Map of letters to fields. */
private static final Map<Character, TemporalField> FIELD_MAP = new HashMap<>();

static {
    // SDF = SimpleDateFormat
    // ...
    FIELD_MAP.put('F', ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH);  // SDF, LDML
    // ...
}

It shows that the symbol "F" may have the same meaning in SimpleDateFormatter(SDF).

In SDF, the symbol "F" means the ordinal of designated day of week, such as "the second Sunday in May" , Mother's Day (US).
With SDF, I can define Mother's Day (US) in 2021 by "2021-05-2-Sun" with pattern "yyyy-MM-F-EEE".
But any pattern with the symbol "F" with DateTimeFormatter may cause a DateTimeParseException.
I found an issue that is already "fixed":
[JDK-8169482]java.time.DateTimeFormatter javadoc: F is not week-of-month
But it only "fixed" the javadoc! Σ(っ °Д °;)っ
The symbol "F" is still with an undefined behavior.

The confusing description in SDF "F: Day of week in month" may be the reason.


Solution

  • Since the resolution of the bug was to fix the documentation, rather than to fix the DateTimeFormatter's implementation, it seems like F meaning ALIGNED_DAY_OF_WEEK_IN_MONTH is actually intended, and it's only that the // SDF comment hasn't been corrected.

    And because it means ALIGNED_DAY_OF_WEEK_IN_MONTH, a date such as "2021-05-2-Sun" doesn't make much sense. You've only given a month and a year, and the day of the week. That's not enough information to figure out which day of month it is. It doesn't happen with "2021-05-2-Sun", but in cases like "2021-05-2-Mon", the day of week (Mon), and the aligned day of week (2) can even say different things about which day of week it should be. :)

    To parse an "nth day-of-week", you need ALIGNED_WEEK_OF_MONTH, which DateFormatter doesn't have a symbol for. (Note that W is the non-aligned week of month.) But don't worry, you can append any TemporalField into the pattern with appendValue:

    DateTimeFormatter formatter =
        new DateTimeFormatterBuilder()
            .appendPattern("yyyy-MM-")
            .appendValue(ChronoField.ALIGNED_WEEK_OF_MONTH)
            .appendPattern("-EEE").toFormatter(Locale.ENGLISH);
    

    Usage:

    System.out.println(LocalDate.parse("2021-05-2-Sun", formatter));
    // 2021-05-09