Java 8时间日期API完全指南
Java 8 引入了全新的时间日期 API(java.time 包),彻底解决了旧 Date/Calendar 的设计缺陷。本文全面讲解新 API 的使用方法。
旧 API 的问题
- 设计糟糕:
Date 既表示日期又表示时间,月份从 0 开始
- 线程不安全:
SimpleDateFormat 并发使用会出问题
- 时区处理复杂:
TimeZone 设计混乱
- 不可变性缺失:
Date 对象可修改
核心类概览
| 类 |
说明 |
| LocalDate |
日期(年-月-日) |
| LocalTime |
时间(时:分:秒) |
| LocalDateTime |
日期+时间 |
| ZonedDateTime |
带时区的日期时间 |
| Instant |
时间戳(秒/纳秒) |
| Duration |
时间间隔(秒/纳秒) |
| Period |
日期间隔(年/月/日) |
| DateTimeFormatter |
格式化 |
LocalDate 日期操作
LocalDate today = LocalDate.now();
LocalDate date = LocalDate.of(2024, 5, 12);
LocalDate parsed = LocalDate.parse("2024-05-12");
int year = date.getYear(); Month month = date.getMonth(); int day = date.getDayOfMonth(); DayOfWeek week = date.getDayOfWeek();
LocalDate tomorrow = date.plusDays(1); LocalDate lastMonth = date.minusMonths(1); LocalDate nextYear = date.plusYears(1);
LocalDate firstDay = date.withDayOfMonth(1); LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth()); LocalDate nextMonday = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
|
LocalTime 时间操作
LocalTime now = LocalTime.now(); LocalTime time = LocalTime.of(14, 30, 0);
boolean isBefore = now.isBefore(LocalTime.NOON);
LocalTime truncated = now.truncatedTo(ChronoUnit.MINUTES);
|
LocalDateTime 日期时间
LocalDateTime now = LocalDateTime.now(); LocalDateTime dateTime = LocalDateTime.of(2024, 5, 12, 14, 30);
LocalDate date = now.toLocalDate(); LocalTime time = now.toLocalTime(); LocalDateTime combined = date.atTime(time);
|
ZonedDateTime 时区处理
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime ny = ZonedDateTime.now(ZoneId.of("America/New_York")); ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime converted = ny.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
Set<String> zones = ZoneId.getAvailableZoneIds();
|
Instant 时间戳
Instant now = Instant.now();
Instant instant = Instant.ofEpochMilli(System.currentTimeMillis());
long seconds = now.getEpochSecond(); int nanos = now.getNano();
Date date = Date.from(instant); Instant instant2 = date.toInstant();
|
Duration 和 Period
LocalTime start = LocalTime.of(9, 0); LocalTime end = LocalTime.of(17, 30); Duration duration = Duration.between(start, end); long hours = duration.toHours(); long minutes = duration.toMinutes();
LocalDate startDate = LocalDate.of(2024, 1, 1); LocalDate endDate = LocalDate.of(2024, 5, 12); Period period = Period.between(startDate, endDate); int months = period.getMonths(); int days = period.getDays();
|
DateTimeFormatter iso = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); String formatted = LocalDateTime.now().format(formatter);
LocalDateTime parsed = LocalDateTime.parse("2024-05-12 14:30:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
与旧 API 互转
Date date = new Date(); LocalDateTime dateTime = date.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDateTime();
Date newDate = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
Calendar cal = Calendar.getInstance(); ZonedDateTime zdt = cal.toInstant().atZone(cal.getTimeZone().toZoneId());
|
线程安全
所有 java.time 类都是不可变且线程安全的:
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
ExecutorService executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 100; i++) { executor.submit(() -> { String formatted = LocalDate.now().format(formatter); System.out.println(formatted); }); }
|
最佳实践
- 新代码全部使用 java.time:不再使用 Date/Calendar
- 数据库映射:JPA 2.2+ 支持 java.time 类型
- JSON 序列化:配置 Jackson/Gson 支持 JSR-310
- API 设计:方法参数使用 LocalDate/LocalDateTime 而非 String
ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
总结
Java 8 时间 API 的设计原则:
- 清晰分离:日期、时间、日期时间、时区各自独立
- 不可变性:所有对象不可修改,线程安全
- 方法命名一致:
plusXxx、minusXxx、withXxx
掌握 java.time 包是现代 Java 开发的基本要求,它能让你避免大量时间处理相关的 Bug。