1. 程式人生 > >全面解析Java日期時間API

全面解析Java日期時間API

時區

  • GMT(Greenwich Mean Time):格林尼治時間,格林尼治標準時間的正午是指當太陽橫穿格林尼治子午線時(也就是在格林尼治上空最高點時)的時間。
  • UTC(Universal Time Coordinated):統一協調時間,其以原子時秒長為基礎,在時刻上儘量接近於格林尼治標準時間,標準 UTC 時間格式 yyyy-MM-dd'T'HH:mm:ss.SSSXXX

格林尼治時間已經不再被作為標準時間使用,UTC 是最主要的世界時間標準。

Java提供了獲取當前時間的方法

  • System.currentTimeMillis(),返回當前時間,以毫秒為單位,表示的就是當前時刻至 1970-01-01 00:00:00.000
    的毫秒差值。返回的long值可以用來初始化java.util.Date, java.sql.Date, java.sql.Timestamp和java.util.GregorianCalendar物件。
  • System.nanoTime(),返回一個時間值(系統計時器的當前值),精確到納秒。它是由 JVM 提供的一個時間,主要用來精確衡量兩個時間段之間的時間

例如,要測量一些程式碼需要執行多長時間,實現如下,

long startTime = System.nanoTime();
//...the code being measured
long estimatedTime = System.nanoTime() - startTime;

時間粒度
事實上System.currentTimeMillis()方法的時間粒度是大於1毫秒的。如果你反覆執行這個方法,你會發現短時間內得到的結果是相同的,隨後又突然在某一次結果增加了幾十毫秒(也可能更多)。這是很正常的,畢竟這個方法肯定不是世界上最精密的計時器。

舊的時間API

舊的時間API存在諸多問題,例如

  • Java的日期/時間類的定義並不一致,在java.util和java.sql的包中都有日期類,此外用於格式化和解析的類在java.text包中定義。
  • java.util.Date同時包含日期和時間,而java.sql.Date僅包含日期,將其納入java.sql包並不合理。另外這兩個類都有相同的名字,這本身就是一個非常糟糕的設計。
  • 對於時間、時間戳、格式化以及解析,並沒有一些明確定義的類。對於格式化和解析的需求,我們有java.text.DateFormat抽象類,但通常情況下,SimpleDateFormat類被用於此類需求。
  • 所有的日期類都是可變的,因此他們都不是執行緒安全的,這是Java日期類最大的問題之一。
  • 日期類並不提供國際化,沒有時區支援,因此Java引入了java.util.Calendar和java.util.TimeZone類,但他們同樣存在上述所有的問題。

java.util.Date類

java.util.Date類用於封裝日期及時間資訊,一般僅用它顯示某個日期,不對他作任何操作處理,作處理推薦用Calendar類,計算方便。以下已過時的方法沒有列出,可自行檢視jdk文件

