日期與即時值和即時值與日期轉換器簡介
1. 概述
在現代 Java 應用程式中,我們經常同時使用傳統的Date API 和java.time套件中的新型Instant類別。雖然 Java 8 的 Date-Time API 在清晰度、不可變性和線程安全性方面有了顯著提升,但許多現有的程式庫和框架仍然依賴Date.
在本教程中,我們將探討如何在Date和Instant之間進行轉換,討論精確度差異,並提供完整的 JUnit 5 測試來驗證該行為。
2. 理解Date和Instant
Date類別表示一個特定的時間點,內部儲存的是自 Unix 紀元(1970 年 1 月 1 日 00:00:00 UTC)以來的毫秒數。但是,它是可變的,並且是舊版 API 的一部分。
Java 8 中引入的Instant類,以奈秒精度表示 UTC 時間軸上的一個時刻。它不可變且線程安全,因此是現代應用程式的首選。
這兩個類別都代表基於紀元值的某個時間點。主要區別在於 API 設計和精度處理方式。
3. 將Date轉換為Instant
Java 8 引進了使用toInstant()方法直接將Date轉換為Instant方法。為了保持程式碼簡潔和可重複使用性,我們將轉換邏輯集中在一個工具類別中:
public final class DateInstantConverter {
public static Instant toInstant(Date date) {
if (date == null) {
return null;
}
return date.toInstant();
}
// ...
}
這個工具類別隔離了轉換邏輯,使我們的領域程式碼保持簡潔。它還確保了應用程式中空值處理的一致性。
現在我們驗證將Date轉換為Instant是否保留了時間戳毫秒數:
@Test
void shouldConvertDateToInstant() {
Date date = new Date(1708752000000L);
Instant instant = DateInstantConverter.toInstant(date);
assertNotNull(instant);
assertEquals(date.getTime(), instant.toEpochMilli());
}
此測試確認兩個物件表示相同的時間戳毫秒值。毫秒值匹配可確保轉換正確無誤。
在實際應用中,轉換方法可能會接收到空值,因此我們編寫了這個測試案例來驗證轉換是否對空值安全:
@Test
void shouldReturnNullWhenDateIsNull() {
Instant instant = DateInstantConverter.toInstant(null);
assertNull(instant);
}
它避免了跨服務層不必要的空值檢查重複。
4. 將Instant資料轉換為Date
要將Instant類型轉換回Date ,我們使用靜態方法Date.from() 。在同一個工具類別中,我們有一個執行轉換的方法:
public final class DateInstantConverter {
// ...
public static Date toDate(Instant instant) {
if (instant == null) {
return null;
}
return Date.from(instant);
}
}
由於這兩個類別都表示基於紀元的時間點,因此轉換是對稱的。讓我們驗證一下轉換結果:
@Test
void shouldConvertInstantToDate() {
Instant instant = Instant.ofEpochMilli(1708752000000L);
Date date = DateInstantConverter.toDate(instant);
assertNotNull(date);
assertEquals(instant.toEpochMilli(), date.getTime());
}
此測試驗證了轉換過程中時間戳毫秒值是否保持一致。只要毫秒精度得以維持,其行為就是可預測的。
與上一節類似,為了檢查我們的轉換是否支援空值,我們有以下測試案例:
@Test
void shouldReturnNullWhenInstantIsNull() {
Date date = DateInstantConverter.toDate(null);
assertNull(date);
}
同樣,這種方法避免了在服務層之間進行不必要的空值檢查重複。
5. Date和Instant的精度差異
Date和Instant的主要區別在於精度。 Date Date毫秒為單位儲存時間,而Instant則以秒和奈秒為單位儲存時間。從Instant轉換為Date時,超出毫秒精度的奈秒會被截斷。
為了驗證毫秒精度,我們執行往返轉換:
@Test
void shouldPreserveMillisecondPrecisionInRoundTrip() {
Instant originalInstant = Instant.now();
Date date = DateInstantConverter.toDate(originalInstant);
Instant convertedBack = DateInstantConverter.toInstant(date);
assertEquals(originalInstant.toEpochMilli(), convertedBack.toEpochMilli());
}
此測試證實,將Instant轉換為Date再轉換回Instant可以保持毫秒精度。我們刻意比較的是 Unix 時間戳記的毫秒數,而不是直接比較物件相等性。
我們也可以明確地驗證截斷行為:
@Test
void shouldTruncateNanosecondsWhenConvertingToDate() {
Instant instantWithNanos = Instant.ofEpochSecond(1000, 123456789);
Date date = DateInstantConverter.toDate(instantWithNanos);
Instant convertedBack = DateInstantConverter.toInstant(date);
assertEquals(instantWithNanos.toEpochMilli(), convertedBack.toEpochMilli());
}
在此,轉換過程中會移除毫秒精度以外的奈秒誤差。這是預期行為,在高精度系統中必須加以考慮。
在現代應用中,我們應該在領域模型和業務邏輯中優先選擇 Instant。
我們應該僅在整合邊界處將其轉換為Date ,例如需要 Date 類型的舊版 API 或資料庫驅動程式。
將轉換邏輯集中化,並透過適當的單元測試來驗證其行為,也是一種好的做法。
在測試中比較時間戳毫秒數可以避免精度相關的問題。
7. 結論
本文介紹了在 Java 8 及更高版本中, Date和Instant之間的轉換非常簡單。要將Date轉換為Instant ,可以使用toInstant()方法;要將Instant轉換為Date ,可以使用Date.from()方法。雖然這兩個類別都表示時間軸上的同一時刻,但它們的精度模型不同。 Date 僅支援毫秒精度,而Instant支援奈秒精度。透過使用適當的 JUnit 測試來驗證這些轉換,我們可以確保轉換Date正確性,並防止生產系統中出現難以察覺的精確度相關問題。
與往常一樣,本文中提供的程式碼可 在 GitHub 上找到。