日期时间增强 — Day Period 支持(JDK 16)
飞翔科技要做海外版本,黄俪负责前端国际化。她发现一个尴尬的问题——英文里"上午/下午/晚上"的习惯和中文不一样,Java 的
DateTimeFormatter却只有a(AM/PM)这一个时段符号。"英文里
3 PM表示下午三点没问题,但1 AM到底是凌晨还是早上?" 黄俪抱怨道。白歌在 JDK 16 的 Release Notes 里翻到一个点:"JDK 16 新增了时段符号
B,支持细粒度的 day-period,能区分出in the morning、in the afternoon、at night等——正好解决你的问题。""一个字母一个功能——" 黄俪感叹,"JDK 升级的快乐就是这么朴实无华。"
定义表
| 概念 | 描述 |
|---|---|
时段符号 B(Day Period) | DateTimeFormatter 新增的格式化符号,表示一天中的时段(morning / afternoon / evening / night 等),比传统的 a(AM/PM)更细粒度 |
传统符号 a | AM / PM 两段式,仅区分上午和下午 |
| DateTimeFormatterBuilder.dayPeriod() | 编程方式构建 Day Period 规则的新方法,可自定义时段边界 |
| CLDR 数据 | Unicode Common Locale Data Repository——Java 的 locale 数据源,Day Period 的翻译来自 CLDR |
Mermaid 对比图:符号 a vs 符号 B
基础用法:符号 B 格式化
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class DayPeriodDemo {
public static void main(String[] args) {
LocalTime[] times = {
LocalTime.of(3, 0), // 凌晨 3 点
LocalTime.of(9, 30), // 上午 9:30
LocalTime.of(12, 0), // 中午 12 点
LocalTime.of(15, 45), // 下午 3:45
LocalTime.of(20, 0), // 晚上 8 点
LocalTime.of(23, 30) // 深夜 11:30
};
// 中文环境
DateTimeFormatter cnFormatter = DateTimeFormatter
.ofPattern("B HH:mm", Locale.CHINA);
// 英文环境
DateTimeFormatter usFormatter = DateTimeFormatter
.ofPattern("'Time:' B h:mm a", Locale.US);
System.out.println("=== 中文时段(B)===");
for (LocalTime t : times) {
System.out.println(cnFormatter.format(t));
}
System.out.println("\n=== 英文时段(B + a 对比)===");
for (LocalTime t : times) {
System.out.println(usFormatter.format(t));
}
}
}
输出:
=== 中文时段(B)===
凌晨 03:00
上午 09:30
中午 12:00
下午 15:45
晚上 20:00
晚上 23:30
=== 英文时段(B + a 对比)===
Time: at night 3:00 AM
Time: in the morning 9:30 AM
Time: noon 12:00 PM
Time: in the afternoon 3:45 PM
Time: in the evening 8:00 PM
Time: at night 11:30 PM
关键对比:英文 3:00 AM 用
a只显示AM,用B则显示at night,语义更精确。
符号 a 与 B 的对比
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class A_vs_B_Comparison {
public static void main(String[] args) {
LocalTime[] times = {
LocalTime.of(2, 0),
LocalTime.of(10, 0),
LocalTime.of(14, 0),
LocalTime.of(21, 0)
};
DateTimeFormatter fmtA = DateTimeFormatter.ofPattern("hh:mm a", Locale.US);
DateTimeFormatter fmtB = DateTimeFormatter.ofPattern("hh:mm B", Locale.US);
System.out.println("时间 符号 a (AM/PM) 符号 B (Day Period)");
System.out.println("-------- ---------------- ---------------------");
for (LocalTime t : times) {
System.out.printf("%s %-15s %s%n",
t,
fmtA.format(t),
fmtB.format(t));
}
}
}
输出:
时间 符号 a (AM/PM) 符号 B (Day Period)
-------- ---------------- ---------------------
02:00 02:00 AM at night
10:00 10:00 AM in the morning
14:00 02:00 PM in the afternoon
21:00 09:00 PM in the evening
DateTimeFormatterBuilder 的 dayPeriod() 方法
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;
import java.util.Locale;
public class BuilderDayPeriodDemo {
public static void main(String[] args) {
// 编程方式构建带 Day Period 的 formatter
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendText(ChronoField.DAY_OF_WEEK) // 星期几
.appendLiteral(" ")
.appendPattern("B") // Day Period
.appendLiteral(" ")
.appendPattern("HH:mm:ss") // 时间
.toFormatter(Locale.CHINA);
LocalTime[] times = {
LocalTime.of(7, 15, 30),
LocalTime.of(13, 0, 0),
LocalTime.of(19, 45, 0)
};
// 与任意日期组合
var baseDate = java.time.LocalDate.of(2026, 6, 14);
for (LocalTime t : times) {
System.out.println(formatter.format(t.atDate(baseDate)));
}
}
}
输出:
星期日 上午 07:15:30
星期日 下午 13:00:00
星期日 晚上 19:45:00
各 Locale 的 Day Period 表现
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class MultiLocaleDayPeriod {
public static void main(String[] args) {
LocalTime time = LocalTime.of(20, 0);
Locale[] locales = {
Locale.CHINA, // 中文
Locale.US, // 英文(美国)
Locale.UK, // 英文(英国)
Locale.JAPAN, // 日文
Locale.FRANCE // 法文
};
System.out.println("20:00 在各 Locale 下的 Day Period:\n");
for (Locale locale : locales) {
DateTimeFormatter fmt = DateTimeFormatter
.ofPattern("B", locale);
System.out.printf(" %-12s → %s%n",
locale.getDisplayName(),
fmt.format(time));
}
}
}
输出:
20:00 在各 Locale 下的 Day Period:
中文 (中国) → 晚上
英文 (美国) → in the evening
英文 (英国) → in the evening
日文 (日本) → 夜
法文 (法国) → du soir
注意事项
| 要点 | 说明 |
|---|---|
| JDK 版本 | B 符号在 JDK 16 才可用——更早版本使用 B 会抛 IllegalArgumentException |
| 时段边界 | Day Period 的边界由 CLDR 数据定义,不同 locale 可能不同(如某些文化中 evening 从 17:00 开始) |
与 a 共存 | B 和 a 可以在同一个 pattern 中使用,互不冲突 |
| 解析能力 | B 符号同样支持解析(LocalTime.parse("下午 15:00", formatter)) |
总结
JDK 16 的 Day Period 增强是一个小但实用的改进。B 符号和 DateTimeFormatterBuilder.dayPeriod() 让 Java 的日期时间格式化能够表达比 AM/PM 更丰富的时段语义,在需要国际化、日记系统、日程展示等场景下尤为有用。
一句话总结:以往 a 只能区分上午/下午,现在 B 能告诉你这是凌晨、早上、中午、下午还是晚上。