1. 程式人生 > 其它 >Java基礎|日期與時間

Java基礎|日期與時間

技術標籤:Javajava

本文從日期與時間的Date、Calendar和TimeZone等舊API到LocalDateTime、ZonedDateTime、ZoneId等新API方面,展開較為詳細的介紹。

標準庫API

  1. Java標準庫有兩套處理日期和時間的API:
    • 一套定義在java.util這個包裡面,主要包括Date、Calendar和TimeZone這幾個類;
    • 一套新的API是在Java 8引入的,定義在java.time這個包裡面,主要包括LocalDateTime、ZonedDateTime、ZoneId等。

old API

Date

  1. java.util.Date是用於表示一個日期和時間的物件,注意與java.sql.Date區分,後者用在資料庫中。如果觀察Date的原始碼,可以發現它實際上儲存了一個long型別的以毫秒錶示的時間戳。
  2. 如果我們想要針對使用者的偏好精確地控制日期和時間的格式,就可以使用SimpleDateFormat對一個Date進行轉換。它用預定義的字串表示格式化:
    • yyyy:年
    • MM:月
    • dd: 日
    • HH: 小時
    • mm: 分鐘
    • ss: 秒
  3. Date物件有幾個嚴重的問題:它不能轉換時區,除了toGMTString()可以按GMT+0:00輸出外,Date總是以當前計算機系統的預設時區為基礎進行輸出。此外,我們也很難對日期和時間進行加減,計算兩個日期相差多少天,計算某個月第一個星期一的日期等。
//Date的基本用法
public class Main {
    public static void
main(String[] args) { //獲取當前時間 Date date = new Date(); System.out.println(date.getYear() + 1900);// 必須加上1900 System.out.println(date.getMonth() + 1); // 0~11,必須加上1 System.out.println(date.getDate()); // 1~31,不能加1 //轉換為String System.out.println(date.toString(
)); //轉換為GMT時區 System.out.println(date.toGMTString()); //轉換為本地時區 System.out.println(date.toLocaleString()); //自定義時間格式輸出 //2020-12-15 08:49:39 System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date)); //星期二 十二月,2020 System.out.println(new SimpleDateFormat("E MMM,yyyy").format(date)); } }

Calendar

  1. Calendar可以用於獲取並設定年、月、日、時、分、秒,它和Date比,主要多了一個可以做簡單的日期和時間運算的功能。
  2. Calendar獲取年月日這些資訊變成了get(int field),返回的年份不必轉換,返回的月份仍然要加1,返回的星期要特別注意,1~7分別表示週日,週一,……,週六。
  3. Calendar只有一種方式獲取,即Calendar.getInstance(),而且一獲取到就是當前時間。如果我們想給它設定成特定的一個日期和時間,就必須先清除所有欄位:
//Calendar的用法
public class Main {
    public static void main(String[] args) {
        //獲取當前時間
        Calendar c = Calendar.getInstance();
        int y = c.get(Calendar.YEAR);
        int m = c.get(Calendar.MONTH) + 1;
        int d = c.get(Calendar.DAY_OF_MONTH);
        //星期幾,1~7分別表示週日,週一,……,週六。
        int w = c.get(Calendar.DAY_OF_WEEK);
        int hh = c.get(Calendar.HOUR_OF_DAY);
        int mm = c.get(Calendar.MINUTE);
        int ss = c.get(Calendar.SECOND);
        int ms = c.get(Calendar.MILLISECOND);
        System.out.println(y + "-" + m + "-" + d + " " + w + " " + hh + ":" + mm + ":" + ss + "." + ms);

        //清除所有
        c.clear();
        //設定2021年
        c.set(Calendar.YEAR,2021);
        // 設定9月:注意8表示9月:
        c.set(Calendar.MONTH, 8);
        // 設定2日:
        c.set(Calendar.DATE, 2);
        // 設定時間:
        c.set(Calendar.HOUR_OF_DAY, 21);
        c.set(Calendar.MINUTE, 22);
        c.set(Calendar.SECOND, 23);
        //2021-09-02 21:22:23
        System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(c.getTime()));
    }
}

利用Calendar.getTime()可以將一個Calendar物件轉換成Date物件,然後就可以用SimpleDateFormat進行格式化了。

TimeZone

  1. Calendar和Date相比,它提供了時區轉換的功能。時區用TimeZone物件表示:
public class Main {
    public static void main(String[] args) {
        //TimeZone的用法
        TimeZone tzDefault = TimeZone.getDefault(); //當前時區
        TimeZone tzGMT9 = TimeZone.getTimeZone("GMT+09:00");//GMT+9:00時區
        TimeZone tzNY = TimeZone.getTimeZone("America/New_York"); // 紐約時區
        System.out.println(tzDefault.getID()); // GMT+08:00
        System.out.println(tzGMT9.getID()); // GMT+09:00
        System.out.println(tzNY.getID()); // America/New_York
        //列出系統支援的所有時區ID
        for
        (String str : TimeZone.getAvailableIDs()) {
            System.out.println(str);
        }
    }
}
  1. 使用Calendar將北京時間2019-11-20 8:15:00轉換為紐約時間:
