Java Date Format with Day Period: Pattern B Examples

Java date format with day period using pattern B in DateTimeFormatter (Java 16+). Print in the morning or at night, parse it and set custom ranges.

Timeline of a 24-hour day showing the US English day periods that the pattern letter B prints in Java 25

For a date format with day period in Java, we use the DateTimeFormatter pattern letter B, available since Java 16, which prints texts such as in the morning, in the afternoon, in the evening or at night instead of the AM/PM marker. The texts and the hours they cover come from the Unicode CLDR data of the locale, so the same pattern also works in German, French or Japanese.

We use day periods where a time should read like speech, for example, a chat app that shows last seen at 9:30 at night, a reminder in a to-do app or a voice assistant that reads the time aloud.

The following example formats a few times with the pattern h:mm B.

DateTimeFormatter friendly = DateTimeFormatter.ofPattern("h:mm B", Locale.US);
String afternoon = LocalTime.of(17, 45).format(friendly);    // "5:45 in the afternoon"
String night = LocalTime.of(21, 30).format(friendly);        // "9:30 at night"
String noon = LocalTime.NOON.format(friendly);               // "12:00 noon"
String german = LocalTime.of(17, 45).format(DateTimeFormatter.ofPattern("h:mm B", Locale.GERMANY));   // "5:45 nachmittags"

Notice that noon gets its own word, and that we pass a Locale, because the period text is localized. We look at the hours behind each period first, followed by full dates, other languages, parsing and custom periods for apps that need their own ranges.

1. Date Format with Day Period (Since Java 16)

The AM/PM marker splits the day into two halves. A day period splits it into more parts, and the borders depend on the language, which is why Java takes them from the CLDR day period rules instead of a fixed table. The letter B was added to DateTimeFormatter in Java 16 (JDK-8247781).

Timeline of a 24-hour day showing the US English day periods that the pattern letter B prints in Java 25
Pattern B in Locale.US on Java 25; midnight and noon are single moments with their own words

For Locale.US, Java 25 prints these periods. The borders are inclusive at the start, so 18:00 is already the evening.

TimePattern aPattern B
00:00AMmidnight
00:01 to 11:59AMin the morning
12:00PMnoon
12:01 to 17:59PMin the afternoon
18:00 to 20:59PMin the evening
21:00 to 23:59PMat night

In US English, a time such as 3:00 is in the morning, not at night, because Java’s English night period ends at midnight. If our app must say at night for 3:00, we need custom ranges, as shown in section 4.

1.1. Pattern Letters B, BBBB and BBBBB

The letter B accepts one, four or five repetitions. One and four letters print the full text in English, and five letters print the narrow form, which matters only for noon and midnight. Two or three letters are rejected when the pattern is created.

LocalTime lunch = LocalTime.NOON;
String one = LocalTime.of(17, 45).format(DateTimeFormatter.ofPattern("B", Locale.US));       // "in the afternoon"
String four = LocalTime.of(17, 45).format(DateTimeFormatter.ofPattern("BBBB", Locale.US));   // "in the afternoon"
String narrow = lunch.format(DateTimeFormatter.ofPattern("BBBBB", Locale.US));              // "n"
DateTimeFormatter two = DateTimeFormatter.ofPattern("BB");                                  // IllegalArgumentException: Wrong number of pattern letters: B

We can combine a and B in one pattern, but readers see the same information twice. In practice, we pair B with the 12-hour clock h and drop a.

1.2. Day Periods With a Full Date and Time Zone

The letter B reads the hour of the day, so it works with LocalTime, LocalDateTime, ZonedDateTime and OffsetDateTime. A LocalDate has no hour, and formatting it with B throws an UnsupportedTemporalTypeException.

LocalDateTime meeting = LocalDateTime.of(2026, 3, 14, 17, 45);
String withDate = meeting.format(DateTimeFormatter.ofPattern("EEEE, MMM d 'at' h:mm B", Locale.US));   // "Saturday, Mar 14 at 5:45 in the afternoon"
String dateOnly = LocalDate.of(2026, 3, 14).format(DateTimeFormatter.ofPattern("MMM d B", Locale.US));   // UnsupportedTemporalTypeException: Unsupported field: HourOfDay

Say a chat app stores the last activity of each user as an Instant. The friend who reads last seen lives in another time zone, so we convert the instant to the reader’s zone before formatting. The period text matches the clock of the reader.

Instant lastActive = Instant.parse("2026-03-14T21:45:00Z");
DateTimeFormatter lastSeen = DateTimeFormatter.ofPattern("'last seen at' h:mm B", Locale.US);
String inNewYork = lastActive.atZone(ZoneId.of("America/New_York")).format(lastSeen);   // "last seen at 5:45 in the afternoon"
String inKolkata = lastActive.atZone(ZoneId.of("Asia/Kolkata")).format(lastSeen);      // "last seen at 3:15 in the morning"

The time zone conversion itself is covered in Convert Date Between Time Zones, and other pattern letters in Format a Date to String.

1.3. appendDayPeriodText() in DateTimeFormatterBuilder

When we build a formatter in code, appendDayPeriodText() adds the same field as the letter B. It takes a TextStyle, where FULL matches BBBB and NARROW matches BBBBB.

DateTimeFormatter built = new DateTimeFormatterBuilder()
        .appendPattern("h:mm ")
        .appendDayPeriodText(TextStyle.FULL)
        .toFormatter(Locale.US);
String reminder = LocalTime.of(9, 15).format(built);       // "9:15 in the morning"

2. Day Periods in Other Languages

Each language has its own periods and borders. German splits the morning into morgens and vormittags, Spanish has de la madrugada for the early hours, and French has no separate night after 21:00. The table shows Java 25 output for four times.

