Java国际化时间-理解时区、ZonedDateTime、OffsetDateTime

基本概念

概念说明
UTCUniversal Time Coordinated,通用协调时
GMTGreenwich Mean Time,位于英国伦敦郊区的皇家格林尼治天文台的标准时间,UTC与GMT时间基本相同
CST中国标准时间,China Standard Time,CST = GMT + 8 = UTC + 8
夏令时DST,Daylight Saving Time,指在夏天太阳升起的比较早时,将时间拨快一小时
冬令时将时间拨慢一小时

我国现在没有实行夏令时和冬令时,但是欧洲北美很多还是会实行夏令时和冬令时,亚洲像印度、巴基斯坦也实行夏令时和冬令时。

每个地区可能夏令时和冬令时的规定也不太一样。所以,做国际化的时候一定要注意。

美国:

  1. 夏令时从每年03月的第2个周日的凌晨2点开始,时钟向前调1小时
  2. 冬令时从每年11月的第1个周日的凌晨2点开始,时钟向后调1小时

英国:

  1. 夏令时(British Summer Time,BST)从每年03月的最后1个周日的凌晨1点开始,时钟向前调1小时
  2. 冬令时(British Winter Time,BWT)从每年10月的最后1个周日的凌晨2点开始,时钟向后调1小时

所以在处理有夏令时和冬令时的地区的时候,不能简单的用offset来计算

例如,美国西5区,中国东8区,中国时间-13小时就是美国时间了,这对于冬令时没有问题,对于夏令时有问题。

再说,美国不像中国统一用1个时区,不管你是在上海、北京、香港、台北用的都是东8区。

美国用多个时区,例如纽约西5区,芝加哥中部时间用的是西6区。

怎么处理这些时间呢?

不要担心,Java提供了ZonedDateTime可以帮我们计算。

几个重要时间类之间的关系

看下面这张图基本就清楚了:

Java ZonedDateTime

  1. LocalDate
  2. LocalTime
  3. ZoneOffset
  4. ZoneId
  5. LocalDateTime = LocalDate + LocalTime
  6. OffsetDateTime = LocalDate + LocalTime + ZoneOffset
  7. ZonedDateTime = LocalDate + LocalTime + ZoneOffset + ZoneId

关于时间计算可以参考:Java强大的新日期类

ZonedDateTime

ZonedDateTime默认的时区是:Asia/Shanghai,我们可以看到语言和数据库都使用的这个值。

为啥是上海呢,可能因为上海比较有名吧(江苏常州哭死,它甚至有一个东经120路)

ZonedDateTime有2个方法需要注意:

  1. withZoneSameInstant(zoneId):获取有相同时间戳,指定时区的时间,用来计算当前的其他时区时间,例如当前是纽约什么时间
  2. withZoneSameLocal(zoneId):获取有相同本地时间对应的时区时间(时间戳不同)
@Test
public void testZonedDateTime() {
    // 夏令时
    ZonedDateTime sumTime = ZonedDateTime.now().withMonth(4);
    // 冬令时
    ZonedDateTime winTime = ZonedDateTime.now().withMonth(12);

    ZoneId anZoneId = ZoneId.of("America/New_York");
//        ZoneId anZoneId = ZoneId.of("America/Chicago");
    ZoneId elZoneId = ZoneId.of("Europe/London");
    printDiff(sumTime,anZoneId);
    System.out.println("---------");
    printDiff(winTime,anZoneId);
    System.out.println("---------");
    printDiff(sumTime,elZoneId);
    System.out.println("---------");
    printDiff(winTime,elZoneId);
}

private void printDiff(ZonedDateTime zonedDateTime,ZoneId zoneId){
    System.out.printf("%-53s %d\n",zonedDateTime,zonedDateTime.toEpochSecond());
    ZonedDateTime sameInstant = zonedDateTime.withZoneSameInstant(zoneId);
    System.out.printf("%-53s %d\n",sameInstant,sameInstant.toEpochSecond());
    ZonedDateTime sameLocal = zonedDateTime.withZoneSameLocal(zoneId);
    System.out.printf("%-53s %d\n",sameLocal,sameLocal.toEpochSecond());
}

输出结果:

2024-04-26T14:42:54.402940+08:00[Asia/Shanghai]       1714113774
2024-04-26T02:42:54.402940-04:00[America/New_York]    1714113774
2024-04-26T14:42:54.402940-04:00[America/New_York]    1714156974
---------
2024-12-26T14:42:54.402940+08:00[Asia/Shanghai]       1735195374
2024-12-26T01:42:54.402940-05:00[America/New_York]    1735195374
2024-12-26T14:42:54.402940-05:00[America/New_York]    1735242174
---------
2024-04-26T14:42:54.402940+08:00[Asia/Shanghai]       1714113774
2024-04-26T07:42:54.402940+01:00[Europe/London]       1714113774
2024-04-26T14:42:54.402940+01:00[Europe/London]       1714138974
---------
2024-12-26T14:42:54.402940+08:00[Asia/Shanghai]       1735195374
2024-12-26T06:42:54.402940Z[Europe/London]            1735195374
2024-12-26T14:42:54.402940Z[Europe/London]            1735224174

OffsetDateTime

OffsetDateTime和PostgreSQL中的timestamptz类型比较像。

@Test
public void offsetDateTime() {
    OffsetDateTime offsetDateTime = OffsetDateTime.now();
    System.out.println("默认时区偏移时间:" + offsetDateTime);
    OffsetDateTime zoneOffsetTime = OffsetDateTime.of(LocalDateTime.now(),
            ZoneOffset.of("-4"));
    System.out.println("指定偏移量偏移时间:" + zoneOffsetTime);
    OffsetDateTime specific = OffsetDateTime.now(ZoneId.of("America/New_York"));
    System.out.println("指定时区偏移时间:" + specific);
}

格式化与解析:

@Test
public void offsetDateTimeFormat() {
    OffsetDateTime now = OffsetDateTime.now(ZoneId.systemDefault());
    System.out.println("格式化:" +
            DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT, FormatStyle.SHORT)
                    .format(now));

    String dateTimeStrParam = "2050-01-10T18:00:00+08:00";
    System.out.println("解析结果:" + OffsetDateTime.parse(dateTimeStrParam));
}

与LocalDateTime转换:

@Test
public void local2OffsetDateTime() {
    LocalDateTime localDateTime = LocalDateTime.now();
    System.out.println("本地时间:" + localDateTime);
    System.out.println("本地时间偏移4小时:" + OffsetDateTime.of(localDateTime, ZoneOffset.ofHours(-4)));
    System.out.println("本地时间偏移4小时:" + localDateTime.atOffset(ZoneOffset.ofHours(-4)));
    ZoneOffset offset = ZoneOffset.ofHours(-4);
    Instant instant = localDateTime.toInstant(offset);
    OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(instant, ZoneOffset.ofHours(-4));
    System.out.println("本地时间偏移4小时:" + offsetDateTime);
    System.out.println("转换为本地时间:" + offsetDateTime.toLocalDateTime());
}

时区、经度与城市

时区时区经度区间时区经度城市
零时区7.5°W~7.5°E伦敦、都柏林、里斯本、达喀尔
东一区7.5°E~22.5°E15°E华沙、维也纳、罗马、柏林、哥本哈根、法兰克福、苏黎世、日内瓦、阿姆斯特丹、巴黎、巴塞罗那、马德里
东二区22.5°E~37.5°E30°E耶路撒冷、开罗、伊斯坦布尔、基辅、雅典城
东三区37.5°E~52.5°E45°E亚丁、巴格达、莫斯科
东四区52.5°E~67.5°E60°E阿布扎比
东五区67.5°E~82.5°E75°E新德里、伊斯兰堡、卡拉奇
东六区82.5°E~97.5°E90°E仰光、达卡
东七区97.5°E~112.5°E105°E河内、曼谷、雅加达
东八区112.5°E~127.5°E120°E北京、上海、广州、深圳、成都、重庆、西安、昆明、杭州、常州
东九区127.5°E~142.5°E135°E东京、汉城
东十区142.5°E~157.5°E150°E堪培拉、墨尔本、悉尼
东十一区157.5°E~172.5°E165°E阿纳德尔
东十二区172.5°E~180°180°惠灵顿
西十二区180°W~172.5°W180°太平洋、无城市
西十一区172.5°W~157.5°W165°W太平洋、无城市
西十区157.5°W~142.5°W150°W夏威夷檀香山
西九区142.5°W~127.5°W135°W阿拉斯加安克雷奇
西八区127.5°W~112.5°W120°W洛杉矶、旧金山、圣地亚哥、拉斯维加斯、西雅图
西六区97.5°W~82.5°W90°W芝加哥、圣路易斯、新奥尔良、休士顿
西五区82.5°W~67.5°W75°W华盛顿特区、纽约、波士顿、亚特兰大、费城、迈阿密
西四区67.5°W~52.5°W60°W亚松森、圣地亚哥
西三区52.5°W~37.5°W45°W里约热内卢、圣保罗、巴西利亚
西二区37.5°W~22.5°W30°W格林兰岛
西一区22.5°W~7.5°W15°W里斯本

