Java基礎|日期與時間
阿新 • • 發佈:2020-12-16
本文從日期與時間的Date、Calendar和TimeZone等舊API到LocalDateTime、ZonedDateTime、ZoneId等新API方面,展開較為詳細的介紹。
標準庫API
- Java標準庫有兩套處理日期和時間的API:
• 一套定義在java.util這個包裡面,主要包括Date、Calendar和TimeZone這幾個類;
• 一套新的API是在Java 8引入的,定義在java.time這個包裡面,主要包括LocalDateTime、ZonedDateTime、ZoneId等。
old API
Date
- java.util.Date是用於表示一個日期和時間的物件,注意與java.sql.Date區分,後者用在資料庫中。如果觀察Date的原始碼,可以發現它實際上儲存了一個long型別的以毫秒錶示的時間戳。
- 如果我們想要針對使用者的偏好精確地控制日期和時間的格式,就可以使用SimpleDateFormat對一個Date進行轉換。它用預定義的字串表示格式化:
• yyyy:年
• MM:月
• dd: 日
• HH: 小時
• mm: 分鐘
• ss: 秒 - 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
- Calendar可以用於獲取並設定年、月、日、時、分、秒,它和Date比,主要多了一個可以做簡單的日期和時間運算的功能。
- Calendar獲取年月日這些資訊變成了get(int field),返回的年份不必轉換,返回的月份仍然要加1,返回的星期要特別注意,1~7分別表示週日,週一,……,週六。
- 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
- 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);
}
}
}
- 使用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()));
}
}
- 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
- 從Java 8開始,java.time包提供了新的日期和時間API,主要涉及的型別有:
• 本地日期和時間:LocalDateTime,LocalDate,LocalTime;
• 帶時區的日期和時間:ZonedDateTime;
• 時刻:Instant;
• 時區:ZoneId,ZoneOffset;
• 時間間隔:Duration。
以及一套新的用於取代SimpleDateFormat的格式化型別DateTimeFormatter。 - 新API修正了舊API不合理的常量設計:
• Month的範圍用1~12表示1月到12月;
• Week的範圍用1~7表示週一到週日。 - 新API的型別幾乎全部是不變型別(和String類似),可以放心使用不必擔心被修改。
LocalDateTime
- 基本用法
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
}
- 注意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 - LocalDateTime提供了對日期和時間進行加減的非常簡單的鏈式呼叫
- 對日期和時間進行調整則使用withXxx()方法,例如:withHour(15)會把10:11:12變為15:11:12:
• 調整年:withYear()
• 調整月:withMonth()
• 調整日:withDayOfMonth()
• 調整時:withHour()
• 調整分:withMinute()
• 調整秒:withSecond() - 要判斷兩個LocalDateTime的先後,可以使用isBefore()、isAfter()方法。由於LocalDateTime無法與時間戳進行轉換,因為LocalDateTime沒有時區,無法確定某一時刻。後面要介紹的ZonedDateTime相當於LocalDateTime加時區的組合,它具有時區,可以與long表示的時間戳進行轉換。
- 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
- LocalDateTime總是表示本地日期和時間,要表示一個帶時區的日期和時間,我們就需要ZonedDateTime。可以簡單地把ZonedDateTime理解成LocalDateTime加ZoneId。
- 基本用法
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
- 使用新的LocalDateTime或ZonedLocalDateTime時,我們要進行格式化顯示,就要使用DateTimeFormatter。
- 優點:因為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
- 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]
}
}
如果您覺得有收穫的話,歡迎點贊評論加關注,互相學習進步,一直在路上~~