構造方法

  • Date() :分配 Date物件並初始化此物件,以表示分配它的時間(精確到毫秒)。

  • Date(long date):分配 Date物件並初始化此物件,以表示自從標準基準時間(稱為“曆元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以來的指定毫秒數。

//1、使用Date類獲取當前系統時間
Date date = new Date();
System.out.println(date);
//由於Date類覆蓋了toString()方法,所以可以直接輸出Date型別的物件
//輸出結果為Fri May 31 10:51:18 GMT+08:00 2019

/*給Date設定年份需要減去 1900
*輸出結果Tue Jul 01 00:00:00 GMT+08:00 3919
*原來這裡存在一個起始年份 1900,實際年份是要在你的年份引數上加上個起始年份。
*/
Date date1 = new Date(2019,5,31);
System.out.println(date1);

//2.分配 Date 物件並初始化此物件,以表示自從標準基準時間(稱為“曆元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以來的指定毫秒數。
Date date2 = new Date(System.currentTimeMillis());//引數為19701月1日以來的毫秒數
Date date3 = new Date(1559284293556l); //long型別要加l
System.out.println(date2);
System.out.println(date3);
//其他Date方法摘要可檢視api

Calendar類與GregorianCalendar

java.util.Calendar類用於封裝日曆資訊,其主作用在於其方法可以對時間分量進行運算。Calendar類是一個抽象類,它為特定瞬間與一組諸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日曆欄位之間的轉換提供了一些方法,併為操作日曆欄位(例如獲得下星期的日期)提供了一些方法。

由於Calendar類是抽象類,且Calendar類的構造方法是protected的,所以無法使用Calendar類的構造方法來建立物件,API中提供了getInstance方法用來建立物件。

Java只提供java.util.GregorianCalendar這一種java.util.Calendar的實現類,可以直接新建出來使用:

Calendar calendar = new GregorianCalendar();//新建出來的calendar預設時間為當前時間,或者說創建出這個物件的時間。

通過Calendar的靜態方法獲取一個例項該方法會根據當前系統所在地區來自行決定時區,幫我們建立Calendar例項,這裡要注意,實際上根據不同的地區,Calendar有若干個子類實現。而Calendar本身是抽象類,不能被例項化!我們不需要關心建立的具體例項為哪個子類,我們只需要根據Calendar規定的方法來使用就可以了。

日曆類所解決的根本問題是簡化日期的計算,要想表示某個日期還應該使用Date類描述。Calendar是可以將其描述的時間轉化為Date的,我們只需要呼叫其getTime()方法就可以獲取描述的日期的Date物件了。

通過日曆類計算時間:為日曆類設定時間,日曆類設定時間使用通用方法set。set(int field,int value),field為時間分量,Calendar提供了相應的常量值,value為對應的值。

只有月份從0開始:0為1月,以此類推,11為12月,其他時間是正常的從1開始。也可以使用Calendar的常量 calendar.NOVEMBER……等。

Calendar.DAY_OF_MONTH  月裡邊的天---號;
Calendar.DAY_OF_WEEK    星期裡的天---星期幾
Calendar.DAY_OF_YEAR     年裡的天
Calendar calendar=Calendar.getInstance();//構造出來表示當前時間的日曆類
Date now=calendar.getTime();//獲取日曆所描述的日期
calendar.set(Calendar.YEAR, 2019);//設定日曆表示2019年 calendar.set(Calendar.DAY_OF_MONTH,15);//設定日曆表示15號
calendar.add(Calendar.DAY_OF_YEAR, 22);//想得到22天以後是哪天
calendar.add(Calendar.DAY_OF_YEAR, -5);//5天以前是哪天
calendar.add(Calendar.MONTH, 1);得到1個月後是哪天
System.out.println(calendar.getTime());

獲取當前日曆表示的日期中的某個時間單位可以使用get方法。

int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH)
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(year+"年"+(month+1)+"月"+day+"日");//month要處理

SimpleDateFormat類

常用構造方法

SimpleDateFormat(String pattern),pattern -為描述日期和時間格式的模式

用給定的模式和預設語言環境的日期格式符號構造 SimpleDateFormat。 注:此構造方法可能不支援所有語言環境。要覆蓋所有語言環境,請使用 DateFormat 類中的工廠方法。

常用方法

public final String format(Date date)將一個 Date 格式化為日期/時間字串
public Date parse(String source)throws ParseException從給定字串的開始解析文字,以生成一個日期。

字串轉成Date物件

//建立一個SimpleDateFormat並且告知它要讀取的字串格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String dateFormat = "2019-05-31";//建立一個日期格式字串
//將一個字串轉換為相應的Date物件
Date date = sdf.parse(dateFormat);//要先捕獲異常
System.out.println(date);//輸出這個Date物件

Date物件轉成字串

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
Date date = new Date(); 
String dateStr = sdf.format(date);//把日期物件傳進去,繼承自DateFormat類的方法。將一個Date格式化為日期/時間字串

