JDK8日期时间API详解
目录
- 一、为什么需要新的日期时间API
- 二、核心类概览
- 三、LocalDate本地日期
- 四、LocalTime本地时间
- 五、LocalDateTime本地日期时间
- 六、ZonedDateTime时区日期时间
- 七、Instant时间戳
- 八、Period和Duration时间间隔
- 九、DateTimeFormatter格式化
- 十、实战应用
- 总结
一、为什么需要新的日期时间API
1.1 旧API的痛点
在JDK 8之前,Java处理日期时间主要使用java.util.Date和java.util.Calendar,存在诸多问题:
旧API的问题:
+---------------------------+
| 1. 非线程安全 |
| SimpleDateFormat |
+---------------------------+
| 2. 设计不合理 |
| 月份从0开始(0=1月) |
+---------------------------+
| 3. 难以使用 |
| API繁琐复杂 |
+---------------------------+
| 4. 时区处理困难 |
| Calendar复杂 |
+---------------------------+
package com.example.datetime;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* 旧API问题演示
*/
public class OldDateApiProblems {
public static void main(String[] args) {
// === 问题1: 月份从0开始 ===
Date date = new Date(2024, 11, 20); // 实际是3924年12月20日
System.out.println("错误的日期创建: " + date);
Calendar calendar = Calendar.getInstance();
calendar.set(2024, 0, 1); // 0表示1月
System.out.println("混乱的月份: " + calendar.get(Calendar.MONTH)); // 输出0
// === 问题2: Date可变性(非线程安全) ===
Date mutableDate = new Date();
System.out.println("原始时间: " + mutableDate);
mutableDate.setTime(0); // 可以随意修改
System.out.println("修改后: " + mutableDate);
// === 问题3: SimpleDateFormat非线程安全 ===
// 多线程环境下会出现问题
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 在多线程中使用会有线程安全问题
System.out.println("\n旧API存在诸多问题,JDK8提供了全新的解决方案!");
}
}
1.2 新API的优势
JDK 8新特性:
+---------------------------+
| ✓ 不可变性(线程安全) |
| ✓ 清晰的API设计 |
| ✓ 链式调用 |
| ✓ 完善的时区支持 |
| ✓ ISO 8601标准 |
+---------------------------+
二、核心类概览
2.1 主要类介绍
日期时间类体系:
+--------------------+------------------------+------------------+
| 类名 | 描述 | 示例 |
+--------------------+------------------------+------------------+
| LocalDate | 本地日期(年月日) | 2024-11-20 |
| LocalTime | 本地时间(时分秒) | 14:30:00 |
| LocalDateTime | 本地日期时间 | 2024-11-20T14:30 |
| ZonedDateTime | 带时区的日期时间 | 2024-11-20T14:30+08:00 |
| Instant | 时间戳(UTC) | 2024-11-20T06:30:00Z |
| Duration | 时间间隔(秒、纳秒) | PT2H30M |
| Period | 日期间隔(年月日) | P1Y2M3D |
| DateTimeFormatter | 格式化/解析 | yyyy-MM-dd |
| ZoneId | 时区ID | Asia/Shanghai |
+--------------------+------------------------+------------------+
2.2 类关系图
TemporalAccessor (接口)
|
+-------------------+-------------------+
| | |
LocalDate LocalTime ZonedDateTime
| | |
+--------+----------+ |
| |
LocalDateTime Instant
|
(时间戳,UTC)
三、LocalDate本地日期
3.1 LocalDate基础操作
package com.example.datetime;
import java.time.*;
import java.time.temporal.ChronoUnit;
/**
* LocalDate详解 - 只包含日期,不包含时间
*/
public class LocalDateDemo {
public static void main(String[] args) {
// === 1. 创建LocalDate ===
// 获取当前日期
LocalDate today = LocalDate.now();
System.out.println("今天: " + today);
// 指定日期
LocalDate birthday = LocalDate.of(1990, 5, 15);
System.out.println("生日: " + birthday);
// 从字符串解析
LocalDate parsed = LocalDate.parse("2024-11-20");
System.out.println("解析: " + parsed);
// 年份的第几天
LocalDate dayOfYear = LocalDate.ofYearDay(2024, 100);
System.out.println("2024年第100天: " + dayOfYear);
// === 2. 获取日期信息 ===
System.out.println("\n=== 日期信息 ===");
System.out.println("年份: " + today.getYear());
System.out.println("月份: " + today.getMonth()); // 枚举
System.out.println("月份数值: " + today.getMonthValue()); // 1-12
System.out.println("日: " + today.getDayOfMonth());
System.out.println("星期: " + today.getDayOfWeek()); // 枚举
System.out.println("一年中的第几天: " + today.getDayOfYear());
System.out.println("这个月有几天: " + today.lengthOfMonth());
System.out.println("这一年有几天: " + today.lengthOfYear());
System.out.println("是否闰年: " + today.isLeapYear());
// === 3. 日期运算 ===
System.out.println("\n=== 日期运算 ===");
// 加法
LocalDate tomorrow = today.plusDays(1);
LocalDate nextWeek = today.plusWeeks(1);
LocalDate nextMonth = today.plusMonths(1);
LocalDate nextYear = today.plusYears(1);
System.out.println("明天: " + tomorrow);
System.out.println("下周: " + nextWeek);
System.out.println("下个月: " + nextMonth);
System.out.println("明年: " + nextYear);
// 减法
LocalDate yesterday = today.minusDays(1);
LocalDate lastWeek = today.minusWeeks(1);
System.out.println("昨天: " + yesterday);
System.out.println("上周: " + lastWeek);
// 使用TemporalUnit
LocalDate future = today.plus(3, ChronoUnit.MONTHS);
System.out.println("3个月后: " + future);
// === 4. 日期修改 ===
System.out.println("\n=== 日期修改 ===");
LocalDate modified = today
.withYear(2025)
.withMonth(12)
.withDayOfMonth(31);
System.out.println("修改后: " + modified);
// 本月第一天
LocalDate firstDayOfMonth = today.withDayOfMonth(1);
System.out.println("本月第一天: " + firstDayOfMonth);
// 本年第一天
LocalDate firstDayOfYear = today.withDayOfYear(1);
System.out.println("本年第一天: " + firstDayOfYear);
// === 5. 日期比较 ===
System.out.println("\n=== 日期比较 ===");
LocalDate date1 = LocalDate.of(2024, 11, 20);
LocalDate date2 = LocalDate.of(2024, 12, 25);
System.out.println("date1在date2之前: " + date1.isBefore(date2));
System.out.println("date1在date2之后: " + date1.isAfter(date2));
System.out.println("date1等于date2: " + date1.equals(date2));
System.out.println("比较结果: " + date1.compareTo(date2)); // <0表示之前
// === 6. 实用示例 ===
demonstratePracticalExamples();
}
/**
* 实用示例
*/
private static void demonstratePracticalExamples() {
System.out.println("\n=== 实用示例 ===");
LocalDate today = LocalDate.now();
// 判断是否是周末
DayOfWeek dayOfWeek = today.getDayOfWeek();
boolean isWeekend = dayOfWeek == DayOfWeek.SATURDAY ||
dayOfWeek == DayOfWeek.SUNDAY;
System.out.println("今天是周末: " + isWeekend);
// 计算两个日期之间的天数
LocalDate startDate = LocalDate.of(2024, 1, 1);
LocalDate endDate = LocalDate.of(2024, 12, 31);
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);
System.out.println("2024年有 " + daysBetween + " 天");
// 获取本月最后一天
LocalDate lastDayOfMonth = today.withDayOfMonth(today.lengthOfMonth());
System.out.println("本月最后一天: " + lastDayOfMonth);
// 下一个周五
LocalDate nextFriday = today;
while (nextFriday.getDayOfWeek() != DayOfWeek.FRIDAY) {
nextFriday = nextFriday.plusDays(1);
}
if (nextFriday.equals(today)) {
nextFriday = nextFriday.plusWeeks(1);
}
System.out.println("下一个周五: " + nextFriday);
}
}
四、LocalTime本地时间
4.1 LocalTime基础操作
package com.example.datetime;
import java.time.*;
import java.time.temporal.ChronoUnit;
/**
* LocalTime详解 - 只包含时间,不包含日期
*/
public class LocalTimeDemo {
public static void main(String[] args) {
// === 1. 创建LocalTime ===
// 当前时间
LocalTime now = LocalTime.now();
System.out.println("现在时间: " + now);
// 指定时间
LocalTime time1 = LocalTime.of(14, 30); // 14:30:00
LocalTime time2 = LocalTime.of(14, 30, 45); // 14:30:45
LocalTime time3 = LocalTime.of(14, 30, 45, 123456789); // 包含纳秒
System.out.println("指定时间1: " + time1);
System.out.println("指定时间2: " + time2);
System.out.println("指定时间3: " + time3);
// 从字符串解析
LocalTime parsed = LocalTime.parse("10:15:30");
System.out.println("解析时间: " + parsed);
// 特殊时间
LocalTime midnight = LocalTime.MIDNIGHT; // 00:00
LocalTime noon = LocalTime.NOON; // 12:00
LocalTime max = LocalTime.MAX; // 23:59:59.999999999
LocalTime min = LocalTime.MIN; // 00:00
System.out.println("午夜: " + midnight);
System.out.println("正午: " + noon);
// === 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=== 时间运算 ===");
LocalTime time = LocalTime.of(10, 30);
// 加法
LocalTime plusHours = time.plusHours(2);
LocalTime plusMinutes = time.plusMinutes(45);
LocalTime plusSeconds = time.plusSeconds(30);
System.out.println("2小时后: " + plusHours);
System.out.println("45分钟后: " + plusMinutes);
System.out.println("30秒后: " + plusSeconds);
// 减法
LocalTime minusHours = time.minusHours(1);
System.out.println("1小时前: " + minusHours);
// 跨天计算(自动处理)
LocalTime late = LocalTime.of(23, 30);
LocalTime nextDay = late.plusHours(2); // 自动到第二天
System.out.println("23:30后2小时: " + nextDay);
// === 4. 时间修改 ===
System.out.println("\n=== 时间修改 ===");
LocalTime modified = time
.withHour(15)
.withMinute(45)
.withSecond(30);
System.out.println("修改后: " + modified);
// === 5. 时间比较 ===
System.out.println("\n=== 时间比较 ===");
LocalTime t1 = LocalTime.of(10, 30);
LocalTime t2 = LocalTime.of(14, 30);
System.out.println("t1在t2之前: " + t1.isBefore(t2));
System.out.println("t1在t2之后: " + t1.isAfter(t2));
System.out.println("比较结果: " + t1.compareTo(t2));
// === 6. 实用示例 ===
demonstrateTimeExamples();
}
/**
* 时间实用示例
*/
private static void demonstrateTimeExamples() {
System.out.println("\n=== 实用示例 ===");
LocalTime now = LocalTime.now();
// 判断是否在营业时间内(9:00-18:00)
LocalTime openTime = LocalTime.of(9, 0);
LocalTime closeTime = LocalTime.of(18, 0);
boolean isOpen = now.isAfter(openTime) && now.isBefore(closeTime);
System.out.println("当前营业中: " + isOpen);
// 计算两个时间之间的差异
LocalTime start = LocalTime.of(9, 0);
LocalTime end = LocalTime.of(17, 30);
long hours = ChronoUnit.HOURS.between(start, end);
long minutes = ChronoUnit.MINUTES.between(start, end);
System.out.println("工作时长: " + hours + "小时 (" + minutes + "分钟)");
// 整点时间
LocalTime nextHour = now.plusHours(1).withMinute(0).withSecond(0).withNano(0);
System.out.println("下一个整点: " + nextHour);
// 截断到分钟
LocalTime truncated = now.truncatedTo(ChronoUnit.MINUTES);
System.out.println("截断到分钟: " + truncated);
}
}
五、LocalDateTime本地日期时间
5.1 LocalDateTime操作
package com.example.datetime;
import java.time.*;
import java.time.temporal.ChronoUnit;
/**
* LocalDateTime详解 - 日期+时间
*/
public class LocalDateTimeDemo {
public static void main(String[] args) {
// === 1. 创建LocalDateTime ===
// 当前日期时间
LocalDateTime now = LocalDateTime.now();
System.out.println("现在: " + now);
// 指定日期时间
LocalDateTime dt1 = LocalDateTime.of(2024, 11, 20, 14, 30);
LocalDateTime dt2 = LocalDateTime.of(2024, 11, 20, 14, 30, 45);
LocalDateTime dt3 = LocalDateTime.of(2024, Month.NOVEMBER, 20, 14, 30, 45, 123);
System.out.println("指定日期时间: " + dt1);
// 从LocalDate和LocalTime组合
LocalDate date = LocalDate.of(2024, 11, 20);
LocalTime time = LocalTime.of(14, 30);
LocalDateTime combined = LocalDateTime.of(date, time);
System.out.println("组合: " + combined);
// 从字符串解析
LocalDateTime parsed = LocalDateTime.parse("2024-11-20T14:30:00");
System.out.println("解析: " + parsed);
// === 2. 获取信息 ===
System.out.println("\n=== 日期时间信息 ===");
System.out.println("年: " + now.getYear());
System.out.println("月: " + now.getMonthValue());
System.out.println("日: " + now.getDayOfMonth());
System.out.println("时: " + now.getHour());
System.out.println("分: " + now.getMinute());
System.out.println("秒: " + now.getSecond());
// 提取日期和时间部分
LocalDate datepart = now.toLocalDate();
LocalTime timepart = now.toLocalTime();
System.out.println("日期部分: " + datepart);
System.out.println("时间部分: " + timepart);
// === 3. 日期时间运算 ===
System.out.println("\n=== 日期时间运算 ===");
LocalDateTime current = LocalDateTime.now();
// 加法
LocalDateTime plusDays = current.plusDays(1);
LocalDateTime plusHours = current.plusHours(2);
LocalDateTime plusMinutes = current.plusMinutes(30);
System.out.println("1天后: " + plusDays);
System.out.println("2小时后: " + plusHours);
// 减法
LocalDateTime minusDays = current.minusDays(1);
System.out.println("1天前: " + minusDays);
// 复杂运算
LocalDateTime complex = current
.plusDays(1)
.plusHours(3)
.plusMinutes(30);
System.out.println("1天3小时30分后: " + complex);
// === 4. 修改日期时间 ===
System.out.println("\n=== 修改日期时间 ===");
LocalDateTime modified = current
.withYear(2025)
.withMonth(12)
.withDayOfMonth(31)
.withHour(23)
.withMinute(59)
.withSecond(59);
System.out.println("修改后: " + modified);
// === 5. 日期时间比较 ===
System.out.println("\n=== 日期时间比较 ===");
LocalDateTime dt_1 = LocalDateTime.of(2024, 11, 20, 10, 0);
LocalDateTime dt_2 = LocalDateTime.of(2024, 11, 20, 14, 0);
System.out.println("dt1在dt2之前: " + dt_1.isBefore(dt_2));
System.out.println("dt1在dt2之后: " + dt_1.isAfter(dt_2));
System.out.println("dt1等于dt2: " + dt_1.equals(dt_2));
// === 6. 实战示例 ===
demonstrateRealWorldExamples();
}
/**
* 实战示例
*/
private static void demonstrateRealWorldExamples() {
System.out.println("\n=== 实战示例 ===");
// 示例1: 计算会议时长
LocalDateTime meetingStart = LocalDateTime.of(2024, 11, 20, 9, 0);
LocalDateTime meetingEnd = LocalDateTime.of(2024, 11, 20, 11, 30);
long hours = ChronoUnit.HOURS.between(meetingStart, meetingEnd);
long minutes = ChronoUnit.MINUTES.between(meetingStart, meetingEnd);
System.out.println("会议开始: " + meetingStart);
System.out.println("会议结束: " + meetingEnd);
System.out.println("会议时长: " + hours + "小时 (" + minutes + "分钟)");
// 示例2: 判断订单是否超时(30分钟内未支付)
LocalDateTime orderTime = LocalDateTime.now().minusMinutes(35);
LocalDateTime paymentDeadline = orderTime.plusMinutes(30);
boolean isExpired = LocalDateTime.now().isAfter(paymentDeadline);
System.out.println("\n订单创建: " + orderTime);
System.out.println("支付截止: " + paymentDeadline);
System.out.println("订单已超时: " + isExpired);
// 示例3: 本周的开始和结束时间
LocalDateTime nowDt = LocalDateTime.now();
DayOfWeek currentDay = nowDt.getDayOfWeek();
LocalDateTime weekStart = nowDt.minusDays(currentDay.getValue() - 1)
.withHour(0).withMinute(0).withSecond(0).withNano(0);
LocalDateTime weekEnd = weekStart.plusDays(6)
.withHour(23).withMinute(59).withSecond(59);
System.out.println("\n本周开始: " + weekStart);
System.out.println("本周结束: " + weekEnd);
// 示例4: 工作日计算(排除周末)
LocalDateTime startWork = LocalDateTime.of(2024, 11, 18, 9, 0); // 周一
int workDays = 5;
LocalDateTime endWork = calculateWorkDays(startWork, workDays);
System.out.println("\n从 " + startWork.toLocalDate() + " 开始," +
workDays + " 个工作日后是: " + endWork.toLocalDate());
}
/**
* 计算工作日(排除周末)
*/
private static LocalDateTime calculateWorkDays(LocalDateTime start, int workDays) {
LocalDateTime result = start;
int addedDays = 0;
while (addedDays < workDays) {
result = result.plusDays(1);
// 跳过周末
if (result.getDayOfWeek() != DayOfWeek.SATURDAY &&
result.getDayOfWeek() != DayOfWeek.SUNDAY) {
addedDays++;
}
}
return result;
}
}
六、ZonedDateTime时区日期时间
6.1 时区处理
package com.example.datetime;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* ZonedDateTime详解 - 带时区的日期时间
*/
public class ZonedDateTimeDemo {
public static void main(String[] args) {
// === 1. 创建ZonedDateTime ===
// 当前时区的日期时间
ZonedDateTime now = ZonedDateTime.now();
System.out.println("当前时区: " + now);
// 指定时区的日期时间
ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime newYork = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime london = ZonedDateTime.now(ZoneId.of("Europe/London"));
System.out.println("东京时间: " + tokyo);
System.out.println("纽约时间: " + newYork);
System.out.println("伦敦时间: " + london);
// 从LocalDateTime创建
LocalDateTime ldt = LocalDateTime.of(2024, 11, 20, 14, 30);
ZonedDateTime zonedShanghai = ldt.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("上海时间: " + zonedShanghai);
// === 2. 时区转换 ===
System.out.println("\n=== 时区转换 ===");
// 北京时间转纽约时间
ZonedDateTime beijing = ZonedDateTime.of(2024, 11, 20, 14, 0, 0, 0,
ZoneId.of("Asia/Shanghai"));
ZonedDateTime newYorkTime = beijing.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println("北京时间: " + beijing);
System.out.println("对应纽约时间: " + newYorkTime);
System.out.println("时差: " + (beijing.getHour() - newYorkTime.getHour()) + "小时");
// === 3. 所有可用时区 ===
System.out.println("\n=== 常用时区 ===");
Map<String, String> commonZones = new LinkedHashMap<>();
commonZones.put("Asia/Shanghai", "中国");
commonZones.put("Asia/Tokyo", "日本");
commonZones.put("America/New_York", "纽约");
commonZones.put("America/Los_Angeles", "洛杉矶");
commonZones.put("Europe/London", "伦敦");
commonZones.put("Europe/Paris", "巴黎");
LocalDateTime base = LocalDateTime.of(2024, 11, 20, 12, 0);
commonZones.forEach((zoneId, name) -> {
ZonedDateTime zdt = base.atZone(ZoneId.of(zoneId));
System.out.println(name + ": " + zdt.format(
DateTimeFormatter.ofPattern("HH:mm (Z)")));
});
// === 4. 实战示例 ===
demonstrateTimezoneExamples();
}
/**
* 时区实战示例
*/
private static void demonstrateTimezoneExamples() {
System.out.println("\n=== 时区实战示例 ===");
// 示例1: 全球会议时间协调
System.out.println("1. 全球会议时间协调");
ZonedDateTime meetingBeijing = ZonedDateTime.of(
2024, 11, 20, 14, 0, 0, 0,
ZoneId.of("Asia/Shanghai")
);
System.out.println("会议时间(北京): " + meetingBeijing.format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
// 其他时区的会议时间
String[] zones = {
"America/New_York",
"Europe/London",
"Asia/Tokyo"
};
for (String zone : zones) {
ZonedDateTime localTime = meetingBeijing.withZoneSameInstant(ZoneId.of(zone));
System.out.println(" " + zone + ": " +
localTime.format(DateTimeFormatter.ofPattern("HH:mm")));
}
// 示例2: 航班起飞到达时间
System.out.println("\n2. 航班时间计算");
// 北京起飞
ZonedDateTime departure = ZonedDateTime.of(
2024, 11, 20, 13, 30, 0, 0,
ZoneId.of("Asia/Shanghai")
);
// 飞行13小时
ZonedDateTime arrival = departure
.plusHours(13)
.withZoneSameInstant(ZoneId.of("America/New_York"));
System.out.println("起飞(北京): " + departure.format(
DateTimeFormatter.ofPattern("MM-dd HH:mm")));
System.out.println("到达(纽约): " + arrival.format(
DateTimeFormatter.ofPattern("MM-dd HH:mm")));
Duration flightDuration = Duration.between(departure, arrival);
System.out.println("飞行时长: " + flightDuration.toHours() + "小时");
}
}
七、Instant时间戳
7.1 Instant操作
package com.example.datetime;
import java.time.*;
import java.time.temporal.ChronoUnit;
/**
* Instant详解 - 时间戳(从1970-01-01T00:00:00Z开始)
*/
public class InstantDemo {
public static void main(String[] args) {
// === 1. 创建Instant ===
// 当前时间戳
Instant now = Instant.now();
System.out.println("当前时间戳: " + now);
// 从秒数创建
Instant fromSeconds = Instant.ofEpochSecond(1700000000L);
System.out.println("从秒数创建: " + fromSeconds);
// 从毫秒数创建
Instant fromMillis = Instant.ofEpochMilli(System.currentTimeMillis());
System.out.println("从毫秒创建: " + fromMillis);
// === 2. Instant与其他类型转换 ===
System.out.println("\n=== 类型转换 ===");
// Instant -> LocalDateTime (需要时区)
LocalDateTime ldt = LocalDateTime.ofInstant(now, ZoneId.systemDefault());
System.out.println("转LocalDateTime: " + ldt);
// LocalDateTime -> Instant (需要时区)
LocalDateTime localDateTime = LocalDateTime.now();
Instant instant = localDateTime.toInstant(ZoneOffset.ofHours(8));
System.out.println("LocalDateTime转Instant: " + instant);
// Instant -> Date (旧API)
java.util.Date date = java.util.Date.from(now);
System.out.println("转Date: " + date);
// Date -> Instant
Instant fromDate = date.toInstant();
System.out.println("Date转Instant: " + fromDate);
// === 3. 获取时间戳值 ===
System.out.println("\n=== 获取时间戳 ===");
System.out.println("秒时间戳: " + now.getEpochSecond());
System.out.println("毫秒时间戳: " + now.toEpochMilli());
System.out.println("纳秒部分: " + now.getNano());
// === 4. Instant运算 ===
System.out.println("\n=== 时间戳运算 ===");
Instant start = Instant.now();
// 加法
Instant plus1Hour = start.plus(1, ChronoUnit.HOURS);
Instant plus30Seconds = start.plusSeconds(30);
Instant plus500Millis = start.plusMillis(500);
System.out.println("1小时后: " + plus1Hour);
System.out.println("30秒后: " + plus30Seconds);
// 减法
Instant minus1Day = start.minus(1, ChronoUnit.DAYS);
System.out.println("1天前: " + minus1Day);
// === 5. 比较 ===
System.out.println("\n=== 时间戳比较 ===");
Instant instant1 = Instant.now();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant instant2 = Instant.now();
System.out.println("instant1在instant2之前: " + instant1.isBefore(instant2));
System.out.println("instant1在instant2之后: " + instant1.isAfter(instant2));
System.out.println("比较结果: " + instant1.compareTo(instant2));
// === 6. 实战应用 ===
demonstrateInstantExamples();
}
/**
* Instant实战示例
*/
private static void demonstrateInstantExamples() {
System.out.println("\n=== Instant实战应用 ===");
// 示例1: 性能测试
System.out.println("1. 性能测试");
Instant start = Instant.now();
// 模拟业务处理
long sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i;
}
Instant end = Instant.now();
Duration duration = Duration.between(start, end);
System.out.println("开始时间: " + start);
System.out.println("结束时间: " + end);
System.out.println("执行耗时: " + duration.toMillis() + "ms");
// 示例2: 生成唯一ID(基于时间戳)
System.out.println("\n2. 生成唯一ID");
for (int i = 0; i < 5; i++) {
String uniqueId = generateUniqueId();
System.out.println("唯一ID: " + uniqueId);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 示例3: 计算接口响应时间
System.out.println("\n3. 接口响应时间统计");
simulateApiCall("getUserInfo", 150);
simulateApiCall("saveOrder", 300);
simulateApiCall("queryProducts", 50);
}
/**
* 生成基于时间戳的唯一ID
*/
private static String generateUniqueId() {
Instant now = Instant.now();
long timestamp = now.toEpochMilli();
int random = (int) (Math.random() * 10000);
return timestamp + "-" + random;
}
/**
* 模拟API调用并统计响应时间
*/
private static void simulateApiCall(String apiName, long sleepMillis) {
Instant start = Instant.now();
try {
Thread.sleep(sleepMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant end = Instant.now();
long responseTime = Duration.between(start, end).toMillis();
System.out.println(apiName + " 响应时间: " + responseTime + "ms");
// 告警阈值判断
if (responseTime > 200) {
System.out.println(" [警告] 响应时间超过200ms");
}
}
}
八、Period和Duration时间间隔
8.1 Period和Duration详解
package com.example.datetime;
import java.time.*;
import java.time.temporal.ChronoUnit;
/**
* Period和Duration详解
* Period: 日期间隔(年、月、日)
* Duration: 时间间隔(时、分、秒、纳秒)
*/
public class PeriodDurationDemo {
public static void main(String[] args) {
// === 1. Period - 日期间隔 ===
System.out.println("=== Period (日期间隔) ===");
// 创建Period
Period period1 = Period.of(1, 2, 3); // 1年2月3天
Period period2 = Period.ofYears(2); // 2年
Period period3 = Period.ofMonths(6); // 6个月
Period period4 = Period.ofWeeks(3); // 3周(21天)
Period period5 = Period.ofDays(10); // 10天
System.out.println("1年2月3天: " + period1);
System.out.println("2年: " + period2);
System.out.println("6个月: " + period3);
// 计算两个日期之间的Period
LocalDate date1 = LocalDate.of(2024, 1, 1);
LocalDate date2 = LocalDate.of(2024, 11, 20);
Period between = Period.between(date1, date2);
System.out.println("\n从 " + date1 + " 到 " + date2);
System.out.println("间隔: " + between);
System.out.println("年: " + between.getYears());
System.out.println("月: " + between.getMonths());
System.out.println("日: " + between.getDays());
System.out.println("总月数: " + between.toTotalMonths());
// Period应用
LocalDate today = LocalDate.now();
LocalDate futureDate = today.plus(period1);
System.out.println("今天: " + today);
System.out.println("1年2月3天后: " + futureDate);
// === 2. Duration - 时间间隔 ===
System.out.println("\n=== Duration (时间间隔) ===");
// 创建Duration
Duration duration1 = Duration.ofHours(2); // 2小时
Duration duration2 = Duration.ofMinutes(30); // 30分钟
Duration duration3 = Duration.ofSeconds(60); // 60秒
Duration duration4 = Duration.ofMillis(1000); // 1000毫秒
Duration duration5 = Duration.of(2, ChronoUnit.HOURS); // 2小时
System.out.println("2小时: " + duration1);
System.out.println("30分钟: " + duration2);
// 计算两个时间之间的Duration
LocalTime time1 = LocalTime.of(9, 0);
LocalTime time2 = LocalTime.of(17, 30);
Duration workTime = Duration.between(time1, time2);
System.out.println("\n从 " + time1 + " 到 " + time2);
System.out.println("间隔: " + workTime);
System.out.println("小时: " + workTime.toHours());
System.out.println("分钟: " + workTime.toMinutes());
System.out.println("秒: " + workTime.getSeconds());
// Duration应用
LocalDateTime now = LocalDateTime.now();
LocalDateTime after = now.plus(duration1);
System.out.println("现在: " + now);
System.out.println("2小时后: " + after);
// === 3. Duration运算 ===
System.out.println("\n=== Duration运算 ===");
Duration d1 = Duration.ofHours(2);
Duration d2 = Duration.ofMinutes(30);
Duration plus = d1.plus(d2); // 加法
Duration minus = d1.minus(d2); // 减法
Duration multiplied = d1.multipliedBy(2); // 乘法
Duration divided = d1.dividedBy(2); // 除法
System.out.println("2小时 + 30分钟 = " + plus.toMinutes() + "分钟");
System.out.println("2小时 - 30分钟 = " + minus.toMinutes() + "分钟");
System.out.println("2小时 × 2 = " + multiplied.toHours() + "小时");
System.out.println("2小时 ÷ 2 = " + divided.toHours() + "小时");
// === 4. 实战应用 ===
demonstratePeriodDurationExamples();
}
/**
* Period和Duration实战示例
*/
private static void demonstratePeriodDurationExamples() {
System.out.println("\n=== 实战示例 ===");
// 示例1: 计算年龄
System.out.println("1. 计算年龄");
LocalDate birthday = LocalDate.of(1990, 5, 15);
LocalDate today = LocalDate.now();
Period age = Period.between(birthday, today);
System.out.println("生日: " + birthday);
System.out.println("年龄: " + age.getYears() + "岁" +
age.getMonths() + "个月" +
age.getDays() + "天");
// 示例2: 会员有效期计算
System.out.println("\n2. 会员有效期");
LocalDate membershipStart = LocalDate.of(2024, 1, 1);
Period validity = Period.ofMonths(12); // 12个月会员
LocalDate membershipEnd = membershipStart.plus(validity);
System.out.println("会员开始: " + membershipStart);
System.out.println("会员到期: " + membershipEnd);
long daysRemaining = ChronoUnit.DAYS.between(LocalDate.now(), membershipEnd);
System.out.println("剩余天数: " + daysRemaining + "天");
// 示例3: 工作时长统计
System.out.println("\n3. 工作时长统计");
LocalDateTime workStart = LocalDateTime.of(2024, 11, 20, 9, 0);
LocalDateTime workEnd = LocalDateTime.of(2024, 11, 20, 18, 30);
Duration workDuration = Duration.between(workStart, workEnd);
long hours = workDuration.toHours();
long minutes = workDuration.toMinutes() % 60;
System.out.println("上班: " + workStart.toLocalTime());
System.out.println("下班: " + workEnd.toLocalTime());
System.out.println("工作时长: " + hours + "小时" + minutes + "分钟");
// 午休1小时
Duration lunchBreak = Duration.ofHours(1);
Duration actualWork = workDuration.minus(lunchBreak);
System.out.println("实际工作: " + actualWork.toHours() + "小时" +
(actualWork.toMinutes() % 60) + "分钟");
// 示例4: 倒计时计算
System.out.println("\n4. 活动倒计时");
LocalDateTime eventTime = LocalDateTime.of(2024, 12, 25, 0, 0);
LocalDateTime nowTime = LocalDateTime.now();
Duration countdown = Duration.between(nowTime, eventTime);
long days = countdown.toDays();
long hoursLeft = countdown.toHours() % 24;
long minutesLeft = countdown.toMinutes() % 60;
System.out.println("活动时间: " + eventTime);
System.out.println("倒计时: " + days + "天" +
hoursLeft + "小时" +
minutesLeft + "分钟");
}
}
九、DateTimeFormatter格式化
9.1 日期时间格式化
package com.example.datetime;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
/**
* DateTimeFormatter详解 - 日期时间格式化与解析
*/
public class DateTimeFormatterDemo {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
// === 1. 预定义格式 ===
System.out.println("=== 预定义格式 ===");
System.out.println("ISO_LOCAL_DATE: " +
now.format(DateTimeFormatter.ISO_LOCAL_DATE));
System.out.println("ISO_LOCAL_TIME: " +
now.format(DateTimeFormatter.ISO_LOCAL_TIME));
System.out.println("ISO_LOCAL_DATE_TIME: " +
now.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
System.out.println("ISO_DATE_TIME: " +
now.atZone(ZoneId.systemDefault()).format(DateTimeFormatter.ISO_DATE_TIME));
// === 2. 自定义格式 ===
System.out.println("\n=== 自定义格式 ===");
DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("MM/dd/yyyy");
DateTimeFormatter formatter4 = DateTimeFormatter.ofPattern("E, MMM dd yyyy", Locale.US);
System.out.println("yyyy-MM-dd HH:mm:ss: " + now.format(formatter1));
System.out.println("中文格式: " + now.format(formatter2));
System.out.println("美式日期: " + now.format(formatter3));
System.out.println("英文格式: " + now.format(formatter4));
// === 3. 本地化格式 ===
System.out.println("\n=== 本地化格式 ===");
DateTimeFormatter shortFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT)
.withLocale(Locale.CHINA);
DateTimeFormatter mediumFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM)
.withLocale(Locale.CHINA);
DateTimeFormatter longFormatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG)
.withLocale(Locale.CHINA);
System.out.println("SHORT: " + now.format(shortFormatter));
System.out.println("MEDIUM: " + now.format(mediumFormatter));
System.out.println("LONG: " + now.format(longFormatter));
// === 4. 解析字符串 ===
System.out.println("\n=== 解析字符串 ===");
String dateStr1 = "2024-11-20";
String dateStr2 = "2024-11-20 14:30:00";
String dateStr3 = "2024年11月20日 14:30:00";
LocalDate parsedDate = LocalDate.parse(dateStr1, DateTimeFormatter.ISO_LOCAL_DATE);
LocalDateTime parsedDateTime = LocalDateTime.parse(dateStr2, formatter1);
LocalDateTime parsedDateTime2 = LocalDateTime.parse(dateStr3, formatter2);
System.out.println("解析1: " + parsedDate);
System.out.println("解析2: " + parsedDateTime);
System.out.println("解析3: " + parsedDateTime2);
// === 5. 格式化符号说明 ===
demonstratePatternSymbols();
// === 6. 实战应用 ===
demonstrateFormatterExamples();
}
/**
* 格式化符号说明
*/
private static void demonstratePatternSymbols() {
System.out.println("\n=== 格式化符号 ===");
LocalDateTime dt = LocalDateTime.of(2024, 11, 20, 14, 5, 9);
System.out.println("符号\t说明\t\t示例");
System.out.println("----\t----\t\t----");
System.out.println("y\t年\t\t" + dt.format(DateTimeFormatter.ofPattern("yyyy")));
System.out.println("M\t月\t\t" + dt.format(DateTimeFormatter.ofPattern("MM")));
System.out.println("d\t日\t\t" + dt.format(DateTimeFormatter.ofPattern("dd")));
System.out.println("H\t时(24)\t\t" + dt.format(DateTimeFormatter.ofPattern("HH")));
System.out.println("h\t时(12)\t\t" + dt.format(DateTimeFormatter.ofPattern("hh")));
System.out.println("m\t分\t\t" + dt.format(DateTimeFormatter.ofPattern("mm")));
System.out.println("s\t秒\t\t" + dt.format(DateTimeFormatter.ofPattern("ss")));
System.out.println("S\t毫秒\t\t" + dt.format(DateTimeFormatter.ofPattern("SSS")));
System.out.println("E\t星期\t\t" + dt.format(DateTimeFormatter.ofPattern("E")));
System.out.println("a\tAM/PM\t\t" + dt.format(DateTimeFormatter.ofPattern("a")));
}
/**
* 实战应用示例
*/
private static void demonstrateFormatterExamples() {
System.out.println("\n=== 实战应用 ===");
// 示例1: 日志时间格式
System.out.println("1. 日志时间格式");
DateTimeFormatter logFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
String logTime = LocalDateTime.now().format(logFormatter);
System.out.println("[" + logTime + "] INFO - Application started");
// 示例2: 文件名时间戳
System.out.println("\n2. 文件名时间戳");
DateTimeFormatter fileFormatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
String filename = "backup_" + LocalDateTime.now().format(fileFormatter) + ".sql";
System.out.println("文件名: " + filename);
// 示例3: 友好的时间显示
System.out.println("\n3. 用户友好格式");
LocalDateTime eventTime = LocalDateTime.of(2024, 12, 25, 18, 30);
DateTimeFormatter friendlyFormatter = DateTimeFormatter.ofPattern(
"yyyy年MM月dd日 E HH:mm", Locale.CHINA);
System.out.println("活动时间: " + eventTime.format(friendlyFormatter));
// 示例4: API接口时间格式
System.out.println("\n4. API接口格式(ISO 8601)");
ZonedDateTime apiTime = ZonedDateTime.now();
String iso8601 = apiTime.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME);
System.out.println("API响应时间: " + iso8601);
// 示例5: 数据库时间格式
System.out.println("\n5. 数据库时间格式");
DateTimeFormatter dbFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dbTime = LocalDateTime.now().format(dbFormatter);
System.out.println("INSERT INTO logs (time) VALUES ('" + dbTime + "')");
}
}
十、实战应用
10.1 订单超时处理系统
package com.example.datetime.practice;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* 实战案例1: 订单超时处理系统
*/
public class OrderTimeoutSystem {
private static final int TIMEOUT_MINUTES = 30; // 30分钟超时
public static void main(String[] args) {
// 模拟订单数据
List<Order> orders = Arrays.asList(
new Order("ORD001", LocalDateTime.now().minusMinutes(45), OrderStatus.UNPAID),
new Order("ORD002", LocalDateTime.now().minusMinutes(15), OrderStatus.UNPAID),
new Order("ORD003", LocalDateTime.now().minusMinutes(35), OrderStatus.UNPAID),
new Order("ORD004", LocalDateTime.now().minusHours(2), OrderStatus.PAID),
new Order("ORD005", LocalDateTime.now().minusMinutes(5), OrderStatus.UNPAID)
);
System.out.println("=== 订单超时检查系统 ===\n");
// 检查超时订单
List<Order> timeoutOrders = checkTimeoutOrders(orders);
System.out.println("超时订单数量: " + timeoutOrders.size());
System.out.println("\n超时订单详情:");
for (Order order : timeoutOrders) {
Duration timeout = Duration.between(order.getCreateTime(), LocalDateTime.now());
long minutes = timeout.toMinutes();
System.out.println("订单号: " + order.getOrderNo());
System.out.println(" 创建时间: " + order.getCreateTime().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
System.out.println(" 超时时长: " + minutes + "分钟");
System.out.println(" 应关闭时间: " + order.getCreateTime()
.plusMinutes(TIMEOUT_MINUTES).format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
System.out.println();
// 自动关闭订单
order.setStatus(OrderStatus.CLOSED);
}
// 统计信息
printStatistics(orders);
}
/**
* 检查超时订单
*/
private static List<Order> checkTimeoutOrders(List<Order> orders) {
LocalDateTime now = LocalDateTime.now();
return orders.stream()
.filter(order -> order.getStatus() == OrderStatus.UNPAID)
.filter(order -> {
LocalDateTime deadline = order.getCreateTime().plusMinutes(TIMEOUT_MINUTES);
return now.isAfter(deadline);
})
.collect(Collectors.toList());
}
/**
* 打印统计信息
*/
private static void printStatistics(List<Order> orders) {
System.out.println("=== 订单统计 ===");
Map<OrderStatus, Long> statusCount = orders.stream()
.collect(Collectors.groupingBy(Order::getStatus, Collectors.counting()));
statusCount.forEach((status, count) ->
System.out.println(status + ": " + count + "个"));
}
}
/**
* 订单状态
*/
enum OrderStatus {
UNPAID("未支付"),
PAID("已支付"),
CLOSED("已关闭");
private String desc;
OrderStatus(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return desc;
}
}
/**
* 订单实体
*/
class Order {
private String orderNo;
private LocalDateTime createTime;
private OrderStatus status;
public Order(String orderNo, LocalDateTime createTime, OrderStatus status) {
this.orderNo = orderNo;
this.createTime = createTime;
this.status = status;
}
public String getOrderNo() { return orderNo; }
public LocalDateTime getCreateTime() { return createTime; }
public OrderStatus getStatus() { return status; }
public void setStatus(OrderStatus status) { this.status = status; }
}
10.2 排班系统
package com.example.datetime.practice;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
/**
* 实战案例2: 员工排班系统
*/
public class ScheduleSystem {
public static void main(String[] args) {
System.out.println("=== 员工排班系统 ===\n");
// 生成本周排班
LocalDate weekStart = LocalDate.now().with(DayOfWeek.MONDAY);
generateWeekSchedule(weekStart);
System.out.println("\n=== 排班冲突检查 ===\n");
// 检查排班冲突
List<Shift> shifts = Arrays.asList(
new Shift("员工A", LocalDateTime.of(2024, 11, 20, 9, 0),
LocalDateTime.of(2024, 11, 20, 17, 0)),
new Shift("员工B", LocalDateTime.of(2024, 11, 20, 14, 0),
LocalDateTime.of(2024, 11, 20, 22, 0)),
new Shift("员工A", LocalDateTime.of(2024, 11, 20, 15, 0),
LocalDateTime.of(2024, 11, 20, 23, 0)) // 冲突
);
checkShiftConflicts(shifts);
// 计算工作时长
System.out.println("\n=== 工作时长统计 ===");
calculateWorkHours(shifts);
}
/**
* 生成一周排班表
*/
private static void generateWeekSchedule(LocalDate startDate) {
System.out.println("本周排班表 (从 " + startDate + " 开始)\n");
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM-dd E", Locale.CHINA);
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm");
for (int i = 0; i < 7; i++) {
LocalDate date = startDate.plusDays(i);
DayOfWeek dayOfWeek = date.getDayOfWeek();
// 判断是否工作日
boolean isWorkday = dayOfWeek != DayOfWeek.SATURDAY &&
dayOfWeek != DayOfWeek.SUNDAY;
System.out.print(date.format(dateFormatter) + ": ");
if (isWorkday) {
LocalTime morningStart = LocalTime.of(9, 0);
LocalTime morningEnd = LocalTime.of(12, 0);
LocalTime afternoonStart = LocalTime.of(14, 0);
LocalTime afternoonEnd = LocalTime.of(18, 0);
System.out.println("上班 " +
morningStart.format(timeFormatter) + "-" +
morningEnd.format(timeFormatter) + ", " +
afternoonStart.format(timeFormatter) + "-" +
afternoonEnd.format(timeFormatter));
} else {
System.out.println("休息");
}
}
}
/**
* 检查排班冲突
*/
private static void checkShiftConflicts(List<Shift> shifts) {
for (int i = 0; i < shifts.size(); i++) {
for (int j = i + 1; j < shifts.size(); j++) {
Shift shift1 = shifts.get(i);
Shift shift2 = shifts.get(j);
// 同一员工
if (shift1.getEmployeeName().equals(shift2.getEmployeeName())) {
// 检查时间重叠
if (isOverlap(shift1.getStartTime(), shift1.getEndTime(),
shift2.getStartTime(), shift2.getEndTime())) {
System.out.println("⚠️ 冲突警告:");
System.out.println(" 员工: " + shift1.getEmployeeName());
System.out.println(" 班次1: " + formatShift(shift1));
System.out.println(" 班次2: " + formatShift(shift2));
System.out.println();
}
}
}
}
}
/**
* 判断时间段是否重叠
*/
private static boolean isOverlap(LocalDateTime start1, LocalDateTime end1,
LocalDateTime start2, LocalDateTime end2) {
return start1.isBefore(end2) && start2.isBefore(end1);
}
/**
* 格式化班次信息
*/
private static String formatShift(Shift shift) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd HH:mm");
return shift.getStartTime().format(formatter) + " - " +
shift.getEndTime().format(formatter);
}
/**
* 计算工作时长
*/
private static void calculateWorkHours(List<Shift> shifts) {
Map<String, Duration> employeeHours = new HashMap<>();
for (Shift shift : shifts) {
String name = shift.getEmployeeName();
Duration shiftDuration = Duration.between(shift.getStartTime(), shift.getEndTime());
employeeHours.merge(name, shiftDuration, Duration::plus);
}
employeeHours.forEach((name, duration) -> {
long hours = duration.toHours();
long minutes = duration.toMinutes() % 60;
System.out.println(name + ": " + hours + "小时" + minutes + "分钟");
});
}
}
/**
* 班次
*/
class Shift {
private String employeeName;
private LocalDateTime startTime;
private LocalDateTime endTime;
public Shift(String employeeName, LocalDateTime startTime, LocalDateTime endTime) {
this.employeeName = employeeName;
this.startTime = startTime;
this.endTime = endTime;
}
public String getEmployeeName() { return employeeName; }
public LocalDateTime getStartTime() { return startTime; }
public LocalDateTime getEndTime() { return endTime; }
}
总结
核心知识点回顾
本文深入探讨了JDK 8日期时间API,主要内容包括:
-
新旧API对比
- 旧API的痛点(非线程安全、设计不合理)
- 新API的优势(不可变、清晰的设计)
-
核心类详解
- LocalDate: 本地日期
- LocalTime: 本地时间
- LocalDateTime: 本地日期时间
- ZonedDateTime: 带时区的日期时间
- Instant: 时间戳
-
时间间隔
- Period: 日期间隔(年月日)
- Duration: 时间间隔(时分秒)
-
格式化与解析
- DateTimeFormatter: 日期时间格式化
- 预定义格式与自定义格式
-
实战应用
- 订单超时处理系统
- 员工排班系统
新旧API对比
| 维度 | 旧API (Date/Calendar) | 新API (java.time) |
|---|---|---|
| 线程安全 | 否 | 是(不可变) |
| API设计 | 混乱 | 清晰直观 |
| 时区支持 | 复杂 | 完善 |
| 月份表示 | 0-11 | 1-12 |
| 可读性 | 差 | 好 |
| 链式调用 | 不支持 | 支持 |
最佳实践
✓ 推荐做法:
1. 使用LocalDate/LocalTime/LocalDateTime处理本地时间
2. 使用ZonedDateTime处理跨时区业务
3. 使用Instant存储时间戳
4. 使用DateTimeFormatter格式化(线程安全)
5. 尽量使用不可变类型
6. Period用于日期间隔,Duration用于时间间隔
✗ 避免做法:
1. 不要再使用Date/Calendar/SimpleDateFormat
2. 不要混用新旧API
3. 不要忽略时区问题
4. 不要使用字符串存储日期时间
迁移建议
// 旧API -> 新API转换示例
// Date -> LocalDateTime
Date date = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
// LocalDateTime -> Date
LocalDateTime localDateTime = LocalDateTime.now();
Date convertedDate = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
// SimpleDateFormat -> DateTimeFormatter
// 旧: SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 新: DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");