玩轉時間操做

玩轉時間操做 + 面試題

在 JDK 8 以前,Java 語言爲咱們提供了兩個類用於操做時間,它們分別是:java.util.Date 和 java.util.Calendar,但在 JDK 8 的時候爲了解決舊時間操做類的一些缺陷,提供了幾個新的類,用於操做時間和日期,它們分別是:LocalTime、LocalDateTime、Instant,都位於 java.time 包下。前端

時間的操做在咱們平常的開發中常常見到,好比,業務數據都要記錄建立時間和修改時間,並要把這些時間格式化以後顯示到前端頁面,再好比咱們須要計算業務數據的時間間隔等,都離不開對時間的操做,那如何正確而優雅地使用時間?這就是咱們接下來要討論的話題。java

時間基礎知識科普

格林威治時間

格林威治(又譯格林尼治)是英國倫敦南郊原格林威治天文臺的所在地,它是世界計算時間和地球經度的起點,國際經度會議 1884 年在美國華盛頓召開,會上經過協議,以通過格林威治天文臺的經線爲零度經線(即本初子午線),做爲地球經度的起點,並以格林威治爲「世界時區」的起點。面試

格林威治時間和北京時間的關係

格林威治時間被定義爲世界時間,就是 0 時區,北京是東八區。也就是說格林威治時間的 1 日 0 點,對應到北京的時間就是 1 日 8 點。sql

時間戳

時間戳是指格林威治時間 1970-01-01 00:00:00(北京時間 1970-01-01 08:00:00)起至如今的總秒數。安全

JDK 8 以前的時間操做

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));
//時間戳的字符串 轉 Date
String tsString = "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。spa

  • 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,提供了多線程下的線程安全和易用性,讓咱們能夠更好的操做時間。
_

歡迎關注個人公衆號,回覆關鍵字「Java」 ,將會有大禮相送!!! 祝各位面試成功!!!

相關文章
相關標籤/搜索