在日期格式中,-和空格無特殊意義。無特殊含義的都原樣輸出

//將當前系統時間轉換為2012/05/14 17:05:22的效果
SimpleDateFormat format1 = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
dateStr=format1.format(date);
System.out.println(dateStr);

DateFormat類

java.text.DateFormat類(抽象類)是SimpleDateFormat類的父類,用的少,沒SimpleDateFormat靈活。

java.sql.Date

java.sql.Date繼承java.util.Date,為了把前者轉為後者,需要以下程式碼

Date date = new Date();
//java.sql.Date 不支援Date引數的構造器,傳入long型別的時間
java.sql.Date d = new java.sql.Date(date.getTime());
System.out.println(d);
System.out.println(d.getHours());

輸出結果

2019-05-31
Exception in thread "main" java.lang.IllegalArgumentException
    at java.sql.Date.getHours(Unknown Source)
    at DateTest.DateTest1.main(DateTest1.java:40)

這是由於java.sql.Date是SQL中的單純的日期型別,沒有時分秒。故一般通過JDBC插入java.sql.Date資料時,會發現時分秒都沒有。因此,如果同時需要日期和時間,應該使用Timestamp。它也是 java.util.Date 的子類,Timestamp 則包含時間戳的完整資訊。

java.sql.Timestamp是java.util.Date的派生類(繼承),所以在java.util.Date上能做的事,也可以在java.sql.Timestamp上做。
如果當前是2019-06-01 14:35,你建立一個java.sql.Date 將只記下2019-06-01這個資訊。若你需要保留時間進行JDBC操作,請使用java.sql.Timestamp代替。

long time = System.currentTimeMillis();
java.sql.Timestamp timestamp = new java.sql.Timestamp(time);
timestamp.setNanos(123456);
int nanos = timestamp.getNanos(); // nanos = 123456

現在的Date類中大部分方法已經棄用,現在一般使用舊的API,Date只負責儲存一個時間,並對Calendar和DateFormat提供操作介面。Calendar獲取Date中特定的資訊,對日期時間進行操作,SimpleDateFormat對日期時間進行格式化輸入輸出。

總的來說,Date、Calendar 和 DateFormat 已經能夠處理一般的時間日期問題了。但是不可避免的是,它們依然很繁瑣,不好用並且這些日期類都是可變且執行緒不安全的

Joda-Time

詳情請檢視https://www.ibm.com/developerworks/cn/java/j-jodatime.html

Java 8 時間日期API

特性

  • 不變性:新的日期/時間API中,所有的類都是不可變的,這對多執行緒很有好處。
  • 關注點分離:借鑑了Joda庫的一些優點,新的API將人可讀的日期時間和機器時間(unix timestamp)明確分離,它為日期(Date)、時間(Time)、日期時間(DateTime)、時間戳(unix timestamp)以及時區定義了不同的類。
  • 清晰:在所有的類中,方法都被明確定義用以完成相同的行為。例如要拿到當前例項我們可以使用now()方法,在所有的類中都定義了format()和parse()方法,而不是像以前那樣專門有一個獨立的類。為了更好的處理問題,所有的類都使用了工廠模式和策略模式,一旦你使用了其中某個類的方法,與其他類協同工作並不困難。
  • 實用操作:所有新的日期/時間API類都實現了一系列方法用以完成通用的任務,如:加、減、格式化、解析、從日期/時間中提取單獨部分,等等。
  • 可擴充套件性:新的日期/時間API是工作在ISO-8601日曆系統上的,但我們也可以將其應用在非IOS的日曆上。

Java8日期時間的預設格式如下:yyyy-MM-dd-HH-mm-ss.zzz

