补充@Ole V.V.的答案,我发现使用ThreeTenABP还有另一种方法可以实现,但不需要使用正则表达式替换(虽然我认为它并不比较简单,下面会说到)。
使用DateTimeFormatterBuilder
,您可以使用一个将偏移量的值映射到String
的HashMap
。因此,对于整个偏移小时(例如+03:00
),您可以将相应的值映射到字符串+3
等。
唯一的问题是,
ZoneOffset
只有
秒精度。最小和最大值分别为
UTC-18:00:00
和
UTC+18:00:00
。因此,所有可能的值都是
UTC-18:00:00
、
UTC-17:59:59
、
UTC-17:59:58
等等。并且格式化程序要求将所有可能的值映射(否则它会显示偏移量的秒值),因此该映射将有超过 120K 个条目!
为构建这个映射,我进行了两个循环:
- 第一个循环将整个小时的偏移量映射到相应的数字(例如,将
+01:00
映射到 +1
,将 -02:00
映射到 -2
,以此类推)
- 第二个循环映射所有其他值(它们保持不变):
- 大于或等于 10 的整数小时(如
+10:00
)
- 非整数小时(如
+05:30
)
创建格式化程序的代码如下:
Map<Long, String> map = new HashMap<>();
for (int i = 1; i <= 9; i++) {
long seconds = ZoneOffset.ofHours(i).getTotalSeconds();
map.put(seconds, "+" + i);
map.put(-seconds, "-" + i);
}
int minOffset = ZoneOffset.MIN.getTotalSeconds();
int maxOffset = ZoneOffset.MAX.getTotalSeconds();
for (long i = minOffset; i <= maxOffset; i++) {
if (!map.containsKey(i)) {
map.put(i, ZoneOffset.ofTotalSeconds((int) i).getId());
}
}
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("uuuu/MM/dd | ")
.appendText(ChronoField.OFFSET_SECONDS, map)
.toFormatter();
一些测试:
LocalDateTime dt = LocalDateTime.of(2017, 5, 1, 10, 0);
ZonedDateTime z = ZonedDateTime.of(dt, ZoneId.of("America/Sao_Paulo"));
System.out.println(formatter.format(z));
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Qatar"));
System.out.println(formatter.format(z));
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Vladivostok"));
System.out.println(formatter.format(z));
z = ZonedDateTime.of(dt, ZoneId.of("Asia/Kathmandu"));
System.out.println(formatter.format(z));
输出结果是:
2017年5月1日 | -3
2017年5月1日 | +3
2017年5月1日 | +10:00
2017年5月1日 | +05:45
注意:
- 我不知道使用一个包含120K条目的映射表是否比使用正则表达式更好(这取决于您的决定)。这种方法会创建一个大型映射表,但至少不需要输出后处理(不确定这是否是一个合理的权衡)。
- 如果您想要显示整小时偏移量 >= 10 为
+10
、+11
等,请将第一个 for
循环更改为 for (int i = 1; i <= maxOffsetYouWant; i++)
- 只是提醒 maxOffsetYouWant
的最大可能值为18。
+03:00
而不是+3
。这似乎是规范化的格式。我已经看到多个库和协议只接受该格式。此外,该格式对人类读者更易识别。 - Basil Bourque±HH:MM
的完整格式?除了我在上面的评论中提供的实用建议之外,使用完整格式的另一个原因是这是java.time中ZoneOffset
类使用的默认格式。 - Basil Bourque