Java16で追加されたDateTimeFormatterクラスの書式設定"B"について

# はじめに
Java16でDateTimeFormatterクラスの書式設定に "B" (period-of-day)が追加されました。
以下のドキュメントには意味として period-of-day 、例が in the morning とあるだけでよくわかりません。
 
https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/time/format/DateTimeFormatter.html
 
そこで、この書式設定 "B" について実機確認とソースから調べました。
 
# 実機確認
 
各時刻で書式設定 B を指定したときにどのように変換されるかを確認。 
## 確認用ソース

```java:TestPreiodOfDay.java
 import java.time.format.DateTimeFormatter;
 import java.time.LocalTime;
 
 public class TestPreiodOfDay  {
   public static void main(String[] args) {
     String s1,s2 ;
     int i ;
     LocalTime  lt   ;
     DateTimeFormatter f = DateTimeFormatter.ofPattern("HH:mm");
     DateTimeFormatter fmt_period = DateTimeFormatter.ofPattern("B");
 
     for ( i = 0 ; i < 24 ; i++ ) {
       lt  =  LocalTime.of(i, 00);
       s1 = lt.format(f);
       s2 = lt.format(fmt_period);
       System.out.println(s1 + " = " + s2);
     }
   }
 }
 ```

## 実行結果
 00:00 = midnight
 01:00 = at night
 02:00 = at night
 03:00 = at night
 04:00 = at night
 05:00 = at night
 06:00 = in the morning
 07:00 = in the morning
 08:00 = in the morning
 09:00 = in the morning
 10:00 = in the morning
 11:00 = in the morning
 12:00 = noon
 13:00 = in the afternoon
 14:00 = in the afternoon
 15:00 = in the afternoon
 16:00 = in the afternoon
 17:00 = in the afternoon
 18:00 = in the evening
 19:00 = in the evening
 20:00 = in the evening
 21:00 = at night
 22:00 = at night
 23:00 = at night
 
OpenJDKのソースを確認するとこの時間帯とメッセージはハードコーディングされており、固定になっています。
ただ、ロケールによって時間帯やメッセージは変わります。
 
ロケールが日本語(ja_JP.UTF-8) の場合
    System Locale: LANG=ja_JP.UTF-8
 
## 実行結果(ロケール ja_JP.UTF-8)
 00:00 = 真夜中
 01:00 = 夜中
 02:00 = 夜中
 03:00 = 夜中
 04:00 = 朝
 05:00 = 朝
 06:00 = 朝
 07:00 = 朝
 08:00 = 朝
 09:00 = 朝
 10:00 = 朝
 11:00 = 朝
 12:00 = 正午
 13:00 = 昼
 14:00 = 昼
 15:00 = 昼
 16:00 = 夕方
 17:00 = 夕方
 18:00 = 夕方
 19:00 = 夜
 20:00 = 夜
 21:00 = 夜
 22:00 = 夜
 23:00 = 夜中
 
 英語では6時から朝(in the morning)なのに対し、日本語では4時から「朝」です。
 
# JDKソース確認
 
ドキュメントには記載されていませんがソースを見ると "B" 以外に "BBBB" , "BBBBB" の書式も用意されています。
 
jdk/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java 
 https://github.com/openjdk/jdk/blob/ddcd851c43aa97477c7e406490c0c7c7d71ac629/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java
 
```java:DateTimeFormatterBuilder.java 
 .....
      *  Pattern  Count  Equivalent builder methods
      *  -------  -----  --------------------------
      *    B       1      appendDayPeriodText(TextStyle.SHORT)
      *    BBBB    4      appendDayPeriodText(TextStyle.FULL)
      *    BBBBB   5      appendDayPeriodText(TextStyle.NARROW)
      
 .....
     private void parsePattern(String pattern) {
        .......
                 } else if (cur == 'B') {
                     switch (count) {
                         case 1 -> appendDayPeriodText(TextStyle.SHORT);
                         case 4 -> appendDayPeriodText(TextStyle.FULL);
                         case 5 -> appendDayPeriodText(TextStyle.NARROW);
                         default -> throw new IllegalArgumentException("Wrong number of pattern letters: " + cur);
```

"BBBB" または "BBBBB"  を指定するとメッセージの長さが変わるようですが、en_US ロケールの場合はほとんど変わらずです。
 
時刻範囲に対するtypeは以下で定義されています。
jdk/make/data/cldr/common/supplemental/dayPeriods.xml
 
