1. 概述
处理日期时间时,我们总需要一个参考基准。国际标准是UTC,但有些应用也会使用GMT。
简单来说:
- UTC 是时间标准
- GMT 是时区名称
维基百科给出了明确建议:
在大多数场景下,UTC 可与格林威治标准时间(GMT)互换使用,但 GMT 已不被科学界精确定义。
这意味着一旦获取了 UTC 偏移列表,GMT 也就自然包含在内了。接下来我们先看 Java 8 的实现方式,再看看 Java 7 版本。
2. 获取时区列表
首先需要获取所有已定义的时区 ID。ZoneId
类提供了静态方法:
Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
然后生成带偏移量的有序列表:
public List<String> getTimeZoneList(OffsetBase base) {
LocalDateTime now = LocalDateTime.now();
return ZoneId.getAvailableZoneIds().stream()
.map(ZoneId::of)
.sorted(new ZoneComparator())
.map(id -> String.format(
"(%s%s) %s",
base, getOffset(now, id), id.getId()))
.collect(Collectors.toList());
}
这里用枚举参数指定偏移基准:
public enum OffsetBase {
GMT, UTC
}
代码解析:
- 通过
LocalDateTime.now()
获取当前时间作为参考点 - 使用 Stream API 处理时区 ID 集合:
- 将字符串 ID 转为
ZoneId
实例 - 按
ZoneComparator
排序 - 格式化为
(基准+偏移量) 时区ID
的字符串
- 将字符串 ID 转为
3. 计算偏移量
需要获取每个时区的 UTC 偏移量(如欧洲中部时间为 +01:00
)。直接使用 LocalDateTime
的 getOffset()
方法即可。
注意 Java 将 +00:00
显示为 Z
,我们统一替换为 +00:00
:
private String getOffset(LocalDateTime dateTime, ZoneId id) {
return dateTime
.atZone(id)
.getOffset()
.getId()
.replace("Z", "+00:00");
}
4. 实现时区排序
可选步骤:按偏移量排序时区。实现 Comparator
接口:
private class ZoneComparator implements Comparator<ZoneId> {
@Override
public int compare(ZoneId zoneId1, ZoneId zoneId2) {
LocalDateTime now = LocalDateTime.now();
ZoneOffset offset1 = now.atZone(zoneId1).getOffset();
ZoneOffset offset2 = now.atZone(zoneId2).getOffset();
return offset1.compareTo(offset2);
}
}
5. 展示时区列表
最后调用 getTimeZoneList()
生成两种基准的列表:
public class TimezoneDisplayApp {
public static void main(String... args) {
TimezoneDisplay display = new TimezoneDisplay();
System.out.println("Time zones in UTC:");
List<String> utc = display.getTimeZoneList(
TimezoneDisplay.OffsetBase.UTC);
utc.forEach(System.out::println);
System.out.println("Time zones in GMT:");
List<String> gmt = display.getTimeZoneList(
TimezoneDisplay.OffsetBase.GMT);
gmt.forEach(System.out::println);
}
}
输出示例:
Time zones in UTC:
(UTC+14:00) Pacific/Apia
(UTC+14:00) Pacific/Kiritimati
(UTC+14:00) Pacific/Tongatapu
(UTC+14:00) Etc/GMT-14
6. Java 7 及更早版本实现
Java 8 的 Stream 和日期时间 API 让实现更简单。在 Java 7 中,我们使用 java.util.TimeZone
:
public List<String> getTimeZoneList(OffsetBase base) {
String[] availableZoneIds = TimeZone.getAvailableIDs();
List<String> result = new ArrayList<>(availableZoneIds.length);
for (String zoneId : availableZoneIds) {
TimeZone curTimeZone = TimeZone.getTimeZone(zoneId);
String offset = calculateOffset(curTimeZone.getRawOffset());
result.add(String.format("(%s%s) %s", base, offset, zoneId));
}
Collections.sort(result);
return result;
}
关键区别在于偏移量计算:
getRawOffset()
返回毫秒级偏移量- 需要转换为
HH:mm
格式:
private String calculateOffset(int rawOffset) {
if (rawOffset == 0) {
return "+00:00";
}
long hours = TimeUnit.MILLISECONDS.toHours(rawOffset);
long minutes = TimeUnit.MILLISECONDS.toMinutes(rawOffset);
minutes = Math.abs(minutes - TimeUnit.HOURS.toMinutes(hours));
return String.format("%+03d:%02d", hours, Math.abs(minutes));
}
7. 总结
本文展示了如何获取所有时区的 UTC/GMT 偏移量列表。关键点包括:
✅ 使用 ZoneId.getAvailableZoneIds()
获取时区
✅ 通过 atZone()
和 getOffset()
计算偏移量
✅ Java 7 需要手动转换毫秒偏移量
⚠️ 注意 Java 8 中 +00:00
显示为 Z
的特殊情况
完整代码示例可在 GitHub 查看。