基本概念
概念 | 说明 |
---|---|
UTC | Universal Time Coordinated,通用协调时 |
GMT | Greenwich Mean Time,位于英国伦敦郊区的皇家格林尼治天文台的标准时间,UTC与GMT时间基本相同 |
CST | 中国标准时间,China Standard Time,CST = GMT + 8 = UTC + 8 |
夏令时 | DST,Daylight Saving Time,指在夏天太阳升起的比较早时,将时间拨快一小时 |
冬令时 | 将时间拨慢一小时 |
我国现在没有实行夏令时和冬令时,但是欧洲北美很多还是会实行夏令时和冬令时,亚洲像印度、巴基斯坦也实行夏令时和冬令时。
每个地区可能夏令时和冬令时的规定也不太一样。所以,做国际化的时候一定要注意。
美国:
- 夏令时从每年03月的第2个周日的凌晨2点开始,时钟向前调1小时
- 冬令时从每年11月的第1个周日的凌晨2点开始,时钟向后调1小时
英国:
- 夏令时(British Summer Time,BST)从每年03月的最后1个周日的凌晨1点开始,时钟向前调1小时
- 冬令时(British Winter Time,BWT)从每年10月的最后1个周日的凌晨2点开始,时钟向后调1小时
所以在处理有夏令时和冬令时的地区的时候,不能简单的用offset来计算
例如,美国西5区,中国东8区,中国时间-13小时就是美国时间了,这对于冬令时没有问题,对于夏令时有问题。
再说,美国不像中国统一用1个时区,不管你是在上海、北京、香港、台北用的都是东8区。
美国用多个时区,例如纽约西5区,芝加哥中部时间用的是西6区。
怎么处理这些时间呢?
不要担心,Java提供了ZonedDateTime可以帮我们计算。
几个重要时间类之间的关系
看下面这张图基本就清楚了:
- LocalDate
- LocalTime
- ZoneOffset
- ZoneId
- LocalDateTime = LocalDate + LocalTime
- OffsetDateTime = LocalDate + LocalTime + ZoneOffset
- ZonedDateTime = LocalDate + LocalTime + ZoneOffset + ZoneId
关于时间计算可以参考:Java强大的新日期类
ZonedDateTime
ZonedDateTime默认的时区是:Asia/Shanghai,我们可以看到语言和数据库都使用的这个值。
为啥是上海呢,可能因为上海比较有名吧(江苏常州哭死,它甚至有一个东经120路)
ZonedDateTime有2个方法需要注意:
- withZoneSameInstant(zoneId):获取有相同时间戳,指定时区的时间,用来计算当前的其他时区时间,例如当前是纽约什么时间
- 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 | 0° | 伦敦、都柏林、里斯本、达喀尔 |
东一区 | 7.5°E~22.5°E | 15°E | 华沙、维也纳、罗马、柏林、哥本哈根、法兰克福、苏黎世、日内瓦、阿姆斯特丹、巴黎、巴塞罗那、马德里 |
东二区 | 22.5°E~37.5°E | 30°E | 耶路撒冷、开罗、伊斯坦布尔、基辅、雅典城 |
东三区 | 37.5°E~52.5°E | 45°E | 亚丁、巴格达、莫斯科 |
东四区 | 52.5°E~67.5°E | 60°E | 阿布扎比 |
东五区 | 67.5°E~82.5°E | 75°E | 新德里、伊斯兰堡、卡拉奇 |
东六区 | 82.5°E~97.5°E | 90°E | 仰光、达卡 |
东七区 | 97.5°E~112.5°E | 105°E | 河内、曼谷、雅加达 |
东八区 | 112.5°E~127.5°E | 120°E | 北京、上海、广州、深圳、成都、重庆、西安、昆明、杭州、常州 |
东九区 | 127.5°E~142.5°E | 135°E | 东京、汉城 |
东十区 | 142.5°E~157.5°E | 150°E | 堪培拉、墨尔本、悉尼 |
东十一区 | 157.5°E~172.5°E | 165°E | 阿纳德尔 |
东十二区 | 172.5°E~180° | 180° | 惠灵顿 |
西十二区 | 180°W~172.5°W | 180° | 太平洋、无城市 |
西十一区 | 172.5°W~157.5°W | 165°W | 太平洋、无城市 |
西十区 | 157.5°W~142.5°W | 150°W | 夏威夷檀香山 |
西九区 | 142.5°W~127.5°W | 135°W | 阿拉斯加安克雷奇 |
西八区 | 127.5°W~112.5°W | 120°W | 洛杉矶、旧金山、圣地亚哥、拉斯维加斯、西雅图 |
西六区 | 97.5°W~82.5°W | 90°W | 芝加哥、圣路易斯、新奥尔良、休士顿 |
西五区 | 82.5°W~67.5°W | 75°W | 华盛顿特区、纽约、波士顿、亚特兰大、费城、迈阿密 |
西四区 | 67.5°W~52.5°W | 60°W | 亚松森、圣地亚哥 |
西三区 | 52.5°W~37.5°W | 45°W | 里约热内卢、圣保罗、巴西利亚 |
西二区 | 37.5°W~22.5°W | 30°W | 格林兰岛 |
西一区 | 22.5°W~7.5°W | 15°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 | 东加塔布 |