Java 8日期时间API完全指南:告别Date和Calendar的混乱时代

引言:为什么需要新的日期时间API?

如果你曾经使用过Java的java.util.Datejava.util.Calendar,一定体会过那种混乱和痛苦:月份从0开始、Date既包含日期又包含时间、SimpleDateFormat线程不安全、时区处理复杂... Java 8引入的全新日期时间API彻底解决了这些问题,带来了现代化、易于使用且线程安全的时间处理方式。

新旧API对比:一场革命

传统API的问题

java 复制代码
import java.util.*;
import java.text.SimpleDateFormat;

public class LegacyDateTimeProblems {
    public static void main(String[] args) throws Exception {
        // 问题1: Date的月份从0开始(0=一月,11=十二月)
        Date date = new Date(2023, 10, 15); // 这实际上是2023年11月15日!
        System.out.println("令人困惑的月份: " + date);
        
        // 问题2: SimpleDateFormat线程不安全
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date parsedDate = sdf.parse("2023-11-15");
        System.out.println("解析后的日期: " + parsedDate);
        
        // 问题3: Calendar操作繁琐且容易出错
        Calendar calendar = Calendar.getInstance();
        calendar.set(2023, Calendar.NOVEMBER, 15); // 注意:这里月份是实际的11月
        calendar.add(Calendar.DAY_OF_MONTH, 7);
        System.out.println("7天后: " + calendar.getTime());
        
        // 问题4: Date同时包含日期和时间,但经常被误用
        Date now = new Date();
        System.out.println("Date包含时间: " + now);
        
        // 问题5: 时区处理复杂
        TimeZone tz = TimeZone.getTimeZone("America/New_York");
        calendar.setTimeZone(tz);
        System.out.println("纽约时间: " + calendar.getTime());
    }
}

Java 8日期时间API的核心优势

  • 不可变性:所有日期时间对象都是不可变的,线程安全
  • 清晰的设计:分离了日期、时间、日期时间等概念
  • 流畅的API:链式调用,代码更易读
  • 强大的时区支持:内置完善的时区处理
  • 更好的扩展性:支持自定义日历系统

核心类概览

java 复制代码
Java 8日期时间API主要类:
├── 基本日期时间类
│   ├── LocalDate       (日期:年-月-日)
│   ├── LocalTime       (时间:时-分-秒-纳秒)
│   ├── LocalDateTime   (日期时间)
│   ├── ZonedDateTime   (带时区的日期时间)
│   └── OffsetDateTime  (带偏移量的日期时间)
│
├── 时间点与持续时间
│   ├── Instant         (时间戳,基于Unix时间)
│   ├── Duration        (时间段,基于时间)
│   └── Period          (时间段,基于日期)
│
├── 辅助类
│   ├── DateTimeFormatter (日期时间格式化)
│   ├── ZoneId          (时区ID)
│   ├── ZoneOffset      (时区偏移量)
│   └── Chronology      (年表,支持不同日历系统)
│
└── 时间调整器
    ├── TemporalAdjuster (时间调整器)
    └── TemporalAdjusters (预定义调整器)

LocalDate:处理日期

LocalDate基础操作

java 复制代码
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;

public class LocalDateExample {
    
    public static void main(String[] args) {
        System.out.println("=== LocalDate 基础操作 ===");
        
        // 1. 创建LocalDate的多种方式
        LocalDate today = LocalDate.now();
        LocalDate specificDate = LocalDate.of(2023, 11, 15);
        LocalDate parsedDate = LocalDate.parse("2023-11-15");
        
        System.out.println("今天: " + today);
        System.out.println("指定日期: " + specificDate);
        System.out.println("解析日期: " + parsedDate);
        
        // 2. 获取日期各部分
        System.out.println("\n日期分解:");
        System.out.println("年: " + today.getYear());
        System.out.println("月: " + today.getMonth() + " (数值: " + today.getMonthValue() + ")");
        System.out.println("日: " + today.getDayOfMonth());
        System.out.println("星期: " + today.getDayOfWeek());
        System.out.println("一年中的第几天: " + today.getDayOfYear());
        
        // 3. 日期运算
        System.out.println("\n日期运算:");
        System.out.println("明天: " + today.plusDays(1));
        System.out.println("一周后: " + today.plusWeeks(1));
        System.out.println("一月后: " + today.plusMonths(1));
        System.out.println("一年后: " + today.plusYears(1));
        
        System.out.println("昨天: " + today.minusDays(1));
        System.out.println("一周前: " + today.minusWeeks(1));
        System.out.println("一月前: " + today.minusMonths(1));
        System.out.println("一年前: " + today.minusYears(1));
        
        // 4. 日期比较
        LocalDate otherDate = LocalDate.of(2023, 12, 25);
        System.out.println("\n日期比较:");
        System.out.println(today + " 在 " + otherDate + " 之前? " + today.isBefore(otherDate));
        System.out.println(today + " 在 " + otherDate + " 之后? " + today.isAfter(otherDate));
        System.out.println(today + " 等于 " + otherDate + "? " + today.isEqual(otherDate));
        
        // 5. 特殊日期
        System.out.println("\n特殊日期:");
        System.out.println("是否是闰年? " + today.isLeapYear());
        System.out.println("当月天数: " + today.lengthOfMonth());
        System.out.println("当年天数: " + today.lengthOfYear());
        
        // 6. 日期调整
        System.out.println("\n日期调整:");
        System.out.println("当月第一天: " + today.with(TemporalAdjusters.firstDayOfMonth()));
        System.out.println("当月最后一天: " + today.with(TemporalAdjusters.lastDayOfMonth()));
        System.out.println("下个月第一天: " + today.with(TemporalAdjusters.firstDayOfNextMonth()));
        System.out.println("下个周一: " + today.with(TemporalAdjusters.next(DayOfWeek.MONDAY)));
        System.out.println("当月最后一个周五: " + today.with(TemporalAdjusters.lastInMonth(DayOfWeek.FRIDAY)));
        
        // 7. 自定义调整器
        TemporalAdjuster nextWorkingDay = TemporalAdjusters.ofDateAdjuster(date -> {
            DayOfWeek dayOfWeek = date.getDayOfWeek();
            if (dayOfWeek == DayOfWeek.FRIDAY) {
                return date.plusDays(3); // 周五到下周一
            } else if (dayOfWeek == DayOfWeek.SATURDAY) {
                return date.plusDays(2); // 周六到下周一
            } else {
                return date.plusDays(1); // 其他日子到明天
            }
        });
        System.out.println("下一个工作日: " + today.with(nextWorkingDay));
    }
    
    // 实际应用:计算年龄
    public static int calculateAge(LocalDate birthDate) {
        return Period.between(birthDate, LocalDate.now()).getYears();
    }
    
    // 实际应用:计算工作日
    public static long calculateWorkingDays(LocalDate start, LocalDate end) {
        return start.datesUntil(end.plusDays(1))
                   .filter(date -> date.getDayOfWeek() != DayOfWeek.SATURDAY &&
                                  date.getDayOfWeek() != DayOfWeek.SUNDAY)
                   .count();
    }
    
    // 实际应用:生成日期范围
    public static List<LocalDate> generateDateRange(LocalDate start, LocalDate end, int stepDays) {
        List<LocalDate> dates = new ArrayList<>();
        LocalDate current = start;
        while (!current.isAfter(end)) {
            dates.add(current);
            current = current.plusDays(stepDays);
        }
        return dates;
    }
}

LocalTime:处理时间

LocalTime基础操作

java 复制代码
import java.time.*;
import java.time.temporal.ChronoUnit;

public class LocalTimeExample {
    
    public static void main(String[] args) {
        System.out.println("=== LocalTime 基础操作 ===");
        
        // 1. 创建LocalTime的多种方式
        LocalTime now = LocalTime.now();
        LocalTime specificTime = LocalTime.of(14, 30, 45); // 14:30:45
        LocalTime parsedTime = LocalTime.parse("09:15:30");
        
        System.out.println("现在时间: " + now);
        System.out.println("指定时间: " + specificTime);
        System.out.println("解析时间: " + parsedTime);
        
        // 2. 获取时间各部分
        System.out.println("\n时间分解:");
        System.out.println("小时: " + now.getHour());
        System.out.println("分钟: " + now.getMinute());
        System.out.println("秒: " + now.getSecond());
        System.out.println("纳秒: " + now.getNano());
        
        // 3. 时间运算
        System.out.println("\n时间运算:");
        System.out.println("1小时后: " + now.plusHours(1));
        System.out.println("30分钟后: " + now.plusMinutes(30));
        System.out.println("15秒后: " + now.plusSeconds(15));
        System.out.println("100纳秒后: " + now.plusNanos(100));
        
        System.out.println("2小时前: " + now.minusHours(2));
        System.out.println("45分钟前: " + now.minusMinutes(45));
        System.out.println("30秒前: " + now.minusSeconds(30));
        
        // 使用ChronoUnit进行更灵活的时间运算
        System.out.println("半小时后: " + now.plus(30, ChronoUnit.MINUTES));
        System.out.println("三刻钟后: " + now.plus(3, ChronoUnit.HALF_HOURS));
        
        // 4. 时间比较
        LocalTime otherTime = LocalTime.of(18, 0, 0);
        System.out.println("\n时间比较:");
        System.out.println(now + " 在 " + otherTime + " 之前? " + now.isBefore(otherTime));
        System.out.println(now + " 在 " + otherTime + " 之后? " + now.isAfter(otherTime));
        
        // 5. 时间调整
        System.out.println("\n时间调整:");
        System.out.println("调整到整点: " + now.withMinute(0).withSecond(0).withNano(0));
        System.out.println("调整到下一小时: " + now.withHour((now.getHour() + 1) % 24));
        
        // 6. 获取最大/最小时间
        System.out.println("\n特殊时间:");
        System.out.println("最小时间: " + LocalTime.MIN);
        System.out.println("最大时间: " + LocalTime.MAX);
        System.out.println("午夜: " + LocalTime.MIDNIGHT);
        System.out.println("中午: " + LocalTime.NOON);
        
        // 7. 时间范围检查
        System.out.println("\n时间范围检查:");
        LocalTime startWork = LocalTime.of(9, 0);
        LocalTime endWork = LocalTime.of(18, 0);
        System.out.println(now + " 是否在工作时间内? " + 
                          (now.isAfter(startWork) && now.isBefore(endWork)));
        
        // 8. 时间差计算
        LocalTime start = LocalTime.of(9, 0);
        LocalTime end = LocalTime.of(17, 30);
        System.out.println("\n时间差计算:");
        long hoursBetween = ChronoUnit.HOURS.between(start, end);
        long minutesBetween = ChronoUnit.MINUTES.between(start, end);
        System.out.println(start + " 到 " + end + " 相差 " + hoursBetween + " 小时");
        System.out.println(start + " 到 " + end + " 相差 " + minutesBetween + " 分钟");
    }
    
    // 实际应用:计算会议持续时间
    public static Duration calculateMeetingDuration(LocalTime startTime, LocalTime endTime) {
        return Duration.between(startTime, endTime);
    }
    
    // 实际应用:检查是否在营业时间
    public static boolean isBusinessHour(LocalTime time, LocalTime open, LocalTime close) {
        return !time.isBefore(open) && !time.isAfter(close);
    }
    
    // 实际应用:生成时间表
    public static List<LocalTime> generateTimeSchedule(LocalTime start, int intervalMinutes, int count) {
        List<LocalTime> schedule = new ArrayList<>();
        LocalTime current = start;
        for (int i = 0; i < count; i++) {
            schedule.add(current);
            current = current.plusMinutes(intervalMinutes);
        }
        return schedule;
    }
}

LocalDateTime:日期时间组合

LocalDateTime基础操作

java 复制代码
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;

public class LocalDateTimeExample {
    
    public static void main(String[] args) {
        System.out.println("=== LocalDateTime 基础操作 ===");
        
        // 1. 创建LocalDateTime的多种方式
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime specificDateTime = LocalDateTime.of(2023, 11, 15, 14, 30, 45);
        LocalDateTime parsedDateTime = LocalDateTime.parse("2023-11-15T14:30:45");
        LocalDateTime fromDateAndTime = LocalDateTime.of(LocalDate.now(), LocalTime.now());
        
        System.out.println("现在: " + now);
        System.out.println("指定日期时间: " + specificDateTime);
        System.out.println("解析日期时间: " + parsedDateTime);
        System.out.println("从日期和时间组合: " + fromDateAndTime);
        
        // 2. 转换操作
        System.out.println("\n转换操作:");
        LocalDate datePart = now.toLocalDate();
        LocalTime timePart = now.toLocalTime();
        System.out.println("日期部分: " + datePart);
        System.out.println("时间部分: " + timePart);
        
        // 3. 日期时间运算
        System.out.println("\n日期时间运算:");
        System.out.println("3天后: " + now.plusDays(3));
        System.out.println("2周后: " + now.plusWeeks(2));
        System.out.println("6个月后: " + now.plusMonths(6));
        System.out.println("1年2个月3天后: " + now.plusYears(1).plusMonths(2).plusDays(3));
        
        System.out.println("5小时前: " + now.minusHours(5));
        System.out.println("30分钟前: " + now.minusMinutes(30));
        
        // 4. 日期时间比较
        LocalDateTime futureDateTime = LocalDateTime.of(2024, 1, 1, 0, 0, 0);
        System.out.println("\n日期时间比较:");
        System.out.println(now + " 在 " + futureDateTime + " 之前? " + now.isBefore(futureDateTime));
        System.out.println(now + " 在 " + futureDateTime + " 之后? " + now.isAfter(futureDateTime));
        
        // 5. 日期时间调整
        System.out.println("\n日期时间调整:");
        System.out.println("调整到当月第一天: " + now.with(TemporalAdjusters.firstDayOfMonth()));
        System.out.println("调整到下个周一上午9点: " + 
            now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).withHour(9).withMinute(0));
        
        // 6. 格式化与解析
        System.out.println("\n格式化与解析:");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        String formatted = now.format(formatter);
        System.out.println("格式化后: " + formatted);
        
        LocalDateTime parsed = LocalDateTime.parse("2023年11月15日 14:30:00", formatter);
        System.out.println("解析后: " + parsed);
        
        // 7. 自定义格式化器
        System.out.println("\n自定义格式化:");
        DateTimeFormatter customFormatter = new DateTimeFormatterBuilder()
            .appendPattern("yyyy/MM/dd")
            .appendLiteral(" ")
            .appendPattern("HH:mm")
            .toFormatter();
        
        System.out.println("自定义格式: " + now.format(customFormatter));
        
        // 8. 与时间戳的转换
        System.out.println("\n与时间戳转换:");
        Instant instant = now.atZone(ZoneId.systemDefault()).toInstant();
        long epochMilli = instant.toEpochMilli();
        System.out.println("时间戳: " + epochMilli);
        
        LocalDateTime fromTimestamp = LocalDateTime.ofInstant(
            Instant.ofEpochMilli(epochMilli), 
            ZoneId.systemDefault()
        );
        System.out.println("从时间戳恢复: " + fromTimestamp);
    }
    
    // 实际应用:计算项目截止日期
    public static LocalDateTime calculateDeadline(LocalDateTime startDate, int workingDays) {
        LocalDateTime deadline = startDate;
        int daysAdded = 0;
        
        while (daysAdded < workingDays) {
            deadline = deadline.plusDays(1);
            DayOfWeek dayOfWeek = deadline.getDayOfWeek();
            if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) {
                daysAdded++;
            }
        }
        return deadline;
    }
    
    // 实际应用:检查是否在维护窗口
    public static boolean isInMaintenanceWindow(LocalDateTime dateTime) {
        LocalTime start = LocalTime.of(2, 0); // 凌晨2点
        LocalTime end = LocalTime.of(4, 0);   // 凌晨4点
        DayOfWeek day = dateTime.getDayOfWeek();
        
        // 假设每周日2-4点维护
        return day == DayOfWeek.SUNDAY && 
               !dateTime.toLocalTime().isBefore(start) && 
               dateTime.toLocalTime().isBefore(end);
    }
    
    // 实际应用:生成时间轴
    public static Map<LocalDateTime, String> generateTimeline(LocalDateTime start, int intervalHours, int count) {
        Map<LocalDateTime, String> timeline = new LinkedHashMap<>();
        LocalDateTime current = start;
        
        for (int i = 0; i < count; i++) {
            timeline.put(current, "事件 " + (i + 1));
            current = current.plusHours(intervalHours);
        }
        
        return timeline;
    }
}

时区处理:ZonedDateTime和OffsetDateTime

时区处理实战

java 复制代码
import java.time.*;
import java.time.format.*;
import java.util.*;

public class TimeZoneExample {
    
    public static void main(String[] args) {
        System.out.println("=== 时区处理实战 ===");
        
        // 1. 时区基础
        System.out.println("\n1. 时区基础:");
        Set<String> allZoneIds = ZoneId.getAvailableZoneIds();
        System.out.println("可用时区数量: " + allZoneIds.size());
        System.out.println("前10个时区:");
        allZoneIds.stream().sorted().limit(10).forEach(System.out::println);
        
        // 2. 创建带时区的时间
        System.out.println("\n2. 创建带时区的时间:");
        ZonedDateTime nowInTokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
        ZonedDateTime nowInNewYork = ZonedDateTime.now(ZoneId.of("America/New_York"));
        ZonedDateTime nowInLondon = ZonedDateTime.now(ZoneId.of("Europe/London"));
        
        System.out.println("东京时间: " + nowInTokyo);
        System.out.println("纽约时间: " + nowInNewYork);
        System.out.println("伦敦时间: " + nowInLondon);
        
        // 3. 时区转换
        System.out.println("\n3. 时区转换:");
        ZonedDateTime tokyoTime = ZonedDateTime.of(
            LocalDateTime.of(2023, 11, 15, 14, 30),
            ZoneId.of("Asia/Tokyo")
        );
        
        ZonedDateTime newYorkTime = tokyoTime.withZoneSameInstant(ZoneId.of("America/New_York"));
        ZonedDateTime londonTime = tokyoTime.withZoneSameInstant(ZoneId.of("Europe/London"));
        
        System.out.println("东京 14:30 相当于:");
        System.out.println("  纽约: " + newYorkTime.toLocalDateTime() + " (" + newYorkTime.getZone() + ")");
        System.out.println("  伦敦: " + londonTime.toLocalDateTime() + " (" + londonTime.getZone() + ")");
        
        // 4. 夏令时处理
        System.out.println("\n4. 夏令时测试:");
        testDaylightSavingTime();
        
        // 5. OffsetDateTime(固定时区偏移)
        System.out.println("\n5. OffsetDateTime:");
        OffsetDateTime offsetDateTime = OffsetDateTime.now(ZoneOffset.ofHours(8));
        System.out.println("UTC+8时间: " + offsetDateTime);
        
        // 6. 时区偏移计算
        System.out.println("\n6. 时区偏移计算:");
        ZoneId tokyoZone = ZoneId.of("Asia/Tokyo");
        ZoneId newYorkZone = ZoneId.of("America/New_York");
        
        Instant instant = Instant.now();
        ZoneOffset tokyoOffset = tokyoZone.getRules().getOffset(instant);
        ZoneOffset newYorkOffset = newYorkZone.getRules().getOffset(instant);
        
        System.out.println("东京时区偏移: " + tokyoOffset);
        System.out.println("纽约时区偏移: " + newYorkOffset);
        System.out.println("时差: " + 
            Duration.between(
                instant.atOffset(tokyoOffset).toLocalTime(),
                instant.atOffset(newYorkOffset).toLocalTime()
            ).toHours() + " 小时"
        );
        
        // 7. 实际应用:全球会议时间
        System.out.println("\n7. 实际应用:全球会议时间安排:");
        scheduleGlobalMeeting();
    }
    
