日期时间迁移
还在用 Date 和 Calendar?
是时候迁移到 JDK 8 的日期时间 API 了。
为什么要迁移
┌─────────────────────────────────────────────────────────────────┐
│ 旧 API vs 新 API │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Date / Calendar │
│ ❌ 线程不安全 │
│ ❌ 月份从 0 开始(容易出错) │
│ ❌ 大量 mutable 方法 │
│ ❌ API 设计混乱 │
│ │
│ java.time │
│ ✅ 线程安全 │
│ ✅ 月份从 1 开始(自然) │
│ ✅ 不可变对象 │
│ ✅ 清晰的设计 │
│ │
└─────────────────────────────────────────────────────────────────┘迁移对照表
| 旧 API | 新 API | 说明 |
|---|---|---|
Date | Instant, LocalDateTime | 时间点 |
Calendar | LocalDate, LocalTime, LocalDateTime | 日历 |
SimpleDateFormat | DateTimeFormatter | 格式化 |
TimeZone | ZoneId | 时区 |
GregorianCalendar | ZonedDateTime | 带时区 |
Date 迁移
创建 Date
java
// ❌ 旧方式
Date now = new Date();
Date date = new Date(System.currentTimeMillis());
// ✅ 新方式
Instant instant = Instant.now();
Date date = Date.from(instant);Date 转其他类型
java
Date date = new Date();
// Date → Instant
Instant instant = date.toInstant();
// Instant → LocalDateTime(需要时区)
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
// Instant → LocalDate
LocalDate ld = instant.atZone(ZoneId.systemDefault()).toLocalDate();其他类型转 Date
java
LocalDateTime ldt = LocalDateTime.now();
Instant instant = ldt.toInstant(ZoneOffset.UTC);
Date date = Date.from(instant);Calendar 迁移
创建 Calendar
java
// ❌ 旧方式
Calendar cal = Calendar.getInstance();
cal.set(2024, Calendar.MARCH, 15, 14, 30, 45); // ❌ 月份从 0 开始!
// ✅ 新方式
LocalDateTime dt = LocalDateTime.of(2024, 3, 15, 14, 30, 45);Calendar 常用操作
java
// ❌ 旧方式:加一天
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DAY_OF_MONTH, 1);
// ✅ 新方式
LocalDateTime dt = LocalDateTime.now();
LocalDateTime tomorrow = dt.plusDays(1);获取组件
java
// ❌ 旧方式
Calendar cal = Calendar.getInstance();
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1; // ❌ 要 +1
int day = cal.get(Calendar.DAY_OF_MONTH);
int hour = cal.get(Calendar.HOUR_OF_DAY);
// ✅ 新方式
LocalDateTime dt = LocalDateTime.now();
int year = dt.getYear();
int month = dt.getMonthValue(); // ✅ 直接就是 1-12
int day = dt.getDayOfMonth();
int hour = dt.getHour();SimpleDateFormat 迁移
格式化和解析
java
// ❌ 旧方式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formatted = sdf.format(new Date());
Date parsed = sdf.parse("2024-03-15 14:30:45");
// ✅ 新方式
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String formatted = LocalDateTime.now().format(fmt);
LocalDateTime parsed = LocalDateTime.parse("2024-03-15 14:30:45", fmt);线程安全问题
java
// ❌ 旧方式:SimpleDateFormat 不是线程安全的
// 多个线程共用一个 SimpleDateFormat 会出问题
// ✅ 新方式:DateTimeFormatter 是线程安全的
// 可以定义为 static final
private static final DateTimeFormatter FMT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");TimeZone 迁移
java
// ❌ 旧方式
TimeZone tz = TimeZone.getTimeZone("America/New_York");
cal.setTimeZone(tz);
// ✅ 新方式
ZoneId zone = ZoneId.of("America/New_York");
ZonedDateTime zdt = LocalDateTime.now().atZone(zone);完整迁移示例
场景:数据库日期字段处理
java
// ❌ 旧方式
public Date parseFromDb(String dbDate) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return sdf.parse(dbDate);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
public String toDb(Date date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
// ✅ 新方式
private static final DateTimeFormatter FMT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public Instant parseFromDb(String dbDate) {
return LocalDateTime.parse(dbDate, FMT)
.toInstant(ZoneOffset.UTC);
}
public String toDb(Instant instant) {
return LocalDateTime.ofInstant(instant, ZoneOffset.UTC)
.format(FMT);
}场景:计算两个日期之间的天数
java
// ❌ 旧方式
public long daysBetween(Date start, Date end) {
long diff = end.getTime() - start.getTime();
return TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
}
// ✅ 新方式
public long daysBetween(LocalDate start, LocalDate end) {
return ChronoUnit.DAYS.between(start, end);
}注意事项
月份从 1 开始
java
// ❌ 旧 API:月份从 0 开始
Calendar cal = Calendar.getInstance();
cal.set(Calendar.MONTH, 2); // 设置为 3 月
// ✅ 新 API:月份从 1 开始
LocalDateTime dt = LocalDateTime.now();
LocalDateTime march = dt.withMonth(3); // 设置为 3 月时区转换
java
// 数据库存储 UTC,Java 显示本地时间
Instant utcInstant = Instant.now(); // 假设这是从 DB 读出的 UTC 时间
// DB → 本地显示
LocalDateTime local = LocalDateTime.ofInstant(utcInstant, ZoneId.systemDefault());
// 本地输入 → DB 存储
LocalDateTime localInput = LocalDateTime.now();
Instant utc = localInput.toInstant(ZoneOffset.UTC);
// 存入数据库小结
迁移检查清单:
- [ ]
Date→Instant/LocalDateTime - [ ]
Calendar→LocalDate/LocalTime/LocalDateTime - [ ]
SimpleDateFormat→DateTimeFormatter - [ ]
TimeZone→ZoneId - [ ] 月份处理是否正确(是否从 1 开始)
- [ ] 时区转换是否正确
记住:新 API 线程安全、性能更好、功能更完整。
