1. 程式人生 > >Java 8 新特性(三)新的日期時間類

Java 8 新特性(三)新的日期時間類

老日期類

一說到在Java中處理日期和時間,我們首先想到的肯定是java.util.Date類。確實,由於很多教材和文獻都比較老,所以大多數人學習Java的時候使用的都是這個類。而且這個類確實在整個Java生態中擔任過重要的作用。

但是隨著時間推移,這個類已經完全不能滿足需要。首先,現在各種資料庫和程式語言都將日期和時間區分開,它們的儲存方式也不相同。但是java.util.Date類對此卻沒有做明確區分,這就導致有時候我們不得不手動區分。下面是Hibernate官方文件的一個例子,由於資料庫需要明確日期型別來進行儲存,所以在Java程式碼中必須顯式指定Temporal來區分型別。

@Entity
(name = "DateEvent") public static class DateEvent { @Id @GeneratedValue private Long id; @Column(name = "`timestamp`") @Temporal(TemporalType.DATE) private Date timestamp; //Getters and setters are omitted for brevity }

同樣的,舊日期類對於時區等特性也沒有很好的支援。這些問題都在新日期時間類中得到了解決。

新日期時間類

Java 8引入了三個新的日期時間類,分別是LocalDateLocalTimeLocalDateTime,分別處理日期、時間和日期時間。而且這些類的使用方法也是大同小異,基本上很快就能學會。

建立例項

要構造這些新類的例項也很簡單,有兩種方法,第一種是使用now()方法,會建立當前時間的例項。第二種是使用of方法,傳入要構造的引數。

//日期
LocalDate date = LocalDate.now();
System.out.println(date);
//時間
LocalTime time = LocalTime.now();
System.out.println(time);
//日期時間 LocalDateTime dateTime = LocalDateTime.now(); System.out.println(dateTime); LocalDate birthday = LocalDate.of(1994, Month.JANUARY, 18);

日期運算

新類附帶了很多新的日期運算方法,可以幫助我們進行很多計算。plusXXXminusXXX方法可以幫助我們計算一段時間之前/之後的日期和時間。withXXX方法需要TemporalAdjuster型別的物件,這個方法可以幫助我們確定今年的第幾天這樣的問題。TemporalAdjusters類包含了很多現成的例項,可供我們使用。當然如果壽星的話,我們還可以自己實現TemporalAdjuster介面。這裡的例子使用的是LocalDate,不過這些方法對於其他幾個類也同樣適用。

//日期運算
LocalDate date = LocalDate.of(1993, 12, 25);
//十天以後
LocalDate dateAfter10Days = date.plusDays(10);
System.out.println("十天以後:" + dateAfter10Days);
//一個月前
LocalDate dateBefore1Month = date.minusMonths(1);
System.out.println("一個月前:" + dateBefore1Month);
//這個月的最後一天
LocalDate lastDayOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());
System.out.println("這個月最後一天:" + lastDayOfMonth);
//今年第一天
System.out.println("今年第一天:" + date.with(TemporalAdjusters.firstDayOfYear()));

查詢

有時候需要對日期和時間進行比較複雜的查詢,這時候可以利用TemporalQuery<R>介面,它只有唯一一個查詢方法R queryFrom(TemporalAccessor temporal)。下面的例子利用這個介面和lambda表示式實現了一個查詢到年底還有幾天的查詢物件,然後呼叫query方法執行這個查詢並返回結果。

TemporalQuery<Integer> remainDaysOfYear = temporal -> {
    LocalDate d = LocalDate.from(temporal);
    LocalDate lastDayOfYear = d.with(TemporalAdjusters.lastDayOfYear());
    Period period = d.until(lastDayOfYear);
    return period.getDays();
};
int days = date.query(remainDaysOfYear);
System.out.println("到年底還剩幾天:" + days);

日期和字串轉換

這裡使用LocalDateTime作為例子,其他類的轉換方法也是類似的。用到的轉換類只有一個,那就是DateTimeFormatter

