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
}

代码解析:

  1. 通过 LocalDateTime.now() 获取当前时间作为参考点
  2. 使用 Stream API 处理时区 ID 集合:
    • 将字符串 ID 转为 ZoneId 实例
    • ZoneComparator 排序
    • 格式化为 (基准+偏移量) 时区ID 的字符串

3. 计算偏移量

需要获取每个时区的 UTC 偏移量(如欧洲中部时间为 +01:00)。直接使用 LocalDateTimegetOffset() 方法即可

注意 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 查看。


原始标题:Display All Time Zones With GMT and UTC in Java