```xml:dayPeriods.xml

 		<dayPeriodRules locales="en">
 			<dayPeriodRule type="midnight" at="00:00"/>	<!-- midnight -->
 			<dayPeriodRule type="noon" at="12:00"/>	<!-- noon -->
 			<dayPeriodRule type="morning1" from="06:00" before="12:00"/>	<!-- morning -->
 			<dayPeriodRule type="afternoon1" from="12:00" before="18:00"/>	<!-- afternoon -->
 			<dayPeriodRule type="evening1" from="18:00" before="21:00"/>	<!-- evening -->
 			<dayPeriodRule type="night1" from="21:00" before="06:00"/>	<!-- night -->
 		</dayPeriodRules>
 .........
 		<dayPeriodRules locales="ja">
 			<dayPeriodRule type="midnight" at="00:00"/>	<!-- 真夜中 -->
 			<dayPeriodRule type="noon" at="12:00"/>	<!-- 正午 -->
 			<dayPeriodRule type="morning1" from="04:00" before="12:00"/>	<!-- 朝 -->
 			<dayPeriodRule type="afternoon1" from="12:00" before="16:00"/>	<!-- 昼 -->
 			<dayPeriodRule type="evening1" from="16:00" before="19:00"/>	<!-- 夕方 -->
 			<dayPeriodRule type="night1" from="19:00" before="23:00"/>	<!-- 夜 -->
 			<dayPeriodRule type="night2" from="23:00" before="04:00"/>	<!-- 夜中 -->
 		</dayPeriodRules>
```

type に対するメッセージは以下で定義されています。
jdk/make/data/cldr/common/main/en.xml

```xml:en.xml
 				<dayPeriods>
 					<dayPeriodContext type="format">
 						<dayPeriodWidth type="abbreviated">
 							<dayPeriod type="midnight">midnight</dayPeriod>
 							<dayPeriod type="am">AM</dayPeriod>
 							<dayPeriod type="am" alt="variant">am</dayPeriod>
 							<dayPeriod type="noon">noon</dayPeriod>
 							<dayPeriod type="pm">PM</dayPeriod>
 							<dayPeriod type="pm" alt="variant">pm</dayPeriod>
 							<dayPeriod type="morning1">in the morning</dayPeriod>
 							<dayPeriod type="afternoon1">in the afternoon</dayPeriod>
 							<dayPeriod type="evening1">in the evening</dayPeriod>
 							<dayPeriod type="night1">at night</dayPeriod>
```

jdk/make/data/cldr/common/main/ja.xml

```xml:ja.xml
 				<dayPeriods>
 					<dayPeriodContext type="format">
 						<dayPeriodWidth type="abbreviated">
 							<dayPeriod type="midnight">真夜中</dayPeriod>
 							<dayPeriod type="am">午前</dayPeriod>
 							<dayPeriod type="noon">正午</dayPeriod>
 							<dayPeriod type="pm">午後</dayPeriod>
 							<dayPeriod type="morning1">朝</dayPeriod>
 							<dayPeriod type="afternoon1">昼</dayPeriod>
 							<dayPeriod type="evening1">夕方</dayPeriod>
 							<dayPeriod type="night1">夜</dayPeriod>
 							<dayPeriod type="night2">夜中</dayPeriod>
 ...........
```

書式設定 Bに対応する主な処理は DayPeriodPrinterParser クラスの format() で行われています。

```java:
     static final class DayPeriodPrinterParser implements DateTimePrinterParser {
     .....
 
         public boolean format(DateTimePrintContext context, StringBuilder buf) {
             Long hod = context.getValue(HOUR_OF_DAY);
             if (hod == null) {
                 return false;
             }
             Long moh = context.getValue(MINUTE_OF_HOUR);
             long value = Math.floorMod(hod, 24) * 60 + (moh != null ? Math.floorMod(moh, 60) : 0);
             Locale locale = context.getLocale();
             LocaleStore store = findDayPeriodStore(locale);
             final long val = value;
             final var map = DayPeriod.getDayPeriodMap(locale);
             value = map.keySet().stream()
                     .filter(k -> k.includes(val))
                     .min(DayPeriod.DPCOMPARATOR)
                     .map(map::get)
                     .orElse(val / 720); // fall back to am/pm
             String text = store.getText(value, textStyle);
             buf.append(text);
             return true;
         }
```

# 確認環境

CentOS Linux release 7.8.2003 (Core)
openjdk version "16.0.2" 2021-07-20