    // 测试夏令时
    public static void testDaylightSavingTime() {
        ZoneId nyZone = ZoneId.of("America/New_York");
        
        // 纽约2023年夏令时开始于3月12日
        LocalDateTime beforeDST = LocalDateTime.of(2023, 3, 12, 1, 30);
        LocalDateTime afterDST = LocalDateTime.of(2023, 3, 12, 3, 30);
        
        ZonedDateTime zonedBefore = ZonedDateTime.of(beforeDST, nyZone);
        ZonedDateTime zonedAfter = ZonedDateTime.of(afterDST, nyZone);
        
        System.out.println("夏令时变化前: " + zonedBefore);
        System.out.println("夏令时变化后: " + zonedAfter);
        System.out.println("是否在夏令时中: " + zonedAfter.getZone().getRules().isDaylightSavings(zonedAfter.toInstant()));
        
        // 检查转换
        System.out.println("\n验证转换:");
        System.out.println("转换到伦敦时间:");
        System.out.println("  前: " + zonedBefore.withZoneSameInstant(ZoneId.of("Europe/London")));
        System.out.println("  后: " + zonedAfter.withZoneSameInstant(ZoneId.of("Europe/London")));
    }
    
    // 安排全球会议
    public static void scheduleGlobalMeeting() {
        System.out.println("=== 全球会议安排 ===");
        
        // 假设会议在旧金山时间上午9点
        LocalDateTime meetingTime = LocalDateTime.of(2023, 11, 15, 9, 0);
        ZonedDateTime meetingSF = ZonedDateTime.of(meetingTime, ZoneId.of("America/Los_Angeles"));
        
        System.out.println("会议时间(旧金山): " + meetingSF);
        
        // 转换到其他城市
        String[] cities = {
            "Asia/Shanghai",      // 上海
            "Asia/Tokyo",         // 东京
            "Europe/London",      // 伦敦
            "Europe/Paris",       // 巴黎
            "America/New_York",   // 纽约
            "Australia/Sydney"    // 悉尼
        };
        
        System.out.println("\n各城市会议时间:");
        for (String city : cities) {
            ZonedDateTime cityTime = meetingSF.withZoneSameInstant(ZoneId.of(city));
            System.out.printf("%-20s: %s%n", 
                city.split("/")[1], 
                cityTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm (zzz)"))
            );
        }
    }
    
    // 计算飞行时间(考虑时区)
    public static Duration calculateFlightDuration(ZonedDateTime departure, ZonedDateTime arrival) {
        return Duration.between(departure, arrival);
    }
    
    // 检查是否在营业时间内(考虑时区)
    public static boolean isBusinessHourGlobal(ZonedDateTime time, ZoneId businessZone, 
                                              LocalTime open, LocalTime close) {
        ZonedDateTime businessZoneTime = time.withZoneSameInstant(businessZone);
        LocalTime localTime = businessZoneTime.toLocalTime();
        
        return !localTime.isBefore(open) && !localTime.isAfter(close);
    }
}

Instant和Duration:时间点与时间段

Instant和Duration实战

java 复制代码
import java.time.*;
import java.time.temporal.*;
import java.util.concurrent.TimeUnit;

public class InstantDurationExample {
    
    public static void main(String[] args) throws Exception {
        System.out.println("=== Instant 和 Duration 实战 ===");
        
        // 1. Instant:时间点(时间戳)
        System.out.println("\n1. Instant - 时间点:");
        
        Instant now = Instant.now();
        Instant specificInstant = Instant.ofEpochMilli(1699999999999L);
        Instant parsedInstant = Instant.parse("2023-11-15T14:30:45.123Z");
        
        System.out.println("当前时刻: " + now);
        System.out.println("指定时间戳: " + specificInstant);
        System.out.println("解析的ISO格式: " + parsedInstant);
        
        // 获取时间戳
        System.out.println("\n时间戳获取:");
        System.out.println("毫秒时间戳: " + now.toEpochMilli());
        System.out.println("秒时间戳: " + now.getEpochSecond());
        System.out.println("纳秒部分: " + now.getNano());
        
        // 2. Instant运算
        System.out.println("\n2. Instant运算:");
        System.out.println("1小时后: " + now.plus(1, ChronoUnit.HOURS));
        System.out.println("30分钟后: " + now.plus(30, ChronoUnit.MINUTES));
        System.out.println("1天前: " + now.minus(1, ChronoUnit.DAYS));
        System.out.println("500毫秒前: " + now.minus(500, ChronoUnit.MILLIS));
        
        // 3. Duration:时间段(基于时间)
        System.out.println("\n3. Duration - 时间段:");
        
        Duration oneHour = Duration.ofHours(1);
        Duration thirtyMinutes = Duration.ofMinutes(30);
        Duration customDuration = Duration.of(90, ChronoUnit.SECONDS);
        Duration parsedDuration = Duration.parse("PT1H30M"); // ISO-8601格式
        
        System.out.println("1小时: " + oneHour);
        System.out.println("30分钟: " + thirtyMinutes);
        System.out.println("90秒: " + customDuration);
        System.out.println("解析1小时30分钟: " + parsedDuration);
        
        // 4. Duration运算
        System.out.println("\n4. Duration运算:");
        System.out.println("1小时 + 30分钟 = " + oneHour.plus(thirtyMinutes));
        System.out.println("2小时 - 30分钟 = " + Duration.ofHours(2).minus(thirtyMinutes));
        System.out.println("30分钟 × 2 = " + thirtyMinutes.multipliedBy(2));
        System.out.println("1小时 ÷ 2 = " + oneHour.dividedBy(2));
        
        // 5. 获取Duration各部分
        System.out.println("\n5. Duration分解:");
        Duration complexDuration = Duration.ofHours(25).plusMinutes(90);
        System.out.println("25小时90分钟分解:");
        System.out.println("  总天数: " + complexDuration.toDays());
        System.out.println("  总小时数: " + complexDuration.toHours());
        System.out.println("  总分钟数: " + complexDuration.toMinutes());
        System.out.println("  总秒数: " + complexDuration.toSeconds());
        System.out.println("  总毫秒数: " + complexDuration.toMillis());
        System.out.println("  总纳秒数: " + complexDuration.toNanos());
        
        System.out.println("\n各部分:");
        System.out.println("  小时部分: " + complexDuration.toHoursPart());
        System.out.println("  分钟部分: " + complexDuration.toMinutesPart());
        System.out.println("  秒部分: " + complexDuration.toSecondsPart());
        
        // 6. Period:时间段(基于日期)
        System.out.println("\n6. Period - 基于日期的时间段:");
        
        Period oneYear = Period.ofYears(1);
        Period sixMonths = Period.ofMonths(6);
        Period complexPeriod = Period.of(1, 6, 15); // 1年6个月15天
        Period parsedPeriod = Period.parse("P1Y6M15D"); // ISO-8601格式
        
        System.out.println("1年: " + oneYear);
        System.out.println("6个月: " + sixMonths);
        System.out.println("1年6个月15天: " + complexPeriod);
        System.out.println("解析的Period: " + parsedPeriod);
        
        // 7. 实际应用:计算两个日期之间的时间段
        System.out.println("\n7. 实际应用:计算时间段:");
        
        LocalDate birthDate = LocalDate.of(1990, 5, 15);
        LocalDate today = LocalDate.now();
        
        Period age = Period.between(birthDate, today);
        System.out.println("年龄: " + age.getYears() + "年 " + 
                         age.getMonths() + "个月 " + 
                         age.getDays() + "天");
        
        // 8. 实际应用:性能测量
        System.out.println("\n8. 性能测量示例:");
        measurePerformance();
        
        // 9. 实际应用:超时控制
        System.out.println("\n9. 超时控制示例:");
        timeoutControl();
    }
    
