Appearance
新日期时间 API
旧 API 的问题
┌─────────────────────────────────────────────────────────────────┐
│ 旧 API 的问题 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ❌ java.util.Date 月份从 0 开始,容易出错 │
│ ❌ java.util.Date 不是线程安全的 │
│ ❌ SimpleDateFormat 不是线程安全的 │
│ ❌ 日期时间格式化是线程共享,效率低下 │
│ │
│ ✅ Java 8+ 新的日期时间 API: │
│ • LocalDate、LocalTime、LocalDateTime │
│ • ZonedDateTime、OffsetDateTime │
│ • Duration、Period │
│ • DateTimeFormatter(线程安全) │
│ │
└─────────────────────────────────────────────────────────────────┘本地日期时间
LocalDate - 日期
java
// 获取当前日期
LocalDate today = LocalDate.now();
// 创建指定日期
LocalDate date1 = LocalDate.of(2024, 1, 15);
LocalDate date2 = LocalDate.of(2024, Month.JANUARY, 15);
// 解析字符串
LocalDate date3 = LocalDate.parse("2024-01-15");
// 获取年月日
int year = today.getYear(); // 2024
int month = today.getMonthValue(); // 1-12
int day = today.getDayOfMonth(); // 1-31
// 获取星期
DayOfWeek dayOfWeek = today.getDayOfWeek(); // MONDAY, TUESDAY...
String dayName = dayOfWeek.getDisplayName(TextStyle.FULL, Locale.CHINA);
// 日期加减
LocalDate tomorrow = today.plusDays(1);
LocalDate nextWeek = today.plusWeeks(1);
LocalDate nextMonth = today.plusMonths(1);
LocalDate nextYear = today.plusYears(1);
LocalDate yesterday = today.minusDays(1);
// 日期比较
boolean isAfter = date1.isAfter(date2);
boolean isBefore = date1.isBefore(date2);
boolean isEqual = date1.isEqual(date2);
// 计算天数差
long daysBetween = ChronoUnit.DAYS.between(date1, date2);LocalTime - 时间
java
// 获取当前时间
LocalTime now = LocalTime.now();
// 创建指定时间
LocalTime time1 = LocalTime.of(14, 30);
LocalTime time2 = LocalTime.of(14, 30, 45);
LocalTime time3 = LocalTime.of(14, 30, 45, 123456789);
// 解析字符串
LocalTime time4 = LocalTime.parse("14:30:45");
// 获取时分秒
int hour = now.getHour(); // 0-23
int minute = now.getMinute(); // 0-59
int second = now.getSecond(); // 0-59
int nano = now.getNano(); // 0-999999999
// 时间加减
LocalTime later = now.plusHours(2);
LocalTime earlier = now.minusMinutes(30);
// 时间比较
boolean isAfter = time1.isAfter(time2);
boolean isBefore = time1.isBefore(time2);
// 计算时间差
long minutesBetween = ChronoUnit.MINUTES.between(time1, time2);LocalDateTime - 日期时间
java
// 获取当前日期时间
LocalDateTime now = LocalDateTime.now();
// 创建指定日期时间
LocalDateTime dt1 = LocalDateTime.of(2024, 1, 15, 14, 30);
LocalDateTime dt2 = LocalDateTime.of(date1, time1);
// 转换
LocalDate date = now.toLocalDate();
LocalTime time = now.toLocalTime();
// 日期时间加减
LocalDateTime future = now.plusDays(7).minusHours(2);
// 格式化
String str = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));时区日期时间
ZonedDateTime - 带时区
java
// 获取当前时区日期时间
ZonedDateTime now = ZonedDateTime.now();
// 获取指定时区
ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime newYork = ZonedDateTime.now(ZoneId.of("America/New_York"));
// 时区转换
ZonedDateTime tokyoTime = now.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));
// 获取时区
ZoneId zone = now.getZone();
ZoneOffset offset = now.getOffset(); // +08:00OffsetDateTime - 带偏移量
java
// 带 UTC 偏移量的日期时间
OffsetDateTime odt = OffsetDateTime.now(ZoneOffset.of("+08:00"));
// 计算偏移量差异
int offsetSeconds = odt.getOffset().getTotalSeconds();Instant - 瞬间
java
// UTC 瞬间
Instant now = Instant.now();
// 与 Date 互转
Date date = Date.from(instant);
Instant instant2 = date.toInstant();
// 时间戳
long epochSecond = now.getEpochSecond(); // 秒
long epochMilli = now.toEpochMilli(); // 毫秒
// 计算
Instant later = now.plus(Duration.ofHours(2));Duration 与 Period
Duration - 时间段
java
// Duration 用于时间(秒、纳秒)
Duration duration = Duration.ofHours(2);
Duration duration2 = Duration.between(startTime, endTime);
// 转换为其他单位
long hours = duration.toHours();
long minutes = duration.toMinutes();
long seconds = duration.toSeconds();
// 加减
Duration sum = duration.plusMinutes(30);
Duration diff = duration.minusHours(1);Period - 日期段
java
// Period 用于日期(年、月、日)
Period period = Period.ofYears(1);
Period period2 = Period.of(1, 6, 15);
// 转换为天数
int totalDays = period.getDays(); // 15
// 加减
Period future = period.plusMonths(3);计算示例
java
LocalDate start = LocalDate.of(2024, 1, 1);
LocalDate end = LocalDate.of(2024, 12, 31);
// 日期差(Period)
Period period = start.until(end);
System.out.println(period.getYears()); // 0
System.out.println(period.getMonths()); // 11
System.out.println(period.getDays()); // 30
// 总天数(Duration)
long days = ChronoUnit.DAYS.between(start, end);
// 精确时间差
LocalDateTime start2 = LocalDateTime.of(2024, 1, 1, 10, 0);
LocalDateTime end2 = LocalDateTime.of(2024, 1, 1, 12, 30);
Duration duration = Duration.between(start2, end2);
System.out.println(duration.toMinutes()); // 150日期时间格式化
DateTimeFormatter
java
// 预定义格式
String s1 = now.format(DateTimeFormatter.BASIC_ISO_DATE); // 20240115
String s2 = now.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2024-01-15
String s3 = now.format(DateTimeFormatter.ISO_LOCAL_TIME); // 14:30:45
// 自定义格式
DateTimeFormatter f1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
DateTimeFormatter f2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
DateTimeFormatter f3 = DateTimeFormatter.ofPattern("HH:mm:ss");
DateTimeFormatter f4 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = now.format(f4); // "2024-01-15 14:30:45"
// 解析
LocalDate date = LocalDate.parse("2024-01-15", f1);
LocalDateTime dt = LocalDateTime.parse("2024-01-15 14:30:45", f4);常用格式模式
| 模式 | 含义 | 示例 |
|---|---|---|
| yyyy | 4位年份 | 2024 |
| MM | 2位月份 | 01-12 |
| dd | 2位日期 | 01-31 |
| HH | 24小时 | 00-23 |
| hh | 12小时 | 01-12 |
| mm | 分钟 | 00-59 |
| ss | 秒 | 00-59 |
| E | 星期几 | 星期一/Monday |
| a | AM/PM | 上午/下午 |
常用工具类
TemporalAdjusters - 日期调整
java
LocalDate date = LocalDate.of(2024, 1, 15);
// 本月第一天
LocalDate firstDayOfMonth = date.with(TemporalAdjusters.firstDayOfMonth());
// 本月最后一天
LocalDate lastDayOfMonth = date.with(TemporalAdjusters.lastDayOfMonth());
// 下个月第一天
LocalDate firstDayOfNextMonth = date.with(TemporalAdjusters.firstDayOfNextMonth());
// 本年度第一天
LocalDate firstDayOfYear = date.with(TemporalAdjusters.firstDayOfYear());
// 下周一
LocalDate nextMonday = date.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
// 上一个周五
LocalDate lastFriday = date.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY));
// 自定义:月末前5天
LocalDate fiveDaysBeforeMonthEnd = date.with(temporal -> {
LocalDate lastDay = temporal.toLocalDate().with(TemporalAdjusters.lastDayOfMonth());
return lastDay.minusDays(5);
});LocalDate 计算工具
java
// 判断是否闰年
boolean isLeap = LocalDate.now().isLeapYear();
// 判断月末
boolean isLastDayOfMonth = LocalDate.now().isEqual(
LocalDate.now().with(TemporalAdjusters.lastDayOfMonth())
);
// 月初判断
boolean isFirstDayOfMonth = LocalDate.now().getDayOfMonth() == 1;
// 工作日判断
public static boolean isWorkday(LocalDate date) {
DayOfWeek dow = date.getDayOfWeek();
return dow != DayOfWeek.SATURDAY && dow != DayOfWeek.SUNDAY;
}与旧 API 互转
java
// Date → LocalDateTime
Date date = new Date();
Instant instant = date.toInstant();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
// LocalDateTime → Date
LocalDateTime ldt = LocalDateTime.now();
Instant instant2 = ldt.atZone(ZoneId.systemDefault()).toInstant();
Date date2 = Date.from(instant2);
// Date → LocalDate
LocalDate ld = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
// String → Date (使用 Instant)
Date date3 = Date.from(LocalDateTime.parse("2024-01-15 14:30:45",
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
.atZone(ZoneId.systemDefault()).toInstant());总结
新日期时间 API 核心知识点:
| 类 | 用途 | 示例 |
|---|---|---|
| LocalDate | 日期(年月日) | 2024-01-15 |
| LocalTime | 时间(时分秒) | 14:30:45 |
| LocalDateTime | 日期时间 | 2024-01-15 14:30:45 |
| ZonedDateTime | 带时区 | 2024-01-15T14:30:45+08:00 |
| Instant | UTC 瞬间 | 2024-01-15T06:30:45Z |
| Duration | 时间段 | 2小时 |
| Period | 日期段 | 1年2个月 |
| DateTimeFormatter | 格式化 | 线程安全 |
⚠️ 易错点提醒:
- 月份从 1 开始,不是 0(和旧 API 不同)
- DateTimeFormatter 是线程安全的,可以静态共享
- Duration 用于时间(秒),Period 用于日期(年月日)
- ZonedDateTime 和 OffsetDateTime 是不同的概念
- 旧 API 和新 API 可以通过 Instant 互转