はじめに
Java16でDateTimeFormatterクラスの書式設定に "B" (period-of-day)が追加されました。
以下のドキュメントには意味として period-of-day 、例が in the morning とあるだけでよくわかりません。
そこで、この書式設定 "B" について実機確認とソースから調べました。
実機確認
各時刻で書式設定 B を指定したときにどのように変換されるかを確認。
確認用ソース
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
.....
* 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
<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
<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
<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() で行われています。
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