public class Main {
    public static void main(String[] args) {
        // 當前時間:
        Calendar c = Calendar.getInstance();
        // 清除所有:
        c.clear();
        // 設定為北京時區:
        c.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        // 設定年月日時分秒:
        c.set(2019, 10, 20, 8, 15, 0);
        // 顯示時間:
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("America/New_York"));
        // 2019-11-19 19:15:00
        System.out.println(sdf.format(c.getTime()));
    }
}
  1. Calendar也可以對日期和時間進行簡單的加減:
public class Main {
    public static void main(String[] args) {
        // 當前時間:
        Calendar c = Calendar.getInstance();
        // 清除所有:
        c.clear();
        // 設定年月日時分秒:
        c.set(2019, 10 /* 11月 */, 20, 8, 15, 0);
        // 加5天並減去2小時:
        c.add(Calendar.DAY_OF_MONTH, 5);
        c.add(Calendar.HOUR_OF_DAY, -2);
        // 顯示時間:
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d = c.getTime();
        System.out.println(sdf.format(d));
        // 2019-11-25 6:15:00
    }
}

new API

  1. 從Java 8開始,java.time包提供了新的日期和時間API,主要涉及的型別有:
    • 本地日期和時間:LocalDateTime,LocalDate,LocalTime;
    • 帶時區的日期和時間:ZonedDateTime;
    • 時刻:Instant;
    • 時區:ZoneId,ZoneOffset;
    • 時間間隔:Duration。
    以及一套新的用於取代SimpleDateFormat的格式化型別DateTimeFormatter。
  2. 新API修正了舊API不合理的常量設計:
    • Month的範圍用1~12表示1月到12月;
    • Week的範圍用1~7表示週一到週日。
  3. 新API的型別幾乎全部是不變型別(和String類似),可以放心使用不必擔心被修改。

LocalDateTime

  1. 基本用法
public static void main(String[] args) {
    LocalDate d = LocalDate.now(); // 當前日期
    LocalTime t = LocalTime.now(); // 當前時間
    LocalDateTime dt = LocalDateTime.now(); // 當前日期和時間
    System.out.println(d); // 嚴格按照ISO 8601格式列印
    System.out.println(t); // 嚴格按照ISO 8601格式列印
    System.out.println(dt); // 嚴格按照ISO 8601格式列印
    
    //保證獲取到同一時刻的日期和時間
    LocalDateTime dt = LocalDateTime.now(); // 當前日期和時間
    LocalDate d = dt.toLocalDate(); // 轉換到當前日期
    LocalTime t = dt.toLocalTime(); // 轉換到當前時間

    // 指定日期和時間:
    LocalDate d2 = LocalDate.of(2019, 11, 30); // 2019-11-30, 注意11=11月
    LocalTime t2 = LocalTime.of(15, 16, 17); // 15:16:17
    LocalDateTime dt2 = LocalDateTime.of(2019, 11, 30, 15, 16, 17);
    LocalDateTime dt3 = LocalDateTime.of(d2, t2);

    //轉換為標準格式
    LocalDateTime dt4 = LocalDateTime.parse("2019-11-19T15:16:17");
    LocalDate d4 = LocalDate.parse("2019-11-19");
    LocalTime t4 = LocalTime.parse("15:16:17");

    //自定義輸出格式
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
    System.out.println(dtf.format(LocalDateTime.now()));//2020/12/15 10:17:44
    //用自定義格式解析為標準格式
    LocalDateTime dt_2 = LocalDateTime.parse("2019/11/30 15:16:17", dtf);
    System.out.println(dt_2); //2019-11-30T15:16:17
}
  1. 注意ISO 8601規定的日期和時間分隔符是T。標準格式如下:
    • 日期:yyyy-MM-dd
    • 時間:HH:mm:ss
    • 帶毫秒的時間:HH:mm:ss.SSS
    • 日期和時間:yyyy-MM-dd’T’HH:mm:ss
    • 帶毫秒的日期和時間:yyyy-MM-dd’T’HH:mm:ss.SSS
  2. LocalDateTime提供了對日期和時間進行加減的非常簡單的鏈式呼叫
  3. 對日期和時間進行調整則使用withXxx()方法,例如:withHour(15)會把10:11:12變為15:11:12:
    • 調整年:withYear()
    • 調整月:withMonth()
    • 調整日:withDayOfMonth()
    • 調整時:withHour()
    • 調整分:withMinute()
    • 調整秒:withSecond()
  4. 要判斷兩個LocalDateTime的先後,可以使用isBefore()、isAfter()方法。由於LocalDateTime無法與時間戳進行轉換,因為LocalDateTime沒有時區,無法確定某一時刻。後面要介紹的ZonedDateTime相當於LocalDateTime加時區的組合,它具有時區,可以與long表示的時間戳進行轉換。
  5. Duration表示兩個時刻之間的時間間隔,另一個類似的Period表示兩個日期之間的天數。
