Java LocalDateTime

LocalDateTime 是 Java 8 引入的日期時間類別,結合了日期和時間,但不包含時區資訊,位於 java.time 套件中。

引入套件

import java.time.LocalDateTime;

建立 LocalDateTime

取得當前日期時間

LocalDateTime now = LocalDateTime.now();
System.out.println(now);  // 2024-12-10T14:30:45.123456789

指定日期時間

// of(年, 月, 日, 時, 分)
LocalDateTime dt1 = LocalDateTime.of(2024, 12, 25, 14, 30);
System.out.println(dt1);  // 2024-12-25T14:30

// of(年, 月, 日, 時, 分, 秒)
LocalDateTime dt2 = LocalDateTime.of(2024, 12, 25, 14, 30, 45);
System.out.println(dt2);  // 2024-12-25T14:30:45

// of(年, 月, 日, 時, 分, 秒, 奈秒)
LocalDateTime dt3 = LocalDateTime.of(2024, 12, 25, 14, 30, 45, 123456789);

// 使用 Month 列舉
LocalDateTime dt4 = LocalDateTime.of(2024, Month.DECEMBER, 25, 14, 30);

結合 LocalDate 和 LocalTime

LocalDate date = LocalDate.of(2024, 12, 25);
LocalTime time = LocalTime.of(14, 30, 45);

LocalDateTime dt1 = LocalDateTime.of(date, time);
LocalDateTime dt2 = date.atTime(time);
LocalDateTime dt3 = date.atTime(14, 30);
LocalDateTime dt4 = time.atDate(date);

System.out.println(dt1);  // 2024-12-25T14:30:45

從字串解析

LocalDateTime dt = LocalDateTime.parse("2024-12-25T14:30:45");
System.out.println(dt);  // 2024-12-25T14:30:45

// 自訂格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime dt2 = LocalDateTime.parse("2024/12/25 14:30:45", formatter);

取得日期時間資訊

LocalDateTime dt = LocalDateTime.of(2024, 12, 25, 14, 30, 45);

// 日期部分
int year = dt.getYear();             // 2024
int month = dt.getMonthValue();      // 12
Month monthEnum = dt.getMonth();     // DECEMBER
int day = dt.getDayOfMonth();        // 25
DayOfWeek dayOfWeek = dt.getDayOfWeek();  // WEDNESDAY
int dayOfYear = dt.getDayOfYear();   // 360

// 時間部分
int hour = dt.getHour();     // 14
int minute = dt.getMinute(); // 30
int second = dt.getSecond(); // 45
int nano = dt.getNano();     // 0

// 取得 LocalDate 和 LocalTime
LocalDate date = dt.toLocalDate();  // 2024-12-25
LocalTime time = dt.toLocalTime();  // 14:30:45

日期時間運算

加減運算

LocalDateTime dt = LocalDateTime.of(2024, 12, 25, 14, 30, 0);

// 加上時間
LocalDateTime plus1Year = dt.plusYears(1);       // 2025-12-25T14:30
LocalDateTime plus2Months = dt.plusMonths(2);    // 2025-02-25T14:30
LocalDateTime plus7Days = dt.plusDays(7);        // 2025-01-01T14:30
LocalDateTime plus3Hours = dt.plusHours(3);      // 2024-12-25T17:30
LocalDateTime plus30Mins = dt.plusMinutes(30);   // 2024-12-25T15:00
LocalDateTime plus45Secs = dt.plusSeconds(45);   // 2024-12-25T14:30:45

// 減去時間
LocalDateTime minus1Month = dt.minusMonths(1);   // 2024-11-25T14:30
LocalDateTime minus2Hours = dt.minusHours(2);    // 2024-12-25T12:30

使用 Period 和 Duration

LocalDateTime dt = LocalDateTime.of(2024, 12, 25, 14, 30, 0);

// Period(日期間隔)
Period period = Period.ofMonths(2);
LocalDateTime newDt1 = dt.plus(period);  // 2025-02-25T14:30

// Duration(時間間隔)
Duration duration = Duration.ofHours(5);
LocalDateTime newDt2 = dt.plus(duration);  // 2024-12-25T19:30

修改日期時間

LocalDateTime dt = LocalDateTime.of(2024, 12, 25, 14, 30, 45);

// 修改日期部分
LocalDateTime newYear = dt.withYear(2025);       // 2025-12-25T14:30:45
LocalDateTime newMonth = dt.withMonth(6);        // 2024-06-25T14:30:45
LocalDateTime newDay = dt.withDayOfMonth(1);     // 2024-12-01T14:30:45

// 修改時間部分
LocalDateTime newHour = dt.withHour(10);         // 2024-12-25T10:30:45
LocalDateTime newMinute = dt.withMinute(0);      // 2024-12-25T14:00:45
LocalDateTime newSecond = dt.withSecond(0);      // 2024-12-25T14:30:00

日期時間比較

LocalDateTime dt1 = LocalDateTime.of(2024, 12, 25, 14, 30);
LocalDateTime dt2 = LocalDateTime.of(2024, 12, 31, 18, 0);

boolean isBefore = dt1.isBefore(dt2);  // true
boolean isAfter = dt1.isAfter(dt2);    // false
boolean isEqual = dt1.isEqual(dt2);    // false

int result = dt1.compareTo(dt2);  // 負數(dt1 較早)

計算間隔

