在 Java 中,处理日期和时间的类经历了重大演进。根据你使用的 Java 版本不同,推荐使用不同的 API。以下是 全面、清晰、现代 的 Java 时间处理类总结(截至 Java 21):


✅ 推荐:Java 8+ 使用 java.time 包(JSR-310)—— 现代、线程安全、易用

这是目前最推荐的方式,设计优秀,避免了旧 API 的诸多坑。

核心类概览

用途 示例
LocalDateTime 不带时区的日期+时间(如:2026-02-09T17:30:45) 业务日志、本地事件
LocalDate 仅日期(如:2026-02-09) 生日、节假日
LocalTime 仅时间(如:17:30:45) 营业时间、闹钟
ZonedDateTime 带时区的完整时间(如:2026-02-09T17:30:45+08:00[Asia/Shanghai]) 全球应用、跨时区调度
Instant 时间戳(UTC,从 1970-01-01T00:00:00Z 开始的纳秒数) 系统日志、数据库存储
Duration 两个时间点之间的时间量(如:2 小时 30 分) 计时、间隔
Period 两个日期之间的日期量(如:1 年 2 个月) 年龄、合同周期
DateTimeFormatter 格式化与解析(替代 SimpleDateFormat "yyyy-MM-dd HH:mm:ss"

📌 常用示例(Java 8+)

import java.time.*;
import java.time.format.DateTimeFormatter;

public class ModernTimeExample {
    public static void main(String[] args) {
        // 1. 获取当前时间(系统默认时区)
        LocalDateTime now = LocalDateTime.now();
        LocalDate today = LocalDate.now();
        ZonedDateTime zonedNow = ZonedDateTime.now(); // 带时区

        // 2. 创建指定时间
        LocalDateTime dt = LocalDateTime.of(2026, 2, 9, 17, 30, 45);
        LocalDate date = LocalDate.of(2026, Month.FEBRUARY, 9);

        // 3. 格式化输出
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        System.out.println(now.format(formatter)); // 2026-02-09 17:30:45

        // 4. 解析字符串
        LocalDateTime parsed = LocalDateTime.parse("2026-02-09 17:30:45", formatter);

        // 5. 加减操作(不可变,返回新对象)
        LocalDateTime tomorrow = now.plusDays(1);
        LocalDateTime oneHourAgo = now.minusHours(1);

        // 6. 转为时间戳(用于数据库或网络传输)
        Instant instant = now.atZone(ZoneId.systemDefault()).toInstant();
        long epochMilli = instant.toEpochMilli(); // 毫秒时间戳

        // 7. 从时间戳创建
        LocalDateTime fromTs = LocalDateTime.ofInstant(
            Instant.ofEpochMilli(epochMilli), 
            ZoneId.systemDefault()
        );
    }
}

✅ 优点:不可变、线程安全、API 清晰、无夏令时陷阱(合理处理)


⚠️ 旧版 API(Java 8 之前)—— 不推荐新项目使用

这些类存在设计缺陷(非线程安全、月份从 0 开始、可变等),但你可能在老代码中见到。

问题 替代方案
java.util.Date 表示时间戳,但命名误导(实际包含时间),可变,非线程安全 InstantLocalDateTime
java.util.Calendar 复杂、月份从 0 开始(Jan=0)、可变、非线程安全 LocalDateTime / ZonedDateTime
java.text.SimpleDateFormat 非线程安全! 格式化性能差 DateTimeFormatter

❌ 旧 API 示例(仅作对比)

// 危险!SimpleDateFormat 非线程安全
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse("2026-02-09 17:30:45"); // 注意:月份不用减1?其实这里字符串解析没问题,但构造 Date 时 Calendar.MONTH 从0开始!

// Calendar 月份从0开始(反人类)
Calendar cal = Calendar.getInstance();
cal.set(2026, Calendar.FEBRUARY, 9); // 正确(因为 Calendar.FEBRUARY = 1)
// cal.set(2026, 1, 9); // 也是2月,但容易混淆

🔁 新旧 API 互转(兼容老系统)

// Date → Instant → LocalDateTime
Date oldDate = new Date();
Instant instant = oldDate.toInstant();
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());

// LocalDateTime → Date
LocalDateTime ldt = LocalDateTime.now();
Date newDate = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

📌 最佳实践建议

  1. 新项目一律使用 java.time(JSR-310)
  2. 永远不要在多线程环境中共享 SimpleDateFormat
  3. 存储时间戳到数据库时,优先用 Instant(UTC)或 long 毫秒值
  4. 显示给用户时,再根据其时区格式化(如 ZonedDateTime
  5. 避免使用 System.currentTimeMillis() 做业务逻辑,改用 Instant.now()

🧩 补充:第三方库(通常不需要)

  • Joda-Timejava.time 的前身,Java 8 后官方已吸收其优点,无需再引入
  • ThreeTenBP:Android 或 Java 6/7 项目可使用(Backport of JSR-310)

总结

场景 推荐类
本地日期时间(无时区) LocalDateTime
仅日期 LocalDate
带时区的时间 ZonedDateTime
时间戳(系统/数据库) Instant
格式化/解析 DateTimeFormatter
时间间隔 Duration(时分秒)、Period(年月日)

💡 记住:Java 8 的 java.time 是处理时间的终极答案,简洁、安全、强大。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