Java支持时区

Java可以使用下面的代码来查看支持的时区:

@Test
public void allZone(){
    Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
    for(String id : availableZoneIds){
        System.out.println(id);
    }
}

时区对应的地区:

时区说明
Asia/Shanghai中国标准时间(北京)
Asia/Hong_Kong香港时间(香港)
Asia/Taipei台北时间(台北)
Asia/Seoul首尔
Asia/Tokyo日本时间(东京)
America/New_York美国东部时间(纽约)
America/Denver美国山区时间(丹佛)
America/Costa_Rica美国中部时间(哥斯达黎加)
America/Chicago美国中部时间(芝加哥)
America/Mexico_City美国中部时间(墨西哥城)
America/Regina美国中部时间(里贾纳)
America/Los_Angeles美国太平洋时间(洛杉矶)
Pacific/Majuro马朱罗
Pacific/Midway中途岛
Pacific/Honolulu檀香山
America/Anchorage安克雷奇
America/Tijuana美国太平洋时间(提华纳)
America/Phoenix美国山区时间(凤凰城)
America/Chihuahua奇瓦瓦
America/Bogota哥伦比亚时间(波哥大)
America/Caracas委内瑞拉时间(加拉加斯)
America/Barbados大西洋时间(巴巴多斯)
America/Manaus亚马逊标准时间(马瑙斯)
America/St_Johns纽芬兰时间(圣约翰)
America/Santiago圣地亚哥
America/Argentina/Buenos_Aires布宜诺斯艾利斯
America/Godthab戈特霍布
America/Montevideo乌拉圭时间(蒙得维的亚)
America/Sao_Paulo圣保罗
Atlantic/South_Georgia南乔治亚
Atlantic/Azores亚述尔群岛
Atlantic/Cape_Verde佛得角
Africa/Casablanca卡萨布兰卡
Europe/London格林尼治标准时间(伦敦)
Europe/Amsterdam中欧标准时间(阿姆斯特丹)
Europe/Belgrade中欧标准时间(贝尔格莱德)
Europe/Brussels中欧标准时间(布鲁塞尔)
Europe/Sarajevo中欧标准时间(萨拉热窝)
Africa/Brazzaville西部非洲标准时间(布拉扎维)
Africa/Windhoek温得和克
Asia/Amman东欧标准时间(安曼)
Europe/Athens东欧标准时间(雅典)
Asia/Beirut东欧标准时间(贝鲁特)
Africa/Cairo东欧标准时间(开罗)
Europe/Helsinki东欧标准时间(赫尔辛基)
Asia/Jerusalem以色列时间(耶路撒冷)
Africa/Harare中部非洲标准时间(哈拉雷)
Europe/Minsk明斯克
Asia/Baghdad巴格达
Europe/Moscow莫斯科
Asia/Kuwait科威特
Africa/Nairobi东部非洲标准时间(内罗毕)
Asia/Tehran伊朗标准时间(德黑兰)
Asia/Baku巴库
Asia/Tbilisi第比利斯
Asia/Yerevan埃里温
Asia/Dubai迪拜
Asia/Kabul阿富汗时间(喀布尔)
Asia/Karachi卡拉奇
Asia/Oral乌拉尔
Asia/Yekaterinburg叶卡捷林堡
Asia/Calcutta加尔各答
Asia/Colombo科伦坡
Asia/Katmandu尼泊尔时间(加德满都)
Asia/Almaty阿拉木图
Asia/Rangoon缅甸时间(仰光)
Asia/Krasnoyarsk克拉斯诺亚尔斯克
Asia/Bangkok曼谷
Asia/Irkutsk伊尔库茨克时间(伊尔库茨克)
Asia/Kuala_Lumpur吉隆坡
Australia/Perth佩思
Asia/Yakutsk雅库茨克时间(雅库茨克)
Australia/Darwin达尔文
Australia/Brisbane布里斯班
Asia/Vladivostok海参崴时间(符拉迪沃斯托克)
Pacific/Guam关岛
Australia/Adelaide阿德莱德
Australia/Hobart霍巴特
Australia/Sydney悉尼
Asia/Magadan马加丹时间(马加丹)
Pacific/Auckland奥克兰
Pacific/Fiji斐济
Pacific/Tongatapu东加塔布
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值