幾個主要的核心類:

  • LocalDate:日期類,不帶時間
  • LocalTime:時間類,不帶日期
  • LocalDateTime:日期和時間類
  • ZonedDateTime:時區日期時間類
  • OffsetDateTime:按UTC時間偏移來得到日期時間
  • Clock:獲取某個時區下當前的瞬時時間,日期或者時間
  • Instant:Unix時間,代表時間戳,比如 2018-01-14T02:20:13.592Z
  • Duration:兩個時間之間,表示一個絕對的精確跨度,使用毫秒為單位
  • Period:兩個日期之間
  • ZoneId:時區
  • DateTimeFormatter:格式化輸出
  • TemporalAdjusters:獲得指定日期時間等,如當月的第一天、今年的最後一天等等

下面看看這些類具體如何使用

LocalDate、LocalTime、LocalDateTime

LocalDate是不變的日期時間物件代表一個日期,往往被視為年月日。其他日期欄位,如一年中的一天,一週和一週的一天,也可以訪問。例如,“2007年10月2日”的值可以被儲存在一個LocalDate。

LocalTime是不變的日期時間物件代表一個時間,往往被視為小時分鐘秒。時間為代表的納秒級精度。例如,值“13:45.30.123456789”可以儲存在一個LocalTime。

LocalDateTime是不變的日期時間物件代表一個日期時間,往往被視為年、月、日、時、分、秒。其他日期和時間欄位,如一年中的一天,一週和一週的一天,也可以訪問。時間為代表的納秒級精度。例如,值“2007年10月2日在13:45.30.123456789”可以儲存在一個LocalDateTime。

//now()在預設時區中從系統時鐘獲取當前日期。
LocalDate today = LocalDate.now();
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS); //等價於 today.plusWeeks(1)

//of(int year, int month, int dayOfMonth) 獲得 LocalDate例項從一年、月、日。 
LocalDate date = LocalDate.of(2019,5,31);
LocalTime time = LocalTime.of(20,31,20);

//of(LocalDate date, LocalTime time) 獲得 LocalDateTime例項的日期和時間。 
LocalDateTime dateTime = LocalDateTime.of(date,time);
System.out.println(dateTime);

//LocalDate結合LocalTime成一個LocalDateTime
LocalDateTime dateTime2 = date.atTime(time);
System.out.println(dateTime2); //2019-05-31T20:31:20

DateTimeFormatter

格式器用於解析日期字串和格式化日期輸出,建立格式器最簡單的方法是通過 DateTimeFormatter 的靜態工廠方法以及常量。建立格式器一般有如下三種方式:

  1. 常用 ISO 格式常量,如 ISO_LOCAL_DATE
  2. 字母模式,如 ofPattern("yyyy/MM/dd")
  3. 本地化樣式,如 ofLocalizedDate(FormatStyle.MEDIUM)

和舊的 java.util.DateFormat 相比較,所有的 DateTimeFormatter 例項都是執行緒安全的。

使用DateTimeFormatter完成格式化

//獲取預設時區中從系統時鐘獲取當前日期時間。
LocalDateTime localDateTime = LocalDateTime.now();
//建立一個格式化程式使用指定的模式。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatDateTime = localDateTime.format(formatter);
System.out.println(formatDateTime);

//DateTimeFormatter提供了一些預設的格式化器,DateTimeFormatter.ISO_LOCAL_DATE_TIME 格式 yyyy-MM-ddTHH:mm:ss.SSS
String dateTime2 = localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
System.out.println(dateTime2);

使用DateTimeFormatter完成解析字串