首先先由日期類轉換為字串,我們想到的最簡單的辦法就是使用toString()方法。注意到結果中日期和時間中使用T進行分隔,這是Java新標準的要求。

LocalDateTime datetime = LocalDateTime.now();
//預設toString方法
System.out.println(datetime);
// 2017-09-14T18:12:00.943

如果希望自行控制日期和時間的格式,就需要使用DateTimeFormatter了。最簡單的用法就是用ofPattern方法將格式傳入,這樣就會返回一個使用該格式的格式化器。然後用這個格式化起來進行格式化。

//手動格式化
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
System.out.println(
        datetime.format(formatter));
// 2017-09-14 18:12:00

最後就是字串到日期類的解析了。同樣需要的是格式化器,不過這次要呼叫格式化器的parse方法傳入字串序列,如果格式匹配的話,就會返回相應的日期物件,否則會丟擲異常。

datetime = LocalDateTime.from(formatter.parse("1993-11-25 12:00:15"));
System.out.println(datetime);

Instant類以及新老日期轉換

Instant類

舊的java.util.Date有一個接受long型別的建構函式,這個這個long型別的意義是當前日期距1970-01-01 00:00:00的毫秒數。新的Java 8 中專門有一個類對這個引數進行了抽象,這就是Instant類,而且把精確度提高到納秒級別。

新老日期轉換

Instant類一般不單獨使用,比較常見的用法就是在新老日期鍵進行轉換。Java 8為舊日期型別全部添加了toInstant()方法,可以將日期轉換為Instant例項,然後將Instant例項轉換為新日期型別。反過來也是一樣的。

//舊日期轉換為新日期
Date date = new Date();
LocalDateTime dateTime = LocalDateTime.ofInstant(date.toInstant()
        , ZoneId.of("Asia/Shanghai"));
System.out.println(dateTime);
//新日期轉換為舊日期
Date now = Date.from(Instant.now());
System.out.println(now);

JDBC支援

現在新的JDBC驅動基本上都已經支援插入和讀取新日期類了。不過具體到底那個版本支援還需要查閱其文件,基本上主流的最新的MySQL等資料庫肯定都支援了。

原來需要使用java.sql.Date來新增日期,由於它繼承自java.util.Date,也算一個過時類。如果要插入和讀取新的日期類物件,需要使用setObjectgetObject方法。

這裡用到了H2記憶體資料庫。執行這個例子可以看到,現在新的JDBC驅動都可以直接將新日期物件存入資料庫,並且可以直接取出。

public static void main(String[] args) throws SQLException {
    //使用H2嵌入式記憶體資料庫
    String url = "jdbc:h2:mem:test";
    try (Connection connection = DriverManager.getConnection(url)) {
        //建立表
        Statement statement = connection.createStatement();
        statement.execute("CREATE TABLE person(id INT PRIMARY KEY,name VARCHAR(255),birthday DATE)");
        //插入資料
        PreparedStatement p = connection.prepareStatement("INSERT INTO person VALUES(?,?,?)");
        p.setInt(1, 1);
        p.setString(2, "zhang3");
        p.setObject(3, LocalDate.of(1993, 5, 25));
        p.executeUpdate();
        //查詢資料
        ResultSet rs = statement.executeQuery("SELECT *FROM person WHERE id=1");
        rs.next();
        Person person = new Person();
        person.setId(rs.getInt(1));
        person.setName(rs.getNString(2));
        person.setBirthday(rs.getObject(3, LocalDate.class));
        System.out.println("資料庫中:" + person);
    }
}

這裡的Person是我編寫的一個小實體類,內部就這三個欄位,還有IDE自動生成的一堆樣板程式碼,就不放這個類了。

以上就是Java 8 對於新的日期時間類的內容。這些類可以幫助我們輕鬆處理時間有關問題。如果不是處理老舊專案的話,我們最好都是用這些新類,享受它們帶給我們的便利。