Java 21中DateFormat::format中英文输出不一致的测试适配问题
Java 21迁移中DateFormat格式字符串比较问题解决
问题背景
将遗留代码从Java 17迁移到Java 21后,发现DateFormat::format方法输出的英文时间字符串(带AM/PM格式)中,空格为非LATIN-1编码字符,导致项目中大量与硬编码预期值的equals断言失败;而德语等非英语语言的输出则无此问题。
复现代码
import java.text.DateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import static java.text.DateFormat.SHORT; public class Main { public static void main(String[] args) { DateFormat endf = DateFormat.getTimeInstance(SHORT, Locale.ENGLISH); endf.setTimeZone(TimeZone.getTimeZone("Europe/London")); DateFormat gedf = DateFormat.getTimeInstance(SHORT, Locale.GERMAN); gedf.setTimeZone(TimeZone.getTimeZone("Europe/Berlin")); var enDate = endf.format(new Date(1727163143068L)); var geDate = gedf.format(new Date(1727163143068L)); assert(enDate.equals("8:32 AM")); assert(geDate.equals("09:32")); System.out.println(enDate); // 输出: 8:32 AM System.out.println(geDate); // 输出: 09:32 } }
解决方案
1. 标准化空白字符
将格式化后的字符串中的非标准空白字符替换为普通空格,再与预期值比较:
// 替换所有类型的空白字符为普通空格 assert(enDate.replaceAll("\\s", " ").equals("8:32 AM"));
或精准替换常见的非标准空格(如不换行空格U+00A0、窄空格U+202F):
assert(enDate.replaceAll("[\\u00A0\\u202F]", " ").equals("8:32 AM"));
2. 切换到现代日期时间API(推荐)
DateFormat属于旧API,Java 8及以后推荐使用java.time包下的类,其格式化输出更稳定,且从根源避免编码类问题:
import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Locale; public class Main { public static void main(String[] args) { var dateTime = LocalDateTime.ofEpochSecond(1727163143, 68, java.time.ZoneOffset.UTC); DateTimeFormatter enFormatter = DateTimeFormatter.ofPattern("h:mm a", Locale.ENGLISH) .withZone(ZoneId.of("Europe/London")); String enDate = enFormatter.format(dateTime); DateTimeFormatter deFormatter = DateTimeFormatter.ofPattern("HH:mm", Locale.GERMAN) .withZone(ZoneId.of("Europe/Berlin")); String geDate = deFormatter.format(dateTime); assert(enDate.equals("8:32 AM")); assert(geDate.equals("09:32")); } }
该API还解决了旧API的线程不安全问题,更适配现代Java开发规范。
3. 语义化比较(最严谨)
跳过字符串格式,将格式化后的字符串重新解析为日期对象,通过时间戳比较语义:
// 解析英文时间字符串回Date对象,比较时间戳 Date parsedEnDate = endf.parse(enDate); assert(parsedEnDate.getTime() == 1727163143068L); // 德语时间同理 Date parsedGeDate = gedf.parse(geDate); assert(parsedGeDate.getTime() == 1727163143068L);
此方法完全不依赖字符串的具体格式,仅校验时间的实际语义,是最可靠的比较方式。
内容的提问来源于stack exchange,提问作者yale
相关产品推荐
相关产品推荐

