
在 JDK 8 以前,Java 語言爲咱們提供了兩個類用於操做時間,它們分別是:java.util.Date 和 java.util.Calendar,但在 JDK 8 的時候爲了解決舊時間操做類的一些缺陷,提供了幾個新的類,用於操做時間和日期,它們分別是:LocalTime、LocalDateTime、Instant,都位於 java.time 包下。時間的操做在咱們平常的開發中常常見到,好比,業務數據都要記錄建立時間和修改時間,並要把這些時間格式化以後顯示到前端頁面,再好比咱們須要計算業務數據的時間間隔等,都離不開對時間的操做,那如何正確而優雅地使用時間?這就是咱們接下來要討論的話題。前端
格林威治時間java
格林威治(又譯格林尼治)是英國倫敦南郊原格林威治天文臺的所在地,它是世界計算時間和地球經度的起點,國際經度會議 1884 年在美國華盛頓召開,會上經過協議,以通過格林威治天文臺的經線爲零度經線(即本初子午線),做爲地球經度的起點,並以格林威治爲「世界時區」的起點。web
格林威治時間和北京時間的關係面試
格林威治時間被定義爲世界時間,就是 0 時區,北京是東八區。也就是說格林威治時間的 1 日 0 點,對應到北京的時間就是 1 日 8 點。sql
時間戳緩存
時間戳是指格林威治時間 1970-01-01 00:00:00(北京時間 1970-01-01 08:00:00)起至如今的總秒數。安全
1 獲取時間微信
Date date = new Date();
System.out.println(date);
Calendar calendar = Calendar.getInstance();
Date time = calendar.getTime();
System.out.println(time);
2 獲取時間戳
long ts = new Date().getTime();
System.out.println(ts);
long ts2 = System.currentTimeMillis();
System.out.println(ts2);
long ts3 = Calendar.getInstance().getTimeInMillis();
System.out.println(ts3);
3 格式化時間
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sf.format(new Date())); // output:2019-08-16 21:46:22
SimpleDateFormat 構造參數的含義,請參考如下表格信息:多線程
字符 | 含義 | 示例 |
---|---|---|
y | 年 | yyyy-1996 |
M | 月 | MM-07 |
d | 月中的天數 | dd-02 |
D | 年中的天數 | 121 |
E | 星期幾 | 星期四 |
H | 小時數(0-23) | HH-23 |
h | 小時數(1-12) | hh-11 |
m | 分鐘數 | mm-02 |
s | 秒數 | ss-03 |
Z | 時區 | +0800 |
使用示例:架構
獲取星期幾:new SimpleDateFormat("E").format(new Date())
獲取當前時區:new SimpleDateFormat("Z").format(new Date*())
注意事項:在多線程下 SimpleDateFormat 是非線程安全的,所以在使用 SimpleDateFormat 時要注意這個問題。在多線程下,若是使用不當,可能會形成結果不對或內存泄漏等問題。
4 時間轉換
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// String 轉 Date
String str = "2019-10-10 10:10:10";
System.out.println(sf.parse(str));
//時間戳的字符串 轉 DateString ts
String = "1556788591462";
// import java.sql
Timestamp ts = new Timestamp(Long.parseLong(tsString)); // 時間戳的字符串轉 Date
System.out.println(sf.format(ts));
注意事項:當使用 SimpleDateFormat.parse() 方法進行時間轉換的時候,SimpleDateFormat 的構造函數必須和待轉換字符串格式一致。
5 得到昨天此刻時間
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -1);
System.out.println(calendar.getTime());
JDK 8 時間操做
JDK 8 對時間操做新增了三個類:LocalDateTime、LocalDate、LocalTime。
LocalDate 只包含日期,不包含時間,不可變類,且線程安全。
LocalTime 只包含時間,不包含日期,不可變類,且線程安全。
LocalDateTime 既包含了時間又包含了日期,不可變類,且線程安全。
值得一提的是 JDK 8 中新增的這三個時間相關的類,都是線程安全的,這極大地下降了多線程下代碼開發的風險。
1 獲取時間
// 獲取日期
LocalDate localDate = LocalDate.now();
System.out.println(localDate); // output:2019-08-16
// 獲取時間
LocalTime localTime = LocalTime.now();
System.out.println(localTime); // output:21:09:13.708
// 獲取日期和時間
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // output:2019-08-16T21:09:13.708
2 獲取時間戳
long milli = Instant.now().toEpochMilli(); // 獲取當前時間戳(精確到毫秒)
long second = Instant.now().getEpochSecond(); // 獲取當前時間戳(精確到秒)
System.out.println(milli); // output:1565932435792
System.out.println(second); // output:1565932435
3 時間格式化
// 時間格式化①
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String timeFormat = dateTimeFormatter.format(LocalDateTime.now());
System.out.println(timeFormat); // output:2019-08-16 21:15:43
// 時間格式化②
String timeFormat2 = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(timeFormat2); // output:2019-08-16 21:17:48
4 時間轉換
String timeStr = "2019-10-10 06:06:06";
LocalDateTime dateTime = LocalDateTime.parse(timeStr,DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
System.out.println(dateTime);
5 得到昨天此刻時間
LocalDateTime today = LocalDateTime.now();
LocalDateTime yesterday = today.plusDays(-1);
System.out.println(yesterday);
相關面試題
1. 獲取當前時間有幾種方式?
答:獲取當前時間常見的方式有如下三種:
new Date()
Calendar.getInstance().getTime()
LocalDateTime.now()
2. 如何獲取昨天此刻的時間?
答:如下爲獲取昨天此刻時間的兩種方式:
// 獲取昨天此刻的時間(JDK 8 之前)
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE,-1);
System.out.println(c.getTime());
// 獲取昨天此刻的時間(JDK 8)
LocalDateTime todayTime = LocalDateTime.now();
System.out.println(todayTime.plusDays(-1));
3. 如何獲取本月的最後一天?
答:如下爲獲取本月最後一天的兩種方式:
// 獲取本月的最後一天(JDK 8 之前)
Calendar ca = Calendar.getInstance();
ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));
System.out.println(ca.getTime());
// 獲取本月的最後一天(JDK 8)
LocalDate today = LocalDate.now();
System.out.println(today.with(TemporalAdjusters.lastDayOfMonth()));
4. 獲取當前時間的時間戳有幾種方式?
答:如下爲獲取當前時間戳的幾種方式:
System.currentTimeMillis()
new Date().getTime()
Calendar.getInstance().getTime().getTime()
Instant.now().toEpochMilli()
LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()
其中,第四種和第五種方式是 JDK 8 才新加的。
5. 如何優雅地計算兩個時間的相隔時間?
答:JDK 8 中可使用 Duration 類來優雅地計算兩個時間的相隔時間,代碼以下:
LocalDateTime dt1 = LocalDateTime.now();
LocalDateTime dt2 = dt1.plusSeconds(60);
Duration duration = Duration.between(dt1, dt2);
System.out.println(duration.getSeconds()); // output:60
6. 如何優雅地計算兩個日期的相隔日期?
答:JDK 8 中可使用 Period 類來優雅地計算兩個日期的相隔日期,代碼以下:
LocalDate d1 = LocalDate.now();
LocalDate d2 = d1.plusDays(2);
Period period = Period.between(d1, d2);
System.out.println(period.getDays()); //output:2
7. SimpleDateFormat 是線程安全的嗎?爲何?
答:SimpleDateFormat 是非線程安全的。由於查看 SimpleDateFormat 的源碼能夠得知,全部的格式化和解析,都須要經過一箇中間對象進行轉換,這個中間對象就是 Calendar,這樣的話就形成非線程安全。試想一下當咱們有多個線程操做同一個 Calendar 的時候後來的線程會覆蓋先來線程的數據,那最後其實返回的是後來線程的數據,所以 SimpleDateFormat 就成爲了非線程的了。
8. 怎麼保證 SimpleDateFormat 的線程安全?
答:保證 SimpleDateFormat 線程安全的方式以下:
使用 Synchronized,在須要時間格式化的操做使用 Synchronized 關鍵字進行包裝,保證線程堵塞格式化;
手動加鎖,把須要格式化時間的代碼,寫到加鎖部分,相對 Synchronized 來講,編碼效率更低,性能略好,代碼風險較大(風險在於不要忘記在操做的最後,手動釋放鎖);
使用 JDK 8 的 DateTimeFormatter 替代 SimpleDateFormat。
9. JDK 8 中新增的時間類都有哪些優勢?
答:JDK 8 中的優勢具體有如下幾個優勢,以下:
線程安全性
使用的便利性(如獲取當前時間戳的便利性、增減日期的便利性等)
編寫代碼更簡單優雅,如當前時間的格式化:LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
10. 如何比較兩個時間(Date)的大小?
答:時間比較有如下三種方式:
獲取兩個時間的時間戳,獲得兩個 long 類型的變量,兩個變量相減,經過結果的正負值來判斷大小;
經過 Date 自帶的 before()、after()、equals() 等方法比較,代碼示例 date1.before(date2);
經過 compareTo() 方法比較,代碼示例:date1.compareTo(date2),返回值 -1 表示前一個時間比後一個時間小,0 表示兩個時間相等,1 表示前一個時間大於後一個時間。
JDK 8 以前使用 java.util.Date 和 java.util.Calendar 來操做時間,它們有兩個很明顯的缺點,第一,非線程安全;第二,API 調用不方便。JDK 8 新增了幾個時間操做類 java.time 包下的 LocalDateTime、LocalDate、LocalTime、Duration(計算相隔時間)、Period(計算相隔日期)和 DateTimeFormatter,提供了多線程下的線程安全和易用性,讓咱們能夠更好的操做時間。
往期精選
本文分享自微信公衆號 - 架構師修煉(jiagouxiulian)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。