Java日期时间API系列19

Wesley13
• 阅读 728

  通过Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类中时间范围示意图:可以很清晰的看出ZonedDateTime相当于LocalDateTime+ZoneId。

  Java日期时间API系列19

  ZonedDateTime是用来处理时区相关的时间,它的各种计算都离不开ZoneId。先看ZoneId。

1. ZoneId 为时区ID,比如Europe/Paris,表示欧洲巴黎时区

1.1 时区相关知识,时区,UTC时间,GMT时间,Unix时间戳

时区

地球自西向东旋转,东边比西边先看到太阳,东边的时间也比西边的早。为了统一世界的时间,1884年的国际经度会议规规定将全球划分为24个时区(东、西各12个时区)。规定英国(格林尼治天文台旧址)为零时区(GMT+00),东1-12区,西1-12区,中国北京处于东8区(GMT+08)。

若英国时间为6点整,则GMT时间为6点整,则北京时间为14点整。

GMT和UTC

GMT,即格林尼治标准时间,也就是世界时。GMT的正午是指当太阳横穿格林尼治子午线(本初子午线)时的时间。但由于地球自转不均匀不规则,导致GMT不精确,现在已经不再作为世界标准时间使用。

UTC,即协调世界时。UTC是以原子时秒长为基础,在时刻上尽量接近于GMT的一种时间计量系统。为确保UTC与GMT相差不会超过0.9秒,在有需要的情况下会在UTC内加上正或负闰秒。UTC现在作为世界标准时间使用。

所以,UTC与GMT基本上等同,误差不超过0.9秒。

UNIX时间戳

计算机中的UNIX时间戳,是以GMT/UTC时间「1970-01-01T00:00:00」为起点,到具体时间的秒数,不考虑闰秒。这么做当然是为了简化计算机对时间操作的复杂度。

比如我的电脑现在的系统时间为2015年2月27日15点43分0秒,因为我的电脑默认时区为东8区,则0时区的时间为2015年2月27日7点43分0秒,则UNIX时间戳为1425022980秒。

1.2  常用时区名称和缩写如下:

package com.xkzhangsan.time.enums; /** * 常用时区枚举 包含中文名称,比如:"Asia/Shanghai","亚洲/上海" * * @ClassName: ZoneIdEnum * @Description: ZoneIdEnum * @author xkzhangsan * @date 2020年02月18日 * @version 0.1 ,初版,试用 */ public enum ZoneIdEnum { /** * "Australia/Darwin","澳洲/达尔文" */ ACT("Australia/Darwin", "澳洲/达尔文"), /** * "Australia/Sydney","澳洲/悉尼" */ AET("Australia/Sydney", "澳洲/悉尼"), /** * "America/Argentina/Buenos_Aires","美洲/阿根廷/布宜诺斯艾利斯" */ AGT("America/Argentina/Buenos_Aires", "美洲/阿根廷/布宜诺斯艾利斯"), /** * "Africa/Cairo","非洲/开罗" */ ART("Africa/Cairo", "非洲/开罗"), /** * "America/Anchorage","美洲/安克雷奇" */ AST("America/Anchorage", "美洲/安克雷奇"), /** * "America/Sao_Paulo","美洲/圣保罗" */ BET("America/Sao_Paulo", "美洲/圣保罗"), /** * "Asia/Dhaka","亚洲/达卡" */ BST("Asia/Dhaka", "亚洲/达卡"), /** * "Africa/Harare","非洲/哈拉雷" */ CAT("Africa/Harare", "非洲/哈拉雷"), /** * "America/St_Johns","美洲/圣约翰" */ CNT("America/St_Johns", "美洲/圣约翰"), /** * "America/Chicago","美洲/芝加哥" */ CST("America/Chicago", "美洲/芝加哥"), /** * "Asia/Shanghai","亚洲/上海" */ CTT("Asia/Shanghai", "亚洲/上海"), /** * "Africa/Addis_Ababa","非洲/亚的斯亚贝巴" */ EAT("Africa/Addis_Ababa", "非洲/亚的斯亚贝巴"), /** * "Europe/Paris","欧洲/巴黎" */ ECT("Europe/Paris", "欧洲/巴黎"), /** * "America/Indiana/Indianapolis","美洲/印第安纳州/印第安纳波利斯" */ IET("America/Indiana/Indianapolis", "美洲/印第安纳州/印第安纳波利斯"), /** * "Asia/Kolkata","亚洲/加尔各答" */ IST("Asia/Kolkata", "亚洲/加尔各答"), /** * "Asia/Tokyo","亚洲/东京" */ JST("Asia/Tokyo", "亚洲/东京"), /** * "Pacific/Apia","太平洋/阿皮亚" */ MIT("Pacific/Apia", "太平洋/阿皮亚"), /** * "Asia/Yerevan","亚洲/埃里温" */ NET("Asia/Yerevan", "亚洲/埃里温"), /** * "Pacific/Auckland","太平洋/奥克兰" */ NST("Pacific/Auckland", "太平洋/奥克兰"), /** * "Asia/Karachi","亚洲/卡拉奇" */ PLT("Asia/Karachi", "亚洲/卡拉奇"), /** * "America/Phoenix","美洲/凤凰城" */ PNT("America/Phoenix", "美洲/凤凰城"), /** * "America/Puerto_Rico","美洲/波多黎各" */ PRT("America/Puerto_Rico", "美洲/波多黎各"), /** * "America/Los_Angeles","美洲/洛杉矶" */ PST("America/Los_Angeles", "美洲/洛杉矶"), /** * "Pacific/Guadalcanal","太平洋/瓜达尔卡纳尔岛" */ SST("Pacific/Guadalcanal", "太平洋/瓜达尔卡纳尔岛"), /** * "Asia/Ho_Chi_Minh","亚洲/胡志明市" */ VST("Asia/Ho_Chi_Minh", "亚洲/胡志明市"), /** * "-05:00","东部标准时间"(纽约、华盛顿) */ EST("-05:00", "东部标准时间"), /** * "-07:00","山地标准时间" */ MST("-07:00", "山地标准时间"), /** * "-10:00","夏威夷-阿留申标准时区" */ HST("-10:00", "夏威夷-阿留申标准时区"),; private final String zoneIdName; private final String zoneIdNameCn; public String getZoneIdName() { return zoneIdName; } public String getZoneIdNameCn() { return zoneIdNameCn; } private ZoneIdEnum(String zoneIdName, String zoneIdNameCn) { this.zoneIdName = zoneIdName; this.zoneIdNameCn = zoneIdNameCn; } }

