通過Java日期時間API系列6-----Jdk8中java.time包中的新的日期時間API類中時間范圍示意圖:可以很清晰的看出ZonedDateTime相當於LocalDateTime+ZoneId。
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