public static void main(String[] args) {
    LocalDateTime dt = LocalDateTime.of(2019, 10, 26, 20, 30, 59);
    System.out.println(dt);
    // 加5天減3小時:
    LocalDateTime dt2 = dt.plusDays(5).minusHours(3);
    System.out.println(dt2); // 2019-10-31T17:30:59
    // 減1月:
    LocalDateTime dt3 = dt2.minusMonths(1);
    System.out.println(dt3); // 2019-09-30T17:30:59
     //withXxx()方法調整日期,月份變為8:
    LocalDateTime dt8 = dt7.withMonth(8);
    System.out.println(dt8);

    //判斷兩個LocalDateTime的先後,可以使用isBefore()、isAfter()方法
    LocalDateTime now = LocalDateTime.now();
    LocalDateTime target = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
    System.out.println(now.isBefore(target));
    System.out.println(LocalDate.now().isBefore(LocalDate.of(2019, 11, 19)));
    System.out.println(LocalTime.now().isAfter(LocalTime.parse("08:15:00")));

    //Duration和Period
    LocalDateTime start = LocalDateTime.of(2019, 11, 19, 8, 15, 0);
    LocalDateTime end = LocalDateTime.of(2020, 1, 9, 19, 25, 30);
    Duration du = Duration.between(start, end);
    System.out.println(du); // PT1235H10M30S

    Period p = LocalDate.of(2019, 11, 19).until(LocalDate.of(2020, 1, 9));
    System.out.println(p); // P1M21D
}

ZonedDateTime

  1. LocalDateTime總是表示本地日期和時間,要表示一個帶時區的日期和時間,我們就需要ZonedDateTime。可以簡單地把ZonedDateTime理解成LocalDateTime加ZoneId。
  2. 基本用法
public class ZonedDateTimeDemo {
    public static void main(String[] args) {
        ZonedDateTime zbj = ZonedDateTime.now(); // 預設時區
        ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定時區獲取當前時間
        System.out.println(zbj);
        System.out.println(zny);

        //時區轉換
        //將北京時間轉換為紐約時間
        // 以中國時區獲取當前時間:
        ZonedDateTime zbj1 = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
        //轉換為紐約時間:
        ZonedDateTime zny1=zbj1.withZoneSameInstant(ZoneId.of("America/New_York"));
        System.out.println(zbj1);
        System.out.println(zny1);

    }
}

DateTimeFormatter

  1. 使用新的LocalDateTime或ZonedLocalDateTime時,我們要進行格式化顯示,就要使用DateTimeFormatter。
  2. 優點:因為SimpleDateFormat不是執行緒安全的,使用的時候,只能在方法內部建立新的區域性變數。而DateTimeFormatter可以只建立一個例項,到處引用。
public class DateTimeFormatterDemo {
    public static void main(String[] args) {
        //DateTimeFormatter基本用法
        ZonedDateTime zdt = ZonedDateTime.now();
        System.out.println(zdt);//2020-12-15T14:10:14.679+08:00[GMT+08:00]
        //建立DateTimeFormatter方法一:傳入格式化字串
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
        System.out.println(formatter.format(zdt));//2020-12-15 14:10
        //建立DateTimeFormatter方法二:傳入格式化字串,同時指定Locale
        DateTimeFormatter usFormatter = DateTimeFormatter.ofPattern("E, yyyy-MMMM-dd HH:mm", Locale.US);
        System.out.println(usFormatter.format(zdt)); //Tue, 2020-December-15 14:10

        //預設的toString()方法顯示的字串就是按照ISO 8601格式顯示的
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(DateTimeFormatter.ISO_DATE.format(ldt));//2020-12-15
        System.out.println(DateTimeFormatter.ISO_DATE_TIME.format(ldt));//2020-12-15T14:10:14.751

    }
}

Instant

  1. Java提供的System.currentTimeMillis()返回的就是以毫秒錶示的當前時間戳。這個當前時間戳在java.time中以Instant型別表示,我們用Instant.now()獲取當前時間戳,效果和System.currentTimeMillis()類似。
public class InstantDemo {
    public static void main(String[] args) {
        Instant now=Instant.now();
        System.out.println(now.getEpochSecond()); //秒1608015033
        System.out.println(now.toEpochMilli());//毫秒 1608015033237

        //以指定時間戳建立Instant
        Instant ins=Instant.ofEpochSecond(1608015033);
        ZonedDateTime zdt=ins.atZone(ZoneId.systemDefault());
        System.out.println(zdt);//2020-12-15T14:49:56+08:00[GMT+08:00]
        
    }
}

如果您覺得有收穫的話,歡迎點贊評論加關注,互相學習進步,一直在路上~~