1.3  更多时区id

  可以通过 java.time.ZoneId.getAvailableZoneIds()获取到。

2. ZonedDateTime,ISO-8601日历系统中带有时区的日期时间,例如:2007-12-03T10:15:30+01:00 Europe/Paris

2.1 创建ZonedDateTime

ZonedDateTime.now();

    ZonedDateTime.now(ZoneId.systemDefault());
    
    ZonedDateTime.of(LocalDateTime.now(), ZoneId.systemDefault());

2.2 ZonedDateTime与其他时间类的转换

/** * 注意时间对应的时区和默认时区差异 * @param zonedDateTime * @return */ public static Date toDate(ZonedDateTime zonedDateTime) { Objects.requireNonNull(zonedDateTime, "zonedDateTime"); return Date.from(zonedDateTime.toInstant()); } /** * 注意时间对应的时区和默认时区差异 * @param zonedDateTime * @return */ public static LocalDateTime toLocalDateTime(ZonedDateTime zonedDateTime) { Objects.requireNonNull(zonedDateTime, "zonedDateTime"); return zonedDateTime.toLocalDateTime(); } /** * 注意时间对应的时区和默认时区差异 * @param zonedDateTime * @return */ public static LocalDate toLocalDate(ZonedDateTime zonedDateTime) { Objects.requireNonNull(zonedDateTime, "zonedDateTime"); return zonedDateTime.toLocalDate(); } /** * 注意时间对应的时区和默认时区差异 * @param zonedDateTime * @return */ public static LocalTime toLocalTime(ZonedDateTime zonedDateTime) { Objects.requireNonNull(zonedDateTime, "zonedDateTime"); return zonedDateTime.toLocalTime(); } /** * 注意时间对应的时区和默认时区差异 * @param zonedDateTime * @return */ public static Instant toInstant(ZonedDateTime zonedDateTime) { Objects.requireNonNull(zonedDateTime, "zonedDateTime"); return zonedDateTime.toInstant(); } /** * 转换为ZonedDateTime,时区为系统默认时区 * @param date * @return */ public static ZonedDateTime toZonedDateTime(Date date) { Objects.requireNonNull(date, "date"); return Instant.ofEpochMilli(date.getTime()).atZone(ZoneId.systemDefault()).toLocalDateTime() .atZone(ZoneId.systemDefault()); } /** * 转换为ZonedDateTime,时区为系统默认时区 * @param localDateTime * @return */ public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime) { Objects.requireNonNull(localDateTime, "localDateTime"); return localDateTime.atZone(ZoneId.systemDefault()); }

