Java日期時間API系列19-----Jdk8中java.time包中的新的日期時間API類,ZonedDateTime與ZoneId和LocalDateTime的關系,ZonedDateTime格式化和時區轉換等。


  通過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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM