Java Instant
Instant 是 Java 8 引入的類別,代表時間線上的一個瞬時點,以 Unix 紀元(1970-01-01T00:00:00Z)為基準的時間戳記。它不含時區資訊,適合用於記錄事件發生的精確時刻。
引入套件
import java.time.Instant;
建立 Instant
取得當前瞬時
Instant now = Instant.now();
System.out.println(now); // 2024-12-10T06:30:45.123456789Z
從時間戳記建立
// 從 Unix 秒數建立
Instant instant1 = Instant.ofEpochSecond(1702200000);
System.out.println(instant1); // 2023-12-10T08:00:00Z
// 從 Unix 毫秒數建立
Instant instant2 = Instant.ofEpochMilli(1702200000000L);
System.out.println(instant2); // 2023-12-10T08:00:00Z
// 秒數 + 奈秒調整
Instant instant3 = Instant.ofEpochSecond(1702200000, 500000000);
System.out.println(instant3); // 2023-12-10T08:00:00.500Z
從字串解析
Instant instant = Instant.parse("2024-12-25T14:30:00Z");
System.out.println(instant); // 2024-12-25T14:30:00Z
// ISO-8601 格式
Instant instant2 = Instant.parse("2024-12-25T14:30:00.123Z");
特殊常數
Instant epoch = Instant.EPOCH; // 1970-01-01T00:00:00Z
Instant min = Instant.MIN; // -1000000000-01-01T00:00:00Z
Instant max = Instant.MAX; // +1000000000-12-31T23:59:59.999999999Z
取得時間資訊
Instant instant = Instant.now();
// 取得 Unix 時間戳記
long epochSecond = instant.getEpochSecond(); // 秒數
long epochMilli = instant.toEpochMilli(); // 毫秒數
int nano = instant.getNano(); // 奈秒部分(0-999999999)
System.out.println("秒數: " + epochSecond);
System.out.println("毫秒數: " + epochMilli);
System.out.println("奈秒: " + nano);
時間運算
加減運算
Instant instant = Instant.now();
// 加上時間
Instant plus10Seconds = instant.plusSeconds(10);
Instant plus5Minutes = instant.plus(5, ChronoUnit.MINUTES);
Instant plus1Hour = instant.plus(Duration.ofHours(1));
Instant plusMillis = instant.plusMillis(500);
Instant plusNanos = instant.plusNanos(1000000);
// 減去時間
Instant minus30Seconds = instant.minusSeconds(30);
Instant minus1Day = instant.minus(1, ChronoUnit.DAYS);
使用 Duration
Instant instant = Instant.now();
Duration duration = Duration.ofHours(2).plusMinutes(30);
Instant later = instant.plus(duration);
Instant earlier = instant.minus(duration);
時間比較
Instant instant1 = Instant.now();
Instant instant2 = instant1.plusSeconds(60);
boolean isBefore = instant1.isBefore(instant2); // true
boolean isAfter = instant1.isAfter(instant2); // false
int result = instant1.compareTo(instant2); // 負數
計算時間差
Instant start = Instant.now();
// ... 執行一些操作 ...
Instant end = Instant.now();
// 使用 Duration
Duration duration = Duration.between(start, end);
long millis = duration.toMillis();
long nanos = duration.toNanos();
System.out.println("耗時: " + millis + " 毫秒");
// 使用 ChronoUnit
long seconds = ChronoUnit.SECONDS.between(start, end);
long minutes = ChronoUnit.MINUTES.between(start, end);
與其他類型轉換
轉換為 ZonedDateTime
Instant instant = Instant.now();
// 加上時區轉換為 ZonedDateTime
ZonedDateTime zdt = instant.atZone(ZoneId.of("Asia/Taipei"));
System.out.println(zdt); // 2024-12-10T14:30:45.123+08:00[Asia/Taipei]
ZonedDateTime zdtUtc = instant.atZone(ZoneId.of("UTC"));
System.out.println(zdtUtc); // 2024-12-10T06:30:45.123Z[UTC]
轉換為 OffsetDateTime
Instant instant = Instant.now();
OffsetDateTime odt = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt); // 2024-12-10T14:30:45.123+08:00
與 Date 互轉
// Instant 轉 Date
Instant instant = Instant.now();
Date date = Date.from(instant);
// Date 轉 Instant
Date oldDate = new Date();
Instant fromDate = oldDate.toInstant();
與 LocalDateTime 互轉
// Instant 轉 LocalDateTime(需要時區)
Instant instant = Instant.now();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.of("Asia/Taipei"));
// LocalDateTime 轉 Instant(需要時區)
LocalDateTime localDateTime = LocalDateTime.now();
Instant toInstant = localDateTime.atZone(ZoneId.of("Asia/Taipei")).toInstant();
時間截斷
Instant instant = Instant.parse("2024-12-25T14:30:45.123456789Z");
// 截斷到秒
Instant truncatedToSeconds = instant.truncatedTo(ChronoUnit.SECONDS);
System.out.println(truncatedToSeconds); // 2024-12-25T14:30:45Z
// 截斷到分鐘
Instant truncatedToMinutes = instant.truncatedTo(ChronoUnit.MINUTES);
System.out.println(truncatedToMinutes); // 2024-12-25T14:30:00Z
// 截斷到小時
Instant truncatedToHours = instant.truncatedTo(ChronoUnit.HOURS);
System.out.println(truncatedToHours); // 2024-12-25T14:00:00Z
實用範例
計時器
public class Timer {
private Instant startTime;
private Instant endTime;
public void start() {
startTime = Instant.now();
endTime = null;
}
public void stop() {
endTime = Instant.now();
}
public long getElapsedMillis() {
Instant end = (endTime != null) ? endTime : Instant.now();
return Duration.between(startTime, end).toMillis();
}
public String getElapsedFormatted() {
long millis = getElapsedMillis();
long seconds = millis / 1000;
long ms = millis % 1000;
return String.format("%d.%03d 秒", seconds, ms);
}
}
// 使用
Timer timer = new Timer();
timer.start();
// ... 執行操作 ...
timer.stop();
System.out.println("耗時: " + timer.getElapsedFormatted());
API 時間戳記
public class ApiResponse {
private String data;
private Instant timestamp;
public ApiResponse(String data) {
this.data = data;
this.timestamp = Instant.now();
}
public long getTimestampMillis() {
return timestamp.toEpochMilli();
}
public String getTimestampISO() {
return timestamp.toString();
}
}
ApiResponse response = new ApiResponse("Hello");
System.out.println("時間戳(毫秒): " + response.getTimestampMillis());
System.out.println("時間戳(ISO): " + response.getTimestampISO());
快取過期檢查
public class CacheEntry<T> {
private T value;
private Instant createdAt;
private Duration ttl;
public CacheEntry(T value, Duration ttl) {
this.value = value;
this.createdAt = Instant.now();
this.ttl = ttl;
}
public boolean isExpired() {
return Instant.now().isAfter(createdAt.plus(ttl));
}
public T getValue() {
return isExpired() ? null : value;
}
public long getRemainingSeconds() {
Instant expiresAt = createdAt.plus(ttl);
long remaining = Duration.between(Instant.now(), expiresAt).getSeconds();
return Math.max(0, remaining);
}
}
// 建立 5 分鐘過期的快取
CacheEntry<String> cache = new CacheEntry<>("cached data", Duration.ofMinutes(5));
System.out.println("剩餘時間: " + cache.getRemainingSeconds() + " 秒");
事件日誌
public class EventLog {
private String event;
private Instant timestamp;
public EventLog(String event) {
this.event = event;
this.timestamp = Instant.now();
}
@Override
public String toString() {
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")
.withZone(ZoneId.systemDefault());
return "[" + formatter.format(timestamp) + "] " + event;
}
}
List<EventLog> logs = new ArrayList<>();
logs.add(new EventLog("應用程式啟動"));
logs.add(new EventLog("使用者登入"));
logs.add(new EventLog("資料載入完成"));
logs.forEach(System.out::println);
Instant vs 其他日期時間類別
| 類別 | 用途 | 時區 |
|---|---|---|
Instant | 時間戳記、事件記錄 | UTC(無時區) |
LocalDateTime | 本地日期時間 | 無時區 |
ZonedDateTime | 完整日期時間 | 有時區 |
OffsetDateTime | 帶偏移的日期時間 | 有偏移量 |
重點整理
Instant代表 UTC 時間線上的一個瞬時點- 使用 Unix 紀元(1970-01-01T00:00:00Z)作為基準
- 適合用於時間戳記、計時、快取過期等場景
- 不含時區資訊,需要顯示本地時間時要轉換
- 精度可達奈秒(10⁻⁹ 秒)
- 是不可變(immutable)類別
- 資料庫儲存和 API 傳輸建議使用
Instant