/\*\*
 \* LocalDateTime转ZonedDateTime,时区为zoneId对应时区
 \* 注意,需要保证localDateTime和zoneId是对应的,不然会出现错误
 \* 
 \* @param localDateTime
 \* @param zoneId
 \* @return
 \*/
public static ZonedDateTime toZonedDateTime(LocalDateTime localDateTime, String zoneId) {
    Objects.requireNonNull(localDateTime, "localDateTime");
    Objects.requireNonNull(zoneId, "zoneId");
    return localDateTime.atZone(ZoneId.of(zoneId));
} /\*\* \* 转换为ZonedDateTime,时区为系统默认时区
 \* @param localDate
 \* @return
 \*/
public static ZonedDateTime toZonedDateTime(LocalDate localDate) {
    Objects.requireNonNull(localDate, "localDate"); return localDate.atStartOfDay().atZone(ZoneId.systemDefault());
} /\*\* \* 以当天的日期+LocalTime组成新的ZonedDateTime,时区为系统默认时区
 \* @param localTime
 \* @return
 \*/
public static ZonedDateTime toZonedDateTime(LocalTime localTime) {
    Objects.requireNonNull(localTime, "localTime"); return LocalDate.now().atTime(localTime).atZone(ZoneId.systemDefault());
} /\*\* \* 转换为ZonedDateTime,时区为系统默认时区
 \* @param instant
 \* @return
 \*/
public static ZonedDateTime toZonedDateTime(Instant instant) { return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).atZone(ZoneId.systemDefault());
}

===================================================================================

测试代码:

@Test public void zonedDateTimeConverterTest(){ System.out.println("===================zonedDateTimeConverterTest====================="); System.out.println("===================ToOther====================="); ZonedDateTime zonedDateTime = ZonedDateTime.now(); System.out.println(zonedDateTime); System.out.println(DateTimeConverterUtil.toDate(zonedDateTime)); System.out.println(DateTimeConverterUtil.toLocalDateTime(zonedDateTime)); System.out.println(DateTimeConverterUtil.toLocalDate(zonedDateTime)); System.out.println(DateTimeConverterUtil.toLocalTime(zonedDateTime)); System.out.println(DateTimeConverterUtil.toInstant(zonedDateTime)); System.out.println("===================toZonedDateTime====================="); System.out.println(zonedDateTime); System.out.println(DateTimeConverterUtil.toZonedDateTime(new Date())); System.out.println(DateTimeConverterUtil.toZonedDateTime(LocalDateTime.now())); System.out.println(DateTimeConverterUtil.toZonedDateTime(LocalDate.now())); System.out.println(DateTimeConverterUtil.toZonedDateTime(LocalTime.now())); System.out.println(DateTimeConverterUtil.toZonedDateTime(Instant.now())); }

输出:

===================zonedDateTimeConverterTest===================== ===================ToOther===================== 2020-02-19T13:33:03.130+08:00[Asia/Shanghai] Wed Feb 19 13:33:03 CST 2020 2020-02-19T13:33:03.130 2020-02-19 13:33:03.130 2020-02-19T05:33:03.130Z ===================toZonedDateTime===================== 2020-02-19T13:33:03.130+08:00[Asia/Shanghai] 2020-02-19T13:33:03.150+08:00[Asia/Shanghai] 2020-02-19T13:33:03.150+08:00[Asia/Shanghai] 2020-02-19T00:00+08:00[Asia/Shanghai] 2020-02-19T13:33:03.150+08:00[Asia/Shanghai] 2020-02-19T13:33:03.150+08:00[Asia/Shanghai]

由于  _public static ZonedDateTime toZonedDateTime(LocalDate localDate),_LocalDate只包含日期,所以,转换后显示为:__2020-02-19T00:00+08:00[Asia/Shanghai]

2.3 常用时区时间创建和时区转换计算

常用时间,如北京时间,巴黎时间,纽约时间,东京时间等。

/** * 获取当前系统当前时区时间 * @param zoneId * @return */ public static ZonedDateTime getZonedDateTimeNowOfDefault(){ return ZonedDateTime.now(ZoneId.systemDefault()); } /** * 获取当前上海时区时间(北京时间) * @param zoneId * @return */ public static ZonedDateTime getZonedDateTimeNowOfShanghai(){ return ZonedDateTime.now(ZoneId.of(ZoneIdEnum.CTT.getZoneIdName())); } /** * 获取当前巴黎时区时间 * @param zoneId * @return */ public static ZonedDateTime getZonedDateTimeNowOfParis(){ return ZonedDateTime.now(ZoneId.of(ZoneIdEnum.ECT.getZoneIdName())); } /** * 获取当前美国东部标准时区(纽约、华盛顿) * @param zoneId * @return */ public static ZonedDateTime getZonedDateTimeNowOfEST(){ return ZonedDateTime.now(ZoneId.of(ZoneIdEnum.EST.getZoneIdName())); } /** * 获取当前东京时区时间 * @param zoneId * @return */ public static ZonedDateTime getZonedDateTimeNowOfTokyo(){ return ZonedDateTime.now(ZoneId.of(ZoneIdEnum.JST.getZoneIdName())); } /** * 获取时区当前时间 * @param zoneId * @return */ public static ZonedDateTime getZonedDateTimeNow(String zoneId){ Objects.requireNonNull(zoneId, "zoneId"); return ZonedDateTime.now(ZoneId.of(zoneId)); } /** * 时区转换计算 * @param zonedDateTime * @param zoneId 例如 Asia/Shanghai * @return */ public static ZonedDateTime transform(ZonedDateTime zonedDateTime, String zoneId){ Objects.requireNonNull(zoneId, "zoneId"); return transform(zonedDateTime, ZoneId.of(zoneId)); } /** * 时区转换计算 * @param zonedDateTime * @param zone * @return */ public static ZonedDateTime transform(ZonedDateTime zonedDateTime, ZoneId zone){ Objects.requireNonNull(zonedDateTime, "zonedDateTime"); Objects.requireNonNull(zone, "zone"); return zonedDateTime.withZoneSameInstant(zone); }

测试代码:

/** * 时区时间计算 */ @Test public void zonedDateTimeTest(){ //系统默认时区 System.out.println(DateTimeCalculatorUtil.getZonedDateTimeNowOfDefault()); //系统上海时区 ZonedDateTime shanghaiZonedDateTime = DateTimeCalculatorUtil.getZonedDateTimeNowOfShanghai(); System.out.println(shanghaiZonedDateTime); //系统巴黎时区 ZonedDateTime parisZonedDateTime = DateTimeCalculatorUtil.getZonedDateTimeNowOfParis(); System.out.println(parisZonedDateTime); //系统美国东部时区纽约时间 System.out.println(DateTimeCalculatorUtil.getZonedDateTimeNowOfEST()); //系统东京时区 System.out.println(DateTimeCalculatorUtil.getZonedDateTimeNowOfTokyo()); //上海时区,转换为巴黎时区 System.out.println("============transform 时区转换============="); System.out.println("shanghaiZonedDateTime: "+shanghaiZonedDateTime); ZonedDateTime transformZonedDateTime = DateTimeCalculatorUtil.transform(shanghaiZonedDateTime, ZoneIdEnum.ECT.getZoneIdName()); System.out.println("transformZonedDateTime: "+transformZonedDateTime);

}

输出:

2020-02-19T13:40:49.638+08:00[Asia/Shanghai] 2020-02-19T13:40:49.640+08:00[Asia/Shanghai] 2020-02-19T06:40:49.642+01:00[Europe/Paris] 2020-02-19T00:40:49.653-05:00 2020-02-19T14:40:49.653+09:00[Asia/Tokyo] ============transform 时区转换============= shanghaiZonedDateTime: 2020-02-19T13:40:49.640+08:00[Asia/Shanghai] transformZonedDateTime: 2020-02-19T06:40:49.640+01:00[Europe/Paris]

2.4 时区时间格式化与解析

(1)时区时间格式化和ISO常用格式化,比如:yyyy-MM-dd'T'HH:mm:ssZ

/** * 时区时间格式化和ISO常用格式化 * YYYY_MM_DD_T_HH_MM_SS_Z = "yyyy-MM-dd'T'HH:mm:ssZ" */ @Test public void zonedDateTimeFormatTest(){ //默认为系统时区 ZonedDateTime zonedDateTime = ZonedDateTime.now(); //2020-02-18T22:37:55+0800 System.out.println(DateTimeFormatterUtil.format(zonedDateTime, DateTimeFormatterUtil.YYYY_MM_DD_T_HH_MM_SS_Z_FMT));

    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.ISO\_DATE\_FMT));
    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.ISO\_DATE\_TIME\_FMT));
    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.ISO\_INSTANT\_FMT));
    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.ISO\_LOCAL\_DATE\_FMT));
    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.ISO\_LOCAL\_DATE\_TIME\_FMT));
    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.ISO\_LOCAL\_TIME\_FMT));
    
    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.ISO\_TIME\_FMT));
    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.ISO\_WEEK\_DATE\_FMT));
    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.ISO\_ZONED\_DATE\_TIME\_FMT));
    System.out.println(zonedDateTime.format(DateTimeFormatterUtil.BASIC\_ISO\_DATE\_FMT));
}

输出:

2020-02-19T13:47:13+0800 2020-02-19+08:00 2020-02-19T13:47:13.271+08:00[Asia/Shanghai] 2020-02-19T05:47:13.271Z 2020-02-19 2020-02-19T13:47:13.271 13:47:13.271 13:47:13.271+08:00 2020-W08-3+08:00 2020-02-19T13:47:13.271+08:00[Asia/Shanghai] 20200219+0800

(2)时区时间解析

/** * 时区时间解析 * YYYY_MM_DD_T_HH_MM_SS_Z = "yyyy-MM-dd'T'HH:mm:ssZ" */ @Test public void parseToZonedDateTimeTest(){ String text = "2020-02-18T22:37:55+0800"; ZonedDateTime zonedDateTime = DateTimeFormatterUtil.parseToZonedDateTime(text, DateTimeFormatterUtil.YYYY_MM_DD_T_HH_MM_SS_Z_FMT); System.out.println(zonedDateTime);

    String text2 \= "2020-02-19T12:30:25.121+08:00\[Asia/Shanghai\]";
    ZonedDateTime zonedDateTime2 \= DateTimeFormatterUtil.parseToZonedDateTime(text2, DateTimeFormatterUtil.ISO\_ZONED\_DATE\_TIME\_FMT);
    System.out.println(zonedDateTime2);
    
    ZonedDateTime zonedDateTime3 \= ZonedDateTime.parse(text2);
    System.out.println(zonedDateTime3);
}

输出:

2020-02-18T22:37:55+08:00 2020-02-19T12:30:25.121+08:00[Asia/Shanghai] 2020-02-19T12:30:25.121+08:00[Asia/Shanghai]

源代码地址:https://github.com/xkzhangsan/xk-time

部分参考:

https://www.cnblogs.com/xwdreamer/p/8761825.html

点赞
收藏
评论区
推荐文章
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
Java日期时间API系列20
  Java日期时间API系列19Jdk8中java.time包中的新的日期时间API类,ZonedDateTime与ZoneId和LocalDateTime的关系,ZonedDateTime格式化和时区转换等。(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnbl
Wesley13 Wesley13
3年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Wesley13 Wesley13
3年前
Java日期时间API系列25
  通过Java日期时间API系列24Jdk8中java.time包中的新的日期时间API类,MonthDay类源码和应用,对比相同月日时间。(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.cnblogs.com%2F%2520https%3A%2F%2F%2F%2Fw
Wesley13 Wesley13
3年前
Java日期时间API系列34
  通过Java日期时间API系列9Jdk8中java.time包中的新的日期时间API类的Period和Duration的区别(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fxkzhangsanx%2Fp%2F12110137.html)
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
3年前
Java日期时间API系列13
  从前面的系列博客中可以看出Jdk8中java.time包中的新的日期时间API类设计的很好,但Date由于使用仍非常广泛,这就涉及到Date转LocalDateTime,LocalDateTime转Date。下面是时间类互相转换大全,包含Instant、LocalDate、LocalDateTime、LocalTime、ZonedDateTime和Dat
Wesley13 Wesley13
3年前
Java日期时间API系列35
  通过Java日期时间API系列1Jdk7及以前的日期时间类(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fxkzhangsanx%2Fp%2F12032719.html)中得知,Java8以前除了java.sql.Timestamp扩充
Wesley13 Wesley13
3年前
Java日期时间API系列33
  从Java日期时间API系列10Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fwww.cnblogs.com%2Fxkzhangsanx%2Fp%2F12113489.html)中