//獲得 LocalDate例項,從使用特定格式的文字字串解析,文字使用格式化程式解析,返回一個日期。
LocalDate localDate = LocalDate.parse("2018/11/11",DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println(localDate); //2018-11-11

Instant

Instant 表示時間線上的一點(與 Date 類似),它只是簡單地表示自 1970 年 1 月 1 日 0 時 0 分 0 秒(UTC)開始的秒數。

Instant 由兩部分組成,一是從原點開始到指定時間點的秒數 s, 二是距離該秒數 s 的納秒數。它以 Unix 時間戳的形式儲存日期時間,不提供處理人類意義上的時間單位(年月日等)。

你可以通過Instant類的工廠方法建立一個Instant例項

//呼叫instant.now()來建立一個確切的表達當前時間的Instant物件.另外也有一些其它方法能建立Instant,具體請查閱Java官方文件。
Instant now = Instant.now();
Instant later = now.plusSeconds(3);
Instant earlier = now.minusSeconds(3);

//第一個引數是秒,第二個是納秒引數,納秒的儲存範圍是0至999,999,999
//2s之後的在加上100萬納秒(1s)
Instant instant = Instant.ofEpochSecond(2,1000000000);
System.out.println(instant); //1970-01-01T00:00:03Z

Instant instant1 = Instant.now();
System.out.println(instant1); //2019-05-31T16:19:28.719Z

Instant instant2 = Instant.parse("2018-11-11T10:12:35.342Z");
System.out.println(instant2); //2018-11-11T10:12:35.342Z

//java.util.Date與Instant可相互轉換
Instant timestamp = new Date().toInstant();
Date.from(Instant.now());

//為了更好的顯示,程式碼改寫為
Date date = new Date();
Instant timestamp = date.toInstant();
System.out.println(date);
System.out.println(timestamp);
Instant now1 = Instant.now();
Date date1 = Date.from(now1);
System.out.println(now1);
System.out.println(date1);
//輸出結果
Sat Jun 01 00:29:42 GMT+08:00 2019
2019-05-31T16:29:42.566Z
2019-05-31T16:29:42.588Z
Sat Jun 01 00:29:42 GMT+08:00 2019

Clock

Clock用於查詢當前時刻,可以用來獲取某個時區下當前的日期和時間,也可以用來代替舊的System.currentTimeMillis()方法和TimeZone.getDefault()方法。

//返回系統預設時間
Clock clock = Clock.systemDefaultZone();
System.out.println(clock.instant().toString());

//世界協調時UTC
Clock clock = Clock.systemUTC();  
//通過Clock獲取當前時刻  
System.out.println("當前時刻為:" + clock.instant());  
//獲取clock對應的毫秒數,與System.currentTimeMillis()輸出相同  
System.out.println(clock.millis());  
System.out.println(System.currentTimeMillis());  

Duration

一個Duration例項是不可變的,當創建出物件後就不能改變它的值了。你只能通過Duration的計算方法,來創建出一個新的Durtaion物件。你會在之後的教程中見到的。一個Duration物件表示兩個Instant間的一段時間。

建立Duration例項,使用Duration類的工廠方法來建立一個Duration物件

Instant first = Instant.now();
// wait some time while something happens
Instant second = Instant.now();
Duration duration = Duration.between(first, second);

//獲得Duration表示秒數,然後獲得在此期間的分鐘數、小時數、天數
Duration d = Duration.ofSeconds(6000);  
System.out.println("6000秒相當於" + d.toMinutes() + "分");  
System.out.println("6000秒相當於" + d.toHours() + "小時");  
System.out.println("6000秒相當於" + d.toDays() + "天");  

訪問Duration的時間
一個Duration物件裡有兩個域:納秒值(小於一秒的部分),秒鐘值(一共有幾秒),他們的組合表達了時間長度。注意與使用System.getCurrentTimeMillis()時不同,Duration不包含毫秒這個屬性。
你可以通過以下兩個方法得到它們的值:getSeconds()和getNano()

Period

Period 是以年月日來衡量一個時間段,用於計算兩個日期間隔,所以 between() 方法只能接收 LocalDate 型別的引數。

LocalDate start = LocalDate.of(2018, Month.JANUARY, 1);
LocalDate end = LocalDate.of(2020, Month.NOVEMBER, 11);
System.out.println("相隔月數:"+Period.between(start, end).getMonths());
System.out.println("相隔天數:"+Period.between(start, end).getDays());
//輸出結果
相隔月數:10
相隔天數:10

值得注意的是,Period 得到的是差值的絕對值(對應年月日直接計算數學上的差值),而並不表示真正的區間距離。

long distanceMonth = start.until(end, ChronoUnit.MONTHS);
long  distanceDay= start.until(end, ChronoUnit.DAYS);
System.out.println("相隔月數"+distanceMonth);
System.out.println("相隔天數"+distanceDay);
//輸出結果
相隔月數:34
相隔天數:1045

ZonedDateTime和ZonedId

ZonedDateTime類是Java 8中日期時間功能裡,用於表示帶時區的日期與時間資訊的類。ZonedDateTime 類的值是不可變的,所以其計算方法會返回一個新的ZonedDateTime 例項。

Java 使用 ZoneId 來標識不同的時區,從基準 UTC 開始的一個固定偏移。

建立一個ZonedDateTime例項

//使用當前時間作為值新建物件
ZonedDateTime dateTime = ZonedDateTime.now();

//使用指定的年月日、時分、納秒以及時區ID來新建物件
//時區是用ZoneId類表示的,可以使用ZoneId.now()或ZoneId.of(“xxx”)來例項化:
//傳給of()方法的引數是時區的ID,如“UTC+1”指距離UTC(格林威治時間)有一小時的時差
ZoneId zoneId = ZoneId.of("UTC+1");
ZonedDateTime dateTime2 = ZonedDateTime.of(2019, 6, 1, 14, 40, 48, 1234, zoneId);

//也可以使用另一種方式表示zone id,即使用地區名字
ZoneId zoneId2 = ZoneId.of("Europe/Copenhagen");

//GregorianCalendar與ZonedDateTime相互轉換
ZonedDateTime zonedDateTime = new GregorianCalendar().toZonedDateTime();
GregorianCalendar.from(zonedDateTime);

TemporalAdjusters

有的時候,你需要進行一些更加複雜的操作,比如,將日期調整到下個週日、下個工作日,或者是本月的最後一天。

簡單應用例子

LocalDate localDate = LocalDate.now();  
// 1. 本月第一天
LocalDate firstDayOfMonth = localDate.with(TemporalAdjusters.firstDayOfMonth());
// 2. 本月最後一天
LocalDate lastDayOfMonth = localDate.with(TemporalAdjusters.lastDayOfMonth());
// 3. 本年第一天
LocalDate firstDayOfYear = localDate.with(TemporalAdjusters.firstDayOfYear());
// 4. 下個月第一天
LocalDate firstDayOfNextMonth = localDate.with(TemporalAdjusters.firstDayOfNextMonth());
// 5. 本年度最後一天
LocalDate lastDayOfYear = localDate.with(TemporalAdjusters.lastDayOfYear());
System.out.println(firstDayOfMonth);
System.out.println(lastDayOfMonth);
System.out.println(firstDayOfYear);
System.out.println(firstDayOfNextMonth);
System.out.println(lastDayOfYear);

這時,你可以使用過載版本的with方法,向其傳遞一個提供了更多定製化選擇的TemporalAdjuster物件,更加靈活地處理日期。TemporalAdjusters類通過靜態方法提供了大量的常用的TemporalAdjuster的實現供我們使用。

/**
 * 時間校正器TemporalAdjuster
 */
@Test
public void Test() {
    LocalDateTime now1 = LocalDateTime.now();
    //獲取月中的第一天
    now1.with(TemporalAdjusters.firstDayOfMonth());
    //獲取下一年的第一天   
    now1.with(TemporalAdjusters.firstDayOfNextYear());
    //獲取年中第一天
    now1.with(TemporalAdjusters.lastDayOfYear());
    //獲取月中最後一天
    now1.with(TemporalAdjusters.lastDayOfMonth());
    //獲取下個星期一
    now1.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
    //自定時時間:下一個工作日,因為這裡需要一個介面,所以完全可以自定義方法
    now1.with((e) -> {
        LocalDateTime now = (LocalDateTime)e; 
        DayOfWeek dow = now.getDayOfWeek();
        if (dow.equals(DayOfWeek.FRIDAY)) 
            return now.plusDays(3);
        else if (dow.equals(DayOfWeek.SATURDAY))
            return now.plusDays(2);
        return  now.plusDays(1);
    });
}

轉換

java.util.Date 與 LocalDate、LocalTime、LocalDateTime 轉換

將Date轉換為LocalDate,LocalTime,LocalDateTime可以藉助於ZonedDateTime和Instant,實現如下:

Date date = new Date();
System.out.println("current date: " + date);

// Date -> LocalDateTime
LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
System.out.println("localDateTime by Instant: " + localDateTime);

// Date -> LocalDate
LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
System.out.println("localDate by Instant: " + localDate);
// Date -> LocalTime
LocalTime localTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalTime();
System.out.println("localTime by Instant: " + localTime);

//2. Date -> LocalDateTime
localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
System.out.println("localDateTime by ofInstant: " + localDateTime);

由於JDK8實現了向下相容,所以Date裡在JDK8版本引入了2個方法,from和
toInstant,所以我們可以藉助這兩個方法來實現LocalDateTime到Date的轉換。將LocalDateTime轉為Date如下:

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println("localDateTime: " + localDateTime);

// LocalDateTime -> Date
Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
System.out.println("LocalDateTime -> current date: " + date);

// LocalDate -> Date,時間預設都是00
LocalDate localDate = LocalDate.now();
date = Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
System.out.println("LocalDate -> current date: " + date);

日期與字串的轉換

通過LocalDate,LocalTime,LocalDateTime的parse方法和DateTimeFormatter來實現:

//字串->日期
LocalDate localDate = LocalDate.parse("2018-09-09", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
LocalDateTime localDateTime = LocalDateTime.parse("2018-09-10 12:12:12", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

//日期->字串
String localDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
String localDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
// 也可以通過DateTimeFormatter的format方法
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
localDateTime = dateTimeFormatter.format(LocalDateTime.now());

時間戳與LocalDateTime轉換

具體實現如下:

//時間戳->LocalDateTime
public static LocalDateTime convertToDate(long timestamp) {
   // ofEpochSecond 以秒為單位, ofEpochMilli 以毫秒為單位
   // Instant.ofEpochSecond(timestamp);
   Instant instant = Instant.ofEpochMilli(timestamp);
   return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}

//LocalDateTime->時間戳
public static long convertToTimestamp() {
   LocalDateTime localDateTime = LocalDateTime.now();
   return localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
}

總結

  • 所有新的日期時間 API 類都實現了一系列方法用以完成通用的任務,如:加、減、格式化、解析、從日期/時間中提取單獨部分。
  • Java 8 中新的時間與日期 API 中的所有類都是不可變且執行緒安全的,任何修改操作都會返回一個新的例項。
  • 新的 API 區分各種日期時間概念並且各個概念使用相似的方法定義模式,這種相似性非常有利於 API 的學習。總結一下一般的方法或者方法字首:
    • of:靜態工廠方法,用於建立例項
    • now:靜態工廠方法,用當前時間建立例項
    • parse:靜態工廠方法,從字串解析得到物件例項
    • get:獲取時間日期物件的部分狀態。
    • is:檢查某些東西的是否是 true,例如比較時間前後
    • with:返回一個部分狀態改變了的時間日期物件拷貝
    • plus:返回一個時間增加了的、時間日期物件拷貝
    • minus:返回一個時間減少了的、時間日期物件拷貝
    • to:轉換到另一個型別
    • at:把這個物件與另一個物件組合起來,例如 date.atTime(time)
    • format:提供格式化時間日期物件的能力

更多方法使用請參考Java api文件

參考文章:

http://www.importnew.com/14140.html

https://docs.oracle.com/javase/8/docs/api/

https://www.jianshu.com/p/f4abe1e38