    // 性能测量
    public static void measurePerformance() {
        Instant start = Instant.now();
        
        // 模拟耗时操作
        try {
            TimeUnit.MILLISECONDS.sleep(1500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        Instant end = Instant.now();
        Duration elapsed = Duration.between(start, end);
        
        System.out.println("操作耗时: " + elapsed.toMillis() + " 毫秒");
        System.out.println("操作耗时: " + elapsed.getSeconds() + " 秒 " + 
                         elapsed.toMillisPart() + " 毫秒");
        
        // 格式化输出
        if (elapsed.toMinutes() > 0) {
            System.out.printf("格式化: %d分 %d秒 %d毫秒%n",
                elapsed.toMinutes(),
                elapsed.toSecondsPart(),
                elapsed.toMillisPart());
        } else {
            System.out.printf("格式化: %d秒 %d毫秒%n",
                elapsed.toSeconds(),
                elapsed.toMillisPart());
        }
    }
    
    // 超时控制
    public static void timeoutControl() {
        Duration timeout = Duration.ofSeconds(3);
        Instant startTime = Instant.now();
        
        System.out.println("开始执行,超时时间: " + timeout.getSeconds() + "秒");
        
        try {
            // 模拟长时间操作
            for (int i = 1; i <= 10; i++) {
                // 检查是否超时
                Duration elapsed = Duration.between(startTime, Instant.now());
                if (elapsed.compareTo(timeout) > 0) {
                    System.out.println("操作超时!");
                    break;
                }
                
                System.out.println("第 " + i + " 步");
                TimeUnit.MILLISECONDS.sleep(500);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        Duration totalTime = Duration.between(startTime, Instant.now());
        System.out.println("总耗时: " + totalTime.toMillis() + "毫秒");
    }
    
    // 计算两个时刻之间的精确时间段
    public static String formatDurationBetween(Instant start, Instant end) {
        Duration duration = Duration.between(start, end);
        
        long days = duration.toDays();
        long hours = duration.toHoursPart();
        long minutes = duration.toMinutesPart();
        long seconds = duration.toSecondsPart();
        long millis = duration.toMillisPart();
        
        StringBuilder sb = new StringBuilder();
        if (days > 0) sb.append(days).append("天 ");
        if (hours > 0 || days > 0) sb.append(hours).append("小时 ");
        if (minutes > 0 || hours > 0 || days > 0) sb.append(minutes).append("分 ");
        sb.append(seconds).append("秒 ");
        sb.append(millis).append("毫秒");
        
        return sb.toString();
    }
    
    // 计算工作日的持续时间
    public static Duration calculateWorkingDuration(LocalDateTime start, LocalDateTime end) {
        Duration totalDuration = Duration.between(start, end);
        
        // 计算包含的周末天数
        long weekendDays = 0;
        LocalDate currentDate = start.toLocalDate();
        LocalDate endDate = end.toLocalDate();
        
        while (!currentDate.isAfter(endDate)) {
            DayOfWeek dayOfWeek = currentDate.getDayOfWeek();
            if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
                weekendDays++;
            }
            currentDate = currentDate.plusDays(1);
        }
        
        // 减去周末的时间
        Duration weekendDuration = Duration.ofDays(weekendDays);
        return totalDuration.minus(weekendDuration);
    }
}

格式化与解析

高级格式化与解析

java 复制代码
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
import java.util.Locale;

public class DateTimeFormatting {
    
    public static void main(String[] args) {
        System.out.println("=== 日期时间格式化与解析 ===");
        
        LocalDateTime now = LocalDateTime.now();
        
        // 1. 预定义的格式化器
        System.out.println("\n1. 预定义的格式化器:");
        
        System.out.println("ISO格式: " + now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
        System.out.println("ISO日期: " + now.format(DateTimeFormatter.ISO_LOCAL_DATE));
        System.out.println("ISO时间: " + now.format(DateTimeFormatter.ISO_LOCAL_TIME));
        
        // 2. 自定义模式
        System.out.println("\n2. 自定义模式:");
        
        DateTimeFormatter[] formatters = {
            DateTimeFormatter.ofPattern("yyyy-MM-dd"),
            DateTimeFormatter.ofPattern("dd/MM/yyyy"),
            DateTimeFormatter.ofPattern("yyyy年MM月dd日"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"),
            DateTimeFormatter.ofPattern("EEEE, MMMM dd, yyyy"), // 星期,月份,日,年
            DateTimeFormatter.ofPattern("hh:mm a"),            // 12小时制
            DateTimeFormatter.ofPattern("yyyy年 第Q季度"),       // 季度
            DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX") // 带时区偏移
        };
        
        for (DateTimeFormatter formatter : formatters) {
            System.out.println(formatter.format(now) + "  <- " + 
                             formatter.toString().replaceAll(".*\[", "["));
        }
        
        // 3. 本地化格式化
        System.out.println("\n3. 本地化格式化:");
        
        Locale[] locales = {Locale.US, Locale.UK, Locale.GERMANY, Locale.JAPAN, Locale.CHINA};
        DateTimeFormatter localizedFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
        
        for (Locale locale : locales) {
            DateTimeFormatter localeSpecific = localizedFormatter.withLocale(locale);
            System.out.println(locale.getDisplayName() + ": " + localeSpecific.format(now));
        }
        
        // 4. 格式化器构建器(复杂格式化)
        System.out.println("\n4. 格式化器构建器:");
        
        DateTimeFormatter complexFormatter = new DateTimeFormatterBuilder()
            .appendLiteral("日期: ")
            .appendValue(ChronoField.YEAR, 4)
            .appendLiteral("年")
            .appendValue(ChronoField.MONTH_OF_YEAR, 2)
            .appendLiteral("月")
            .appendValue(ChronoField.DAY_OF_MONTH, 2)
            .appendLiteral("日")
            .appendLiteral(" 时间: ")
            .appendValue(ChronoField.HOUR_OF_DAY, 2)
            .appendLiteral(":")
            .appendValue(ChronoField.MINUTE_OF_HOUR, 2)
            .appendLiteral(":")
            .appendValue(ChronoField.SECOND_OF_MINUTE, 2)
            .optionalStart() // 可选部分
            .appendLiteral(".")
            .appendValue(ChronoField.MILLI_OF_SECOND, 3)
            .optionalEnd()
            .toFormatter();
        
        System.out.println("复杂格式化: " + complexFormatter.format(now));
        
        // 5. 解析日期时间
        System.out.println("\n5. 解析日期时间:");
        
        String[] dateStrings = {
            "2023-11-15",
            "15/11/2023",
            "2023年11月15日",
            "2023-11-15 14:30:45",
            "November 15, 2023"
        };
        
        DateTimeFormatter[] parsers = {
            DateTimeFormatter.ofPattern("yyyy-MM-dd"),
            DateTimeFormatter.ofPattern("dd/MM/yyyy"),
            DateTimeFormatter.ofPattern("yyyy年MM月dd日"),
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
            DateTimeFormatter.ofPattern("MMMM dd, yyyy", Locale.US)
        };
        
        for (int i = 0; i < dateStrings.length; i++) {
            try {
                TemporalAccessor parsed = parsers[i].parse(dateStrings[i]);
                System.out.println(dateStrings[i] + " -> 解析成功");
            } catch (DateTimeParseException e) {
                System.out.println(dateStrings[i] + " -> 解析失败: " + e.getMessage());
            }
        }
        
        // 6. 宽松解析与严格解析
        System.out.println("\n6. 宽松解析与严格解析:");
        
        String looseDate = "2023-2-31"; // 2月31日不存在
        DateTimeFormatter lenientFormatter = DateTimeFormatter.ofPattern("yyyy-M-d")
            .withResolverStyle(ResolverStyle.LENIENT);
        DateTimeFormatter strictFormatter = DateTimeFormatter.ofPattern("yyyy-M-d")
            .withResolverStyle(ResolverStyle.STRICT);
        
        try {
            LocalDate parsedLenient = LocalDate.parse(looseDate, lenientFormatter);
            System.out.println("宽松解析成功: " + parsedLenient); // 会自动调整到3月3日
        } catch (DateTimeParseException e) {
            System.out.println("宽松解析失败: " + e.getMessage());
        }
        
        try {
            LocalDate parsedStrict = LocalDate.parse(looseDate, strictFormatter);
            System.out.println("严格解析成功: " + parsedStrict);
        } catch (DateTimeParseException e) {
            System.out.println("严格解析失败: " + e.getMessage());
        }
        
        // 7. 实际应用:多格式解析器
        System.out.println("\n7. 多格式解析器:");
        
        String[] possibleFormats = {
            "yyyy-MM-dd",
            "yyyy/MM/dd",
            "dd-MM-yyyy",
            "dd/MM/yyyy",
            "yyyy.MM.dd",
            "MMM dd, yyyy",
            "dd MMM yyyy"
        };
        
        String testDate = "15/11/2023";
        LocalDate result = parseWithMultipleFormats(testDate, possibleFormats);
        System.out.println("多格式解析结果: " + result);
        
        // 8. 自定义格式化扩展
        System.out.println("\n8. 自定义格式化扩展:");
        
        DateTimeFormatter customFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
            .withZone(ZoneId.of("Asia/Shanghai"))
            .withDecimalStyle(DecimalStyle.STANDARD)
            .withChronology(IsoChronology.INSTANCE);
        
        ZonedDateTime zonedDateTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println("带时区格式化: " + customFormatter.format(zonedDateTime));
    }
    
    // 多格式解析器实现
    public static LocalDate parseWithMultipleFormats(String dateString, String[] formats) {
        for (String format : formats) {
            try {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
                return LocalDate.parse(dateString, formatter);
            } catch (DateTimeParseException e) {
                // 继续尝试下一个格式
                continue;
            }
        }
        throw new IllegalArgumentException("无法解析日期: " + dateString);
    }
    
    // 智能日期解析
    public static LocalDate smartDateParse(String input) {
        // 移除常见的分隔符,统一处理
        String normalized = input.replaceAll("[/.-]", "-");
        
        // 尝试常见格式
        String[] patterns = {
            "yyyy-MM-dd",
            "yyyy-M-d",
            "yy-MM-dd",
            "yy-M-d",
            "MM-dd-yyyy",
            "M-d-yyyy",
            "dd-MM-yyyy",
            "d-M-yyyy"
        };
        
        for (String pattern : patterns) {
            try {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
                return LocalDate.parse(normalized, formatter);
            } catch (DateTimeParseException e) {
                continue;
            }
        }
        
        // 尝试文本月份
        patterns = new String[]{
            "MMM dd, yyyy",
            "MMM d, yyyy",
            "MMMM dd, yyyy",
            "MMMM d, yyyy",
            "dd MMM yyyy",
            "d MMM yyyy"
        };
        
        for (String pattern : patterns) {
            try {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH);
                return LocalDate.parse(input, formatter);
            } catch (DateTimeParseException e) {
                continue;
            }
        }
        
        throw new IllegalArgumentException("无法识别的日期格式: " + input);
    }
    
    // 生成可读的时间间隔描述
    public static String formatTimeAgo(LocalDateTime pastTime) {
        Duration duration = Duration.between(pastTime, LocalDateTime.now());
        
        if (duration.toDays() > 365) {
            long years = duration.toDays() / 365;
            return years + "年前";
        } else if (duration.toDays() > 30) {
            long months = duration.toDays() / 30;
            return months + "个月前";
        } else if (duration.toDays() > 0) {
            return duration.toDays() + "天前";
        } else if (duration.toHours() > 0) {
            return duration.toHours() + "小时前";
        } else if (duration.toMinutes() > 0) {
            return duration.toMinutes() + "分钟前";
        } else {
            return "刚刚";
        }
    }
    
    // 生成日历视图
    public static void printCalendar(int year, int month) {
        LocalDate firstDay = LocalDate.of(year, month, 1);
        DayOfWeek firstDayOfWeek = firstDay.getDayOfWeek();
        int daysInMonth = firstDay.lengthOfMonth();
        
        System.out.printf("\n     %d年 %d月\n", year, month);
        System.out.println("日 一 二 三 四 五 六");
        
        // 打印前面的空白
        for (int i = 0; i < firstDayOfWeek.getValue() % 7; i++) {
            System.out.print("   ");
        }
        
        // 打印日期
        for (int day = 1; day <= daysInMonth; day++) {
            System.out.printf("%2d ", day);
            
            // 换行
            if ((firstDayOfWeek.getValue() % 7 + day) % 7 == 0) {
                System.out.println();
            }
        }
        System.out.println();
    }
}

实战:完整的企业级应用

java 复制代码
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
import java.util.*;
import java.util.concurrent.*;

public class EnterpriseDateTimeApplication {
    
    // 1. 航班预订系统
    static class FlightBookingSystem {
        
        static class Flight {
            String flightNumber;
            String airline;
            String departureAirport;
            String arrivalAirport;
            ZonedDateTime departureTime;
            ZonedDateTime arrivalTime;
            Duration flightDuration;
            
            public Flight(String flightNumber, String airline, String departureAirport, 
                         String arrivalAirport, ZonedDateTime departureTime, 
                         ZonedDateTime arrivalTime) {
                this.flightNumber = flightNumber;
                this.airline = airline;
                this.departureAirport = departureAirport;
                this.arrivalAirport = arrivalAirport;
                this.departureTime = departureTime;
                this.arrivalTime = arrivalTime;
                this.flightDuration = Duration.between(departureTime, arrivalTime);
            }
            
            public boolean isOverlapping(Flight other) {
                return !(this.arrivalTime.isBefore(other.departureTime) || 
                        this.departureTime.isAfter(other.arrivalTime));
            }
            
            public Duration getLayoverTime(Flight connectingFlight) {
                if (!this.arrivalAirport.equals(connectingFlight.departureAirport)) {
                    throw new IllegalArgumentException("不是连续的航班");
                }
                return Duration.between(this.arrivalTime, connectingFlight.departureTime);
            }
            
            @Override
            public String toString() {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm (zzz)");
                return String.format("%s %s: %s [%s] -> %s [%s] 时长: %s",
                    airline, flightNumber,
                    departureTime.format(formatter),
                    departureAirport,
                    arrivalTime.format(formatter),
                    arrivalAirport,
                    formatDuration(flightDuration));
            }
            
            private String formatDuration(Duration duration) {
                long hours = duration.toHours();
                long minutes = duration.toMinutesPart();
                return String.format("%d小时%d分钟", hours, minutes);
            }
        }
        
        public static void bookFlight() {
            System.out.println("=== 航班预订系统 ===");
            
            // 定义航班
            Flight flight1 = new Flight(
                "CA123",
                "中国航空",
                "北京首都国际机场 (PEK)",
                "上海浦东国际机场 (PVG)",
                ZonedDateTime.of(2023, 12, 1, 8, 0, 0, 0, ZoneId.of("Asia/Shanghai")),
                ZonedDateTime.of(2023, 12, 1, 10, 30, 0, 0, ZoneId.of("Asia/Shanghai"))
            );
            
            Flight flight2 = new Flight(
                "AA789",
                "美国航空",
                "上海浦东国际机场 (PVG)",
                "纽约肯尼迪机场 (JFK)",
                ZonedDateTime.of(2023, 12, 1, 13, 30, 0, 0, ZoneId.of("Asia/Shanghai")),
                ZonedDateTime.of(2023, 12, 1, 16, 0, 0, 0, ZoneId.of("America/New_York"))
            );
            
            System.out.println("航班1: " + flight1);
            System.out.println("航班2: " + flight2);
            
            // 检查转机时间
            Duration layover = flight1.getLayoverTime(flight2);
            System.out.println("\n转机时间: " + 
                layover.toHours() + "小时" + layover.toMinutesPart() + "分钟");
            
            if (layover.toHours() < 1) {
                System.out.println("⚠️ 警告:转机时间太短!");
            } else if (layover.toHours() > 4) {
                System.out.println("⚠️ 警告:转机时间太长!");
            } else {
                System.out.println("✓ 转机时间合适");
            }
            
            // 显示到达目的地的时间(按目的地时区)
            ZonedDateTime arrivalInLocalTime = flight2.arrivalTime
                .withZoneSameInstant(ZoneId.of("America/New_York"));
            System.out.println("\n到达纽约时间: " + 
                arrivalInLocalTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm (zzz)")));
        }
    }
    
    // 2. 会议调度系统
    static class MeetingScheduler {
        
        static class Meeting {
            String title;
            String organizer;
            ZonedDateTime startTime;
            Duration duration;
            Set<String> participants;
            String location;
            
            public Meeting(String title, String organizer, ZonedDateTime startTime, 
                          Duration duration, Set<String> participants, String location) {
                this.title = title;
                this.organizer = organizer;
                this.startTime = startTime;
                this.duration = duration;
                this.participants = participants;
                this.location = location;
            }
            
            public ZonedDateTime getEndTime() {
                return startTime.plus(duration);
            }
            
            public boolean conflictsWith(Meeting other) {
                return !(this.getEndTime().isBefore(other.startTime) || 
                        this.startTime.isAfter(other.getEndTime()));
            }
            
            public Map<String, ZonedDateTime> getLocalTimesForParticipants() {
                Map<String, ZonedDateTime> localTimes = new HashMap<>();
                
                // 模拟从数据库获取参与者时区
                Map<String, String> participantTimezones = Map.of(
                    "张三", "Asia/Shanghai",
                    "李四", "America/New_York",
                    "王五", "Europe/London",
                    "山田", "Asia/Tokyo"
                );
                
                for (String participant : participants) {
                    String timezone = participantTimezones.getOrDefault(participant, "UTC");
                    localTimes.put(participant, 
                        startTime.withZoneSameInstant(ZoneId.of(timezone)));
                }
                
                return localTimes;
            }
            
            @Override
            public String toString() {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm (zzz)");
                return String.format("会议: %s\n组织者: %s\n时间: %s - %s\n地点: %s\n参与者: %s",
                    title, organizer,
                    startTime.format(formatter),
                    getEndTime().format(formatter),
                    location,
                    String.join(", ", participants));
            }
        }
        
        public static void scheduleMeeting() {
            System.out.println("\n=== 会议调度系统 ===");
            
            Set<String> participants = new HashSet<>(Arrays.asList(
                "张三", "李四", "王五", "山田"
            ));
            
            Meeting meeting = new Meeting(
                "季度项目评审",
                "张三",
                ZonedDateTime.of(2023, 12, 15, 14, 0, 0, 0, ZoneId.of("Asia/Shanghai")),
                Duration.ofHours(2),
                participants,
                "会议室A"
            );
            
            System.out.println(meeting);
            
            // 显示各参与者的本地时间
            System.out.println("\n各参与者的本地时间:");
            Map<String, ZonedDateTime> localTimes = meeting.getLocalTimesForParticipants();
            
            DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
            DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM月dd日");
            
            for (Map.Entry<String, ZonedDateTime> entry : localTimes.entrySet()) {
                ZonedDateTime localTime = entry.getValue();
                System.out.printf("%-8s: %s %s%n",
                    entry.getKey(),
                    localTime.format(dateFormatter),
                    localTime.format(timeFormatter));
            }
            
            // 检查时间是否合适
            System.out.println("\n时间合适性检查:");
            for (Map.Entry<String, ZonedDateTime> entry : localTimes.entrySet()) {
                ZonedDateTime localTime = entry.getValue();
                LocalTime meetingLocalTime = localTime.toLocalTime();
                
                boolean isWorkingHour = meetingLocalTime.isAfter(LocalTime.of(9, 0)) &&
                                       meetingLocalTime.isBefore(LocalTime.of(18, 0));
                
                System.out.printf("%-8s: %s (%s)%n",
                    entry.getKey(),
                    isWorkingHour ? "✓ 工作时间" : "⚠️ 非工作时间",
                    meetingLocalTime.format(DateTimeFormatter.ofPattern("HH:mm")));
            }
        }
    }
    
    // 3. 任务管理系统
    static class TaskManagementSystem {
        
        static class Task {
            String id;
            String title;
            String description;
            TaskPriority priority;
            LocalDateTime dueDate;
            Duration estimatedEffort;
            LocalDateTime actualStart;
            LocalDateTime actualEnd;
            TaskStatus status;
            
            enum TaskPriority { LOW, MEDIUM, HIGH, CRITICAL }
            enum TaskStatus { PENDING, IN_PROGRESS, BLOCKED, COMPLETED, CANCELLED }
            
            public Task(String id, String title, String description, TaskPriority priority,
                       LocalDateTime dueDate, Duration estimatedEffort) {
                this.id = id;
                this.title = title;
                this.description = description;
                this.priority = priority;
                this.dueDate = dueDate;
                this.estimatedEffort = estimatedEffort;
                this.status = TaskStatus.PENDING;
            }
            
            public void startTask() {
                this.actualStart = LocalDateTime.now();
                this.status = TaskStatus.IN_PROGRESS;
            }
            
            public void completeTask() {
                this.actualEnd = LocalDateTime.now();
                this.status = TaskStatus.COMPLETED;
            }
            
            public Duration getTimeSpent() {
                if (actualStart == null) return Duration.ZERO;
                if (actualEnd == null) return Duration.between(actualStart, LocalDateTime.now());
                return Duration.between(actualStart, actualEnd);
            }
            
            public Duration getTimeRemaining() {
                if (status == TaskStatus.COMPLETED) return Duration.ZERO;
                
                Duration timeSpent = getTimeSpent();
                if (timeSpent.compareTo(estimatedEffort) >= 0) {
                    return Duration.ZERO;
                }
                return estimatedEffort.minus(timeSpent);
            }
            
            public boolean isOverdue() {
                return status != TaskStatus.COMPLETED && 
                       LocalDateTime.now().isAfter(dueDate);
            }
            
            public Duration getOverdueDuration() {
                if (!isOverdue()) return Duration.ZERO;
                return Duration.between(dueDate, LocalDateTime.now());
            }
            
            public String getProgress() {
                if (status == TaskStatus.PENDING) return "0%";
                if (status == TaskStatus.COMPLETED) return "100%";
                
                if (estimatedEffort.isZero()) return "N/A";
                
                long spentMillis = getTimeSpent().toMillis();
                long totalMillis = estimatedEffort.toMillis();
                
                double progress = (double) spentMillis / totalMillis * 100;
                return String.format("%.1f%%", Math.min(100, progress));
            }
            
            @Override
            public String toString() {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
                return String.format("任务: %s [%s]\n优先级: %s\n截止时间: %s\n预估工作量: %s\n状态: %s\n进度: %s\n%s",
                    title, id,
                    priority,
                    dueDate.format(formatter),
                    formatDuration(estimatedEffort),
                    status,
                    getProgress(),
                    isOverdue() ? "⚠️ 已超期 " + formatDuration(getOverdueDuration()) : "✓ 按时"
                );
            }
            
            private String formatDuration(Duration duration) {
                if (duration.toDays() > 0) {
                    return String.format("%d天%d小时", duration.toDays(), duration.toHoursPart());
                } else if (duration.toHours() > 0) {
                    return String.format("%d小时%d分钟", duration.toHours(), duration.toMinutesPart());
                } else {
                    return String.format("%d分钟", duration.toMinutes());
                }
            }
        }
        
        public static void manageTasks() {
            System.out.println("\n=== 任务管理系统 ===");
            
            Task task1 = new Task(
                "TASK-001",
                "实现用户登录功能",
                "实现用户登录、注册、忘记密码功能",
                Task.TaskPriority.HIGH,
                LocalDateTime.of(2023, 11, 30, 18, 0),
                Duration.ofHours(16)
            );
            
            Task task2 = new Task(
                "TASK-002",
                "编写单元测试",
                "为核心模块编写单元测试",
                Task.TaskPriority.MEDIUM,
                LocalDateTime.of(2023, 12, 5, 18, 0),
                Duration.ofHours(8)
            );
            
            task1.startTask();
            task1.completeTask();
            
            task2.startTask();
            // task2 正在进行中
            
            System.out.println(task1);
            System.out.println("\n---\n");
            System.out.println(task2);
            
            // 生成任务报告
            System.out.println("\n=== 任务报告 ===");
            List<Task> tasks = Arrays.asList(task1, task2);
            
            long totalEstimated = tasks.stream()
                .mapToLong(t -> t.estimatedEffort.toHours())
                .sum();
            
            long totalActual = tasks.stream()
                .mapToLong(t -> t.getTimeSpent().toHours())
                .sum();
            
            long overdueCount = tasks.stream()
                .filter(Task::isOverdue)
                .count();
            
            System.out.printf("总任务数: %d\n", tasks.size());
            System.out.printf("预估总工时: %d 小时\n", totalEstimated);
            System.out.printf("实际总工时: %d 小时\n", totalActual);
            System.out.printf("超期任务数: %d\n", overdueCount);
            System.out.printf("完成率: %.1f%%\n", 
                tasks.stream().filter(t -> t.status == Task.TaskStatus.COMPLETED).count() * 100.0 / tasks.size());
        }
    }
    
    // 4. 缓存过期系统
    static class CacheExpirySystem<K, V> {
        private final ConcurrentHashMap<K, CacheEntry<V>> cache = new ConcurrentHashMap<>();
        private final ScheduledExecutorService cleaner = Executors.newScheduledThreadPool(1);
        
        static class CacheEntry<V> {
            V value;
            Instant expiryTime;
            
            CacheEntry(V value, Duration ttl) {
                this.value = value;
                this.expiryTime = Instant.now().plus(ttl);
            }
            
            boolean isExpired() {
                return Instant.now().isAfter(expiryTime);
            }
            
            Duration getTimeUntilExpiry() {
                return Duration.between(Instant.now(), expiryTime);
            }
        }
        
        public CacheExpirySystem() {
            // 每5秒清理一次过期缓存
            cleaner.scheduleAtFixedRate(this::cleanExpiredEntries, 5, 5, TimeUnit.SECONDS);
        }
        
        public void put(K key, V value, Duration ttl) {
            cache.put(key, new CacheEntry<>(value, ttl));
        }
        
        public V get(K key) {
            CacheEntry<V> entry = cache.get(key);
            if (entry == null || entry.isExpired()) {
                cache.remove(key);
                return null;
            }
            return entry.value;
        }
        
        public void remove(K key) {
            cache.remove(key);
        }
        
        public Set<K> getKeysAboutToExpire(Duration threshold) {
            Instant warningTime = Instant.now().plus(threshold);
            Set<K> result = new HashSet<>();
            
            for (Map.Entry<K, CacheEntry<V>> entry : cache.entrySet()) {
                if (entry.getValue().expiryTime.isBefore(warningTime)) {
                    result.add(entry.getKey());
                }
            }
            
            return result;
        }
        
        public Map<K, Duration> getExpiryTimes() {
            Map<K, Duration> result = new HashMap<>();
            
            for (Map.Entry<K, CacheEntry<V>> entry : cache.entrySet()) {
                result.put(entry.getKey(), entry.getValue().getTimeUntilExpiry());
            }
            
            return result;
        }
        
        private void cleanExpiredEntries() {
            cache.entrySet().removeIf(entry -> entry.getValue().isExpired());
        }
        
        public void shutdown() {
            cleaner.shutdown();
        }
        
        public void demonstrate() {
            System.out.println("\n=== 缓存过期系统 ===");
            
            put("user:1001", "用户数据A", Duration.ofSeconds(30));
            put("product:2001", "产品数据B", Duration.ofMinutes(1));
            put("config:3001", "配置数据C", Duration.ofHours(1));
            
            System.out.println("初始缓存状态:");
            getExpiryTimes().forEach((key, duration) -> 
                System.out.printf("  %s: %d秒后过期%n", key, duration.getSeconds())
            );
            
            System.out.println("\n30秒内将过期的缓存:");
            getKeysAboutToExpire(Duration.ofSeconds(30)).forEach(key ->
                System.out.println("  " + key)
            );
            
            System.out.println("\n等待35秒后...");
            try {
                Thread.sleep(35000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println("\n当前缓存状态:");
            getExpiryTimes().forEach((key, duration) -> 
                System.out.printf("  %s: %d秒后过期%n", key, duration.getSeconds())
            );
            
            System.out.println("\n获取user:1001: " + get("user:1001"));
        }
    }
    
    public static void main(String[] args) {
        // 运行各个系统
        FlightBookingSystem.bookFlight();
        MeetingScheduler.scheduleMeeting();
        TaskManagementSystem.manageTasks();
        
        CacheExpirySystem<String, String> cacheSystem = new CacheExpirySystem<>();
        cacheSystem.demonstrate();
        cacheSystem.shutdown();
    }
}

最佳实践与性能优化

java 复制代码
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
import java.util.concurrent.*;

public class DateTimeBestPractices {
    
    // 1. 线程安全的日期时间格式化
    static class ThreadSafeFormatter {
        // ❌ 错误做法:SimpleDateFormat不是线程安全的
        // private static final SimpleDateFormat unsafeFormatter = new SimpleDateFormat("yyyy-MM-dd");
        
        // ✅ 正确做法:DateTimeFormatter是线程安全的
        private static final DateTimeFormatter safeFormatter = 
            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        
        private static final Map<String, DateTimeFormatter> formatterCache = 
            new ConcurrentHashMap<>();
        
        public static DateTimeFormatter getFormatter(String pattern) {
            return formatterCache.computeIfAbsent(pattern, 
                p -> DateTimeFormatter.ofPattern(p));
        }
        
        public static String format(LocalDateTime dateTime, String pattern) {
            return dateTime.format(getFormatter(pattern));
        }
    }
    
    // 2. 日期时间对象池(针对高频率创建场景)
    static class DateTimePool {
        private final ConcurrentLinkedQueue<LocalDateTime> pool = 
            new ConcurrentLinkedQueue<>();
        private final int maxSize;
        
        public DateTimePool(int maxSize) {
            this.maxSize = maxSize;
        }
        
        public LocalDateTime borrow() {
            LocalDateTime dt = pool.poll();
            return dt != null ? dt : LocalDateTime.now();
        }
        
        public void release(LocalDateTime dateTime) {
            if (pool.size() < maxSize) {
                pool.offer(dateTime);
            }
        }
    }
    
    // 3. 高效的时间比较
    static class EfficientDateTimeComparison {
        // 比较两个时间段是否重叠
        public static boolean isOverlap(LocalDateTime start1, LocalDateTime end1,
                                       LocalDateTime start2, LocalDateTime end2) {
            // 使用时间戳比较,避免多次方法调用
            long s1 = start1.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            long e1 = end1.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            long s2 = start2.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            long e2 = end2.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
            
            return !(e1 <= s2 || s1 >= e2);
        }
        
        // 批量检查日期是否在范围内
        public static List<LocalDate> filterDatesInRange(List<LocalDate> dates,
                                                        LocalDate start, 
                                                        LocalDate end) {
            long startEpochDay = start.toEpochDay();
            long endEpochDay = end.toEpochDay();
            
            return dates.stream()
                .filter(date -> {
                    long epochDay = date.toEpochDay();
                    return epochDay >= startEpochDay && epochDay <= endEpochDay;
                })
                .collect(Collectors.toList());
        }
    }
    
    // 4. 避免常见的性能陷阱
    static class PerformancePitfalls {
        
        // ❌ 错误做法:在循环中重复创建格式化器
        public static void badPractice(List<LocalDateTime> dateTimes) {
            for (LocalDateTime dt : dateTimes) {
                // 每次循环都创建新的格式化器
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                String formatted = dt.format(formatter);
                // 使用 formatted...
            }
        }
        
        // ✅ 正确做法:重用格式化器
        public static void goodPractice(List<LocalDateTime> dateTimes) {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            for (LocalDateTime dt : dateTimes) {
                String formatted = dt.format(formatter);
                // 使用 formatted...
            }
        }
        
        // ❌ 错误做法:不必要的时区转换
        public static void badTimeZoneConversion() {
            LocalDateTime localDateTime = LocalDateTime.now();
            // 不必要的双重转换
            ZonedDateTime zoned = localDateTime.atZone(ZoneId.systemDefault());
            Instant instant = zoned.toInstant();
            // ...
        }
        
        // ✅ 正确做法:直接转换
        public static void goodTimeZoneConversion() {
            LocalDateTime localDateTime = LocalDateTime.now();
            // 直接转换为Instant
            Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
            // ...
        }
    }
    
    // 5. 内存优化:使用原始类型处理时间戳
    static class MemoryOptimizedTimeSeries {
        private final long[] timestamps;
        private final double[] values;
        private int size;
        
        public MemoryOptimizedTimeSeries(int capacity) {
            this.timestamps = new long[capacity];
            this.values = new double[capacity];
            this.size = 0;
        }
        
        public void addDataPoint(LocalDateTime timestamp, double value) {
            if (size >= timestamps.length) {
                throw new IllegalStateException("容量已满");
            }
            
            // 存储时间戳(毫秒)
            timestamps[size] = timestamp.atZone(ZoneId.systemDefault())
                                        .toInstant()
                                        .toEpochMilli();
            values[size] = value;
            size++;
        }
        
        public LocalDateTime getTimestamp(int index) {
            return LocalDateTime.ofInstant(
                Instant.ofEpochMilli(timestamps[index]),
                ZoneId.systemDefault()
            );
        }
        
        public double getValue(int index) {
            return values[index];
        }
        
        // 高效的时间范围查询
        public List<Integer> queryByTimeRange(LocalDateTime start, LocalDateTime end) {
            long startMillis = start.atZone(ZoneId.systemDefault())
                                   .toInstant()
                                   .toEpochMilli();
            long endMillis = end.atZone(ZoneId.systemDefault())
                               .toInstant()
                               .toEpochMilli();
            
            List<Integer> result = new ArrayList<>();
            for (int i = 0; i < size; i++) {
                if (timestamps[i] >= startMillis && timestamps[i] <= endMillis) {
                    result.add(i);
                }
            }
            return result;
        }
    }
    
    // 6. 基准测试:对比不同实现方式的性能
    static class DateTimeBenchmark {
        
        public static void benchmarkFormatting() {
            int iterations = 100000;
            LocalDateTime now = LocalDateTime.now();
            
            // 测试1:重复创建格式化器
            long start1 = System.nanoTime();
            for (int i = 0; i < iterations; i++) {
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                String formatted = now.format(formatter);
            }
            long time1 = System.nanoTime() - start1;
            
            // 测试2:重用格式化器
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            long start2 = System.nanoTime();
            for (int i = 0; i < iterations; i++) {
                String formatted = now.format(formatter);
            }
            long time2 = System.nanoTime() - start2;
            
            System.out.println("=== 格式化性能测试 ===");
            System.out.printf("重复创建格式化器: %,d ns%n", time1);
            System.out.printf("重用格式化器: %,d ns%n", time2);
            System.out.printf("性能提升: %.1f%%%n", (time1 - time2) * 100.0 / time1);
        }
        
        public static void benchmarkComparison() {
            int iterations = 1000000;
            List<LocalDate> dates = new ArrayList<>();
            Random random = new Random();
            
            // 生成测试数据
            for (int i = 0; i < 1000; i++) {
                dates.add(LocalDate.now().plusDays(random.nextInt(365)));
            }
            
            LocalDate start = LocalDate.now().minusDays(180);
            LocalDate end = LocalDate.now().plusDays(180);
            
            // 测试不同的比较方法
            long startTime = System.nanoTime();
            long count1 = dates.stream()
                .filter(date -> date.isAfter(start) && date.isBefore(end))
                .count();
            long time1 = System.nanoTime() - startTime;
            
            startTime = System.nanoTime();
            long startEpochDay = start.toEpochDay();
            long endEpochDay = end.toEpochDay();
            long count2 = dates.stream()
                .filter(date -> {
                    long epochDay = date.toEpochDay();
                    return epochDay > startEpochDay && epochDay < endEpochDay;
                })
                .count();
            long time2 = System.nanoTime() - startTime;
            
            System.out.println("\n=== 日期比较性能测试 ===");
            System.out.printf("使用isAfter/isBefore: %,d ns%n", time1);
            System.out.printf("使用epochDay直接比较: %,d ns%n", time2);
            System.out.printf("性能提升: %.1f%%%n", (time1 - time2) * 100.0 / time1);
        }
    }
    
    // 7. 实用的工具方法
    static class DateTimeUtils {
        
        // 安全解析日期,支持多种格式
        public static Optional<LocalDate> safeParseDate(String dateStr) {
            String[] patterns = {
                "yyyy-MM-dd",
                "yyyy/MM/dd",
                "dd/MM/yyyy",
                "dd-MM-yyyy",
                "yyyy.MM.dd",
                "MM/dd/yyyy"
            };
            
            for (String pattern : patterns) {
                try {
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
                    return Optional.of(LocalDate.parse(dateStr, formatter));
                } catch (DateTimeParseException e) {
                    continue;
                }
            }
            
            return Optional.empty();
        }
        
        // 计算两个日期之间的工作日
        public static long calculateWorkingDays(LocalDate start, LocalDate end) {
            return start.datesUntil(end.plusDays(1))
                .filter(date -> date.getDayOfWeek() != DayOfWeek.SATURDAY &&
                              date.getDayOfWeek() != DayOfWeek.SUNDAY)
                .count();
        }
        
        // 获取下一个工作日
        public static LocalDate getNextWorkingDay(LocalDate date) {
            LocalDate nextDay = date.plusDays(1);
            while (nextDay.getDayOfWeek() == DayOfWeek.SATURDAY || 
                   nextDay.getDayOfWeek() == DayOfWeek.SUNDAY) {
                nextDay = nextDay.plusDays(1);
            }
            return nextDay;
        }
        
        // 格式化时间差为可读字符串
        public static String formatDurationReadable(Duration duration) {
            if (duration.isNegative()) {
                return "已过期";
            }
            
            if (duration.toDays() > 0) {
                return String.format("%d天%d小时", duration.toDays(), duration.toHoursPart());
            } else if (duration.toHours() > 0) {
                return String.format("%d小时%d分钟", duration.toHours(), duration.toMinutesPart());
            } else if (duration.toMinutes() > 0) {
                return String.format("%d分钟", duration.toMinutes());
            } else {
                return String.format("%d秒", duration.toSeconds());
            }
        }
        
        // 检查是否是营业时间(考虑周末和节假日)
        public static boolean isBusinessTime(LocalDateTime dateTime, 
                                            Set<LocalDate> holidays) {
            // 检查是否是周末
            DayOfWeek dayOfWeek = dateTime.getDayOfWeek();
            if (dayOfWeek == DayOfWeek.SATURDAY || dayOfWeek == DayOfWeek.SUNDAY) {
                return false;
            }
            
            // 检查是否是节假日
            if (holidays.contains(dateTime.toLocalDate())) {
                return false;
            }
            
            // 检查时间是否在营业时间内(假设9:00-18:00)
            LocalTime time = dateTime.toLocalTime();
            return !time.isBefore(LocalTime.of(9, 0)) && 
                   !time.isAfter(LocalTime.of(18, 0));
        }
    }
    
    public static void main(String[] args) {
        System.out.println("=== 日期时间API最佳实践 ===");
        
        // 运行性能测试
        DateTimeBenchmark.benchmarkFormatting();
        DateTimeBenchmark.benchmarkComparison();
        
        // 演示实用工具方法
        System.out.println("\n=== 实用工具方法演示 ===");
        
        LocalDate today = LocalDate.now();
        System.out.println("今天: " + today);
        System.out.println("下一个工作日: " + DateTimeUtils.getNextWorkingDay(today));
        
        LocalDate start = today.minusDays(30);
        LocalDate end = today.plusDays(30);
        System.out.printf("%s 到 %s 之间的工作日: %d天%n", 
            start, end, DateTimeUtils.calculateWorkingDays(start, end));
        
        // 测试安全解析
        String[] testDates = {"2023-11-15", "15/11/2023", "2023.11.15", "无效日期"};
        for (String testDate : testDates) {
            Optional<LocalDate> parsed = DateTimeUtils.safeParseDate(testDate);
            System.out.printf("解析 '%s': %s%n", 
                testDate, 
                parsed.map(LocalDate::toString).orElse("解析失败"));
        }
        
        // 演示缓存系统
        System.out.println("\n=== 线程安全格式化演示 ===");
        ExecutorService executor = Executors.newFixedThreadPool(10);
        List<Future<String>> futures = new ArrayList<>();
        
        for (int i = 0; i < 100; i++) {
            final int index = i;
            futures.add(executor.submit(() -> 
                ThreadSafeFormatter.format(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss") + 
                " - 线程" + index
            ));
        }
        
        for (Future<String> future : futures) {
            try {
                System.out.println(future.get());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        executor.shutdown();
    }
}

总结:Java 8日期时间API的核心要点

主要优势

  1. 不可变性:所有核心类都是不可变的,线程安全
  2. 清晰的API设计:分离了日期、时间、时区等概念
  3. 强大的时区支持:内置完善的时区处理机制
  4. 流畅的链式调用:代码更易读、更易写
  5. ISO标准兼容:默认使用ISO-8601标准

常见使用场景

场景 推荐使用的类 说明
只处理日期 LocalDate 生日、纪念日、截止日期
只处理时间 LocalTime 营业时间、会议时间
日期+时间 LocalDateTime 日志时间、创建时间
跨时区应用 ZonedDateTime 国际会议、航班时间
时间点/时间戳 Instant 性能测量、缓存过期
时间段(时间) Duration 电影时长、任务耗时
时间段(日期) Period 年龄、订阅期限

迁移指南(从传统API迁移)

java 复制代码
public class MigrationGuide {
    
    // 传统API -> Java 8 API的迁移
    public static void migrateFromLegacy() {
        System.out.println("=== 从传统API迁移到Java 8 API ===");
        
        // 1. Date -> Instant/LocalDateTime
        java.util.Date oldDate = new java.util.Date();
        Instant instant = oldDate.toInstant();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        System.out.println("Date -> LocalDateTime: " + localDateTime);
        
        // 2. Calendar -> ZonedDateTime
        Calendar calendar = Calendar.getInstance();
        ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(
            calendar.toInstant(),
            calendar.getTimeZone().toZoneId()
        );
        System.out.println("Calendar -> ZonedDateTime: " + zonedDateTime);
        
        // 3. SimpleDateFormat -> DateTimeFormatter
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        System.out.println("格式化: " + formatter.format(LocalDate.now()));
        
        // 4. 时间运算的迁移
        calendar.add(Calendar.DAY_OF_MONTH, 7);
        ZonedDateTime sevenDaysLater = zonedDateTime.plusDays(7);
        System.out.println("7天后: " + sevenDaysLater);
        
        // 5. 时间比较的迁移
        boolean isBefore = calendar.before(Calendar.getInstance());
        boolean isBefore8 = zonedDateTime.isBefore(ZonedDateTime.now());
        System.out.println("时间比较: " + isBefore8);
    }
    
    // 常见陷阱和解决方案
    public static void commonPitfalls() {
        System.out.println("\n=== 常见陷阱和解决方案 ===");
        
        // 陷阱1:时区处理不当
        System.out.println("\n陷阱1:时区处理");
        System.out.println("❌ LocalDateTime.now() 不包含时区信息");
        System.out.println("✅ 使用 ZonedDateTime.now() 或指定时区");
        
        // 陷阱2:忽略不可变性
        System.out.println("\n陷阱2:忽略不可变性");
        LocalDate date = LocalDate.of(2023, 11, 15);
        date.plusDays(1); // 这个方法返回新对象,不修改原对象
        System.out.println("date.plusDays(1) 后,原date未改变: " + date);
        LocalDate newDate = date.plusDays(1);
        System.out.println("需要接收返回值: " + newDate);
        
        // 陷阱3:格式化模式错误
        System.out.println("\n陷阱3:格式化模式");
        System.out.println("❌ yyyy-MM-dd HH:mm:ss - 24小时制");
        System.out.println("❌ yyyy-MM-dd hh:mm:ss - 12小时制(需要a表示上午/下午)");
        System.out.println("✅ yyyy-MM-dd HH:mm:ss - 24小时制");
        System.out.println("✅ yyyy-MM-dd hh:mm:ss a - 12小时制");
        
        // 陷阱4:解析严格性
        System.out.println("\n陷阱4:解析严格性");
        String dateStr = "2023-02-31"; // 2月31日不存在
        try {
            LocalDate date2 = LocalDate.parse(dateStr);
            System.out.println("宽松解析: " + date2);
        } catch (Exception e) {
            System.out.println("默认严格解析失败: " + e.getMessage());
        }
    }
    
    // 性能最佳实践总结
    public static void performanceBestPractices() {
        System.out.println("\n=== 性能最佳实践 ===");
        
        System.out.println("1. 重用DateTimeFormatter");
        System.out.println("   ✅ 将格式化器声明为静态常量");
        System.out.println("   ❌ 避免在循环中创建新的格式化器");
        
        System.out.println("\n2. 使用正确的数据结构");
        System.out.println("   ✅ 对于时间序列数据,考虑使用long数组存储时间戳");
        System.out.println("   ❌ 避免存储大量LocalDateTime对象");
        
        System.out.println("\n3. 高效的时间比较");
        System.out.println("   ✅ 对于批量比较,转换为epochDay或时间戳进行比较");
        System.out.println("   ❌ 避免在循环中多次调用isBefore/isAfter");
        
        System.out.println("\n4. 时区处理优化");
        System.out.println("   ✅ 如果不需要时区信息,使用LocalDateTime而不是ZonedDateTime");
        System.out.println("   ✅ 避免不必要的时区转换");
        System.out.println("   ❌ 不要在性能关键路径中频繁转换时区");
    }
    
    public static void main(String[] args) {
        migrateFromLegacy();
        commonPitfalls();
        performanceBestPractices();
        
        System.out.println("\n=== 学习资源推荐 ===");
        System.out.println("1. Oracle官方教程: Java Date Time");
        System.out.println("2. 书籍: 《Java 8 in Action》");
        System.out.println("3. 在线练习: codingbat.com/java/Date-Time");
        System.out.println("4. 开源项目: Joda-Time (Java 8日期时间API的前身)");
        
        System.out.println("\n记住:熟练掌握Java 8日期时间API是现代Java开发的必备技能!");
    }
}
相关推荐
不能只会打代码2 小时前
力扣--3433. 统计用户被提及情况
java·算法·leetcode·力扣
知青先生2 小时前
E9项目调试方式
java·ide
本地运行没问题2 小时前
从零散编译到一键打包:Maven如何重塑Java构建流程
java
10km2 小时前
java:延迟加载实现方案对比:双重检查锁定 vs 原子化条件更新
java·延迟加载·双重检查锁定
独自归家的兔2 小时前
千问通义plus - 代码解释器的使用
java·人工智能
嘟嘟w3 小时前
什么是UUID,怎么组成的?
java
通往曙光的路上3 小时前
认证--JSON
java
期待のcode3 小时前
springboot热部署
java·spring boot·后端
222you3 小时前
Spring框架的介绍和IoC入门
java·后端·spring