TimeLocale.USLocale.GERMANYLocale.FRANCESpanish (es-ES)
03:00in the morningnachtsdu matinde la madrugada
10:00in the morningvormittagsdu matinde la mañana
17:45in the afternoonnachmittagsde l’après-midide la tarde
22:00at nightabendsdu soirde la noche

A fixed pattern such as h:mm B keeps the period after the time in every language, which is wrong for Japanese, where the period comes first. The method ofLocalizedPattern(), added in Java 19, takes a template of fields and returns the order of the locale, as Localized Date Time Formats explains.

LocalTime evening = LocalTime.of(17, 45);
String usOrder = evening.format(DateTimeFormatter.ofLocalizedPattern("Bhm").withLocale(Locale.US));      // "5:45 in the afternoon"
String jaOrder = evening.format(DateTimeFormatter.ofLocalizedPattern("Bhm").withLocale(Locale.JAPAN));   // "夕方5:45"

A locale without day period data, such as Locale.ROOT, falls back to AM and PM, so the output always contains some marker.

3. Parsing Text With Day Periods

A formatter with B also parses. With a 12-hour clock, the day period decides whether the hour belongs to the morning or the afternoon, in the same way as AM and PM.

DateTimeFormatter parser = DateTimeFormatter.ofPattern("h:mm B", Locale.US);
LocalTime parsedAfternoon = LocalTime.parse("5:45 in the afternoon", parser);   // 17:45
LocalTime parsedNight = LocalTime.parse("9:30 at night", parser);               // 21:30

When the text contains only a day period, Java sets the time to the middle of the period in the default SMART resolver style. When the hour conflicts with the period, the SMART and STRICT styles throw a DateTimeParseException, and the LENIENT style ignores the period.

LocalTime middle = LocalTime.parse("in the morning", DateTimeFormatter.ofPattern("B", Locale.US));   // 06:00
DateTimeFormatter clock24 = DateTimeFormatter.ofPattern("H:mm B", Locale.US);
LocalTime conflict = LocalTime.parse("17:45 in the morning", clock24);   // DateTimeParseException: Text '17:45 in the morning' could not be parsed: Conflict found: Resolved time 17:45 conflicts with DayPeriod(00:00-12:00)
LocalTime ignored = LocalTime.parse("17:45 in the morning", clock24.withResolverStyle(ResolverStyle.LENIENT));   // 17:45

The conflict check helps when users type times by hand, because a typo such as 17:45 in the morning is reported instead of stored. More about resolver styles is in ResolverStyle.STRICT.

4. Custom Day Periods With Our Own Ranges

Sometimes the product needs its own ranges, for example, a food delivery app that labels orders as breakfast, lunch or dinner, or an app that must say at night until 6:00. The CLDR periods cannot be changed, so we compute the label ourselves. Before Java 16, this was the only way to print a day period.

We compare the hour with half-open ranges, where the start belongs to the period and the end does not. A switch on the hour covers each hour of the day once, so no time falls between two periods.

static String mealTime(LocalTime time) {
    return switch (time.getHour()) {
        case 6, 7, 8, 9, 10 -> "breakfast";
        case 11, 12, 13, 14 -> "lunch";
        case 15, 16, 17 -> "snack";
        case 18, 19, 20, 21, 22 -> "dinner";
        default -> "late night";
    };
}
String early = mealTime(LocalTime.of(6, 0));          // "breakfast"
String midday = mealTime(LocalTime.of(12, 30));       // "lunch"
String late = mealTime(LocalTime.of(23, 15));         // "late night"
String label = LocalTime.of(19, 5).format(DateTimeFormatter.ofPattern("h:mm a", Locale.US)) + " (" + mealTime(LocalTime.of(19, 5)) + ")";   // "7:05 PM (dinner)"

A common bug in hand-written versions is a chain of isAfter() and isBefore() checks. Both methods exclude the border, so a time such as 06:00 or 18:00 on the border matches no branch and gets no label. The switch on getHour() avoids that gap, and unlike the B letter, the labels are not translated, so a multi-language app keeps them in a resource bundle.

5. Day Period Format FAQs

The letter B is newer than most date tutorials, so its Java version and its English wording raise the first questions.

5.1. Which Java version added the day period pattern B?

Java 16. The letter B and DateTimeFormatterBuilder.appendDayPeriodText() are not available in Java 8, 11 or 15, where the pattern throws IllegalArgumentException.

5.2. What is the difference between the pattern letters a and B?

The letter a prints AM or PM, two halves of the day. The letter B prints the CLDR day period, which has more parts, special words for noon and midnight, and borders that differ by language.

5.3. Does SimpleDateFormat support day periods?

No. The legacy SimpleDateFormat class does not know the letter B, and new SimpleDateFormat(“h:mm B”) throws IllegalArgumentException: Illegal pattern character ‘B’. We convert a java.util.Date with toInstant() and format it with DateTimeFormatter.

5.4. Why does 3 AM print “in the morning” and not “at night”?

Because Java 25, when it formats English text, maps every time from 00:01 to 11:59 to the morning period and uses at night only from 21:00 to 23:59. Other locales differ, for example, German prints nachts for 3:00. For our own wording, we use custom ranges as in section 4.

6. Conclusion

The letter B in a DateTimeFormatter pattern prints the day period of the locale, such as in the afternoon or nachmittags, and Java has supported it since Java 16. It needs a type with an hour, a Locale for the language and, for an Instant, a time zone.

The same formatter parses day periods and reports conflicts with the hour. When the CLDR borders do not fit the product, a switch on getHour() gives custom labels without gaps at the borders. For the remaining pattern letters and the parsing guides, the Java date and time guide is the starting point.

7. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.