LocalDateTime start = LocalDateTime.of(2024, 1, 1, 9, 0);
LocalDateTime end = LocalDateTime.of(2024, 12, 31, 18, 0);

// 使用 Duration(適合時間間隔)
Duration duration = Duration.between(start, end);
long hours = duration.toHours();    // 8769
long minutes = duration.toMinutes(); // 526140

// 使用 ChronoUnit
long days = ChronoUnit.DAYS.between(start, end);       // 365
long hours2 = ChronoUnit.HOURS.between(start, end);    // 8769
long months = ChronoUnit.MONTHS.between(start, end);   // 11

時間調節器

import java.time.temporal.TemporalAdjusters;

LocalDateTime dt = LocalDateTime.of(2024, 12, 15, 14, 30);

// 該月第一天
LocalDateTime firstDay = dt.with(TemporalAdjusters.firstDayOfMonth());  // 2024-12-01T14:30

// 該月最後一天
LocalDateTime lastDay = dt.with(TemporalAdjusters.lastDayOfMonth());    // 2024-12-31T14:30

// 下一個星期一
LocalDateTime nextMonday = dt.with(TemporalAdjusters.next(DayOfWeek.MONDAY));

// 設定時間為當天開始
LocalDateTime startOfDay = dt.toLocalDate().atStartOfDay();  // 2024-12-15T00:00

格式化輸出

LocalDateTime dt = LocalDateTime.of(2024, 12, 25, 14, 30, 45);

// 預設格式
System.out.println(dt.toString());  // 2024-12-25T14:30:45

// 自訂格式
DateTimeFormatter f1 = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
System.out.println(dt.format(f1));  // 2024/12/25 14:30:45

DateTimeFormatter f2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH時mm分ss秒");
System.out.println(dt.format(f2));  // 2024年12月25日 14時30分45秒

DateTimeFormatter f3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm a");
System.out.println(dt.format(f3));  // 2024-12-25 02:30 PM

轉換為其他類型

LocalDateTime dt = LocalDateTime.of(2024, 12, 25, 14, 30, 45);

// 轉換為 ZonedDateTime(加上時區)
ZonedDateTime zdt = dt.atZone(ZoneId.of("Asia/Taipei"));

// 轉換為 OffsetDateTime
OffsetDateTime odt = dt.atOffset(ZoneOffset.ofHours(8));

// 轉換為 Instant(需要時區)
Instant instant = dt.atZone(ZoneId.systemDefault()).toInstant();

// 轉換為 Epoch 秒數
long epochSecond = dt.atZone(ZoneId.systemDefault()).toEpochSecond();

// 從 Epoch 毫秒數建立
LocalDateTime fromEpoch = LocalDateTime.ofInstant(
    Instant.ofEpochMilli(System.currentTimeMillis()),
    ZoneId.systemDefault()
);

實用範例

計算會議結束時間

public static LocalDateTime calculateEndTime(LocalDateTime start, int durationMinutes) {
    return start.plusMinutes(durationMinutes);
}

LocalDateTime meetingStart = LocalDateTime.of(2024, 12, 25, 14, 0);
LocalDateTime meetingEnd = calculateEndTime(meetingStart, 90);
System.out.println("會議結束時間: " + meetingEnd);  // 2024-12-25T15:30

判斷是否在特定時間範圍內

public static boolean isWithinRange(LocalDateTime target, 
                                     LocalDateTime start, 
                                     LocalDateTime end) {
    return !target.isBefore(start) && !target.isAfter(end);
}

LocalDateTime event = LocalDateTime.of(2024, 12, 25, 15, 0);
LocalDateTime rangeStart = LocalDateTime.of(2024, 12, 25, 14, 0);
LocalDateTime rangeEnd = LocalDateTime.of(2024, 12, 25, 16, 0);

System.out.println(isWithinRange(event, rangeStart, rangeEnd));  // true

格式化為相對時間

public static String formatRelativeTime(LocalDateTime dateTime) {
    LocalDateTime now = LocalDateTime.now();
    Duration duration = Duration.between(dateTime, now);
    
    if (duration.isNegative()) {
        return "未來";
    }
    
    long seconds = duration.getSeconds();
    if (seconds < 60) return "剛剛";
    if (seconds < 3600) return (seconds / 60) + " 分鐘前";
    if (seconds < 86400) return (seconds / 3600) + " 小時前";
    if (seconds < 604800) return (seconds / 86400) + " 天前";
    
    return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}

取得本週的開始和結束

public static LocalDateTime getStartOfWeek(LocalDateTime dt) {
    return dt.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
             .toLocalDate()
             .atStartOfDay();
}

public static LocalDateTime getEndOfWeek(LocalDateTime dt) {
    return dt.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY))
             .toLocalDate()
             .atTime(23, 59, 59);
}

LocalDateTime now = LocalDateTime.now();
System.out.println("本週開始: " + getStartOfWeek(now));
System.out.println("本週結束: " + getEndOfWeek(now));

重點整理

  • LocalDateTime 結合日期和時間,但不含時區
  • 不可變(immutable)類別
  • 使用 now() 取得當前日期時間
  • 可由 LocalDate + LocalTime 組合而成
  • 使用 plus/minus 方法進行日期時間運算
  • 需要時區資訊時,轉換為 ZonedDateTime
  • 搭配 DateTimeFormatter 格式化輸出