新旧日期API全解析!不用头疼 Java 处理?
点击上方“程序员蜗牛g”,选择“设为星标”
跟蜗牛哥一起,每天进步一点点

程序员蜗牛g
大厂程序员一枚 跟蜗牛一起 每天进步一点点
33篇原创内容
公众号
Java新日期/时间API于Java 8中引入,并在Java 21中得到了增强,为现代应用程序中的日期和时间处理提供了强大的解决方案。
传统日期/时间API(java.util.Date、Calendar、SimpleDateFormat)存在以下问题:
-
可变(非线程安全)
-
令人困惑(月份从0开始,但日期从1开始)
-
容易出错(日期和时间混在一个类中)
-
时间区处理能力弱
-
可读性差
2.实战案例
2.1 旧日期API
-
可变性问题
public static void main(String[] args) {// 当前时间Date date = new Date();System.out.println("原始值: " + date);// 修改同一对象date.setTime(date.getTime() + 1000000000L);System.out.println("修改后: " + date) ;}
输出结果
原始值: Fri Oct 7 08:43:44 CST 2025修改后: Tue Oct 18 22:30:24 CST 2025
问题:日期对象被修改——这在多线程下是非常不友好的。
-
令人困惑的API
public static void main(String[] args) {Calendar cal = Calendar.getInstance();// 1月,11日cal.set(2025, 0, 11);System.out.println(cal.getTime());}
问题:月份从0开始。许多开发者会无意中引发了错误。
-
线程不安全的格式化
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");public static void main(String[] args) throws Exception {final int max = 100;final int iterations = 1000;CyclicBarrier barrier = new CyclicBarrier(max) ;Runnable task = () -> {try {barrier.await(); // 所有线程在此同步for (int i = 0; i < iterations; i++) {// 这里调用 format 是不安全的!FORMAT.format(new Date());}} catch (Exception e) {e.printStackTrace();System.err.println("线程 " + Thread.currentThread().getName() + " 抛出异常: " + e.getClass().getSimpleName());}};for (int i = 0; i < max; i++) {new Thread(task, "T-" + i).start();}}
问题:SimpleDateFormat 并非线程安全的,导致在并发应用中输出结果损坏。
2.2 新日期API(Java8+)
新API位于java.time包中:
-
不可变性:不会意外修改
-
职责明确分离:LocalDate → 仅日期;LocalTime → 仅时间;LocalDateTime → 日期时间(无时区);ZonedDateTime → 带时区
-
流畅且易读的API
-
不可变且安全
public static void main(String[] args) {LocalDate today = LocalDate.now();System.out.println("当前日期: " + today);// 返回新的对象LocalDate nextWeek = today.plusWeeks(1);System.out.println("下一周日期: " + nextWeek);// 并没有发生变化System.out.println("日期保持不变: " + today);}
安全:原始内容今日从未被修改。
-
告别基于 0 的月份混淆
public static void main(String[] args) {// 月份从1开始LocalDate date = LocalDate.of(2025, 1, 11) ;System.out.println(date);}
输出结果
2025-01-11
明确:月份为1至12,符合预期。
-
线程安全的格式化
public static void main(String[] args) {DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");LocalDateTime now = LocalDateTime.now();String formatted = now.format(formatter);System.out.println(formatted);}
安全:DateTimeFormatter 是不可变的且线程安全的类。
总结
旧版API(Date、Calendar、SimpleDateFormat)
-
在多线程应用中存在可变性缺陷
-
令人困惑(月份从0开始计数,职责划分不明确)
-
非线程安全
-
难以使用
新版API(java.time)
-
不可变且线程安全
-
类型清晰(LocalDate、LocalTime等)
-
易于格式化和解析
-
提供完善的时区支持
-
可读性更强
2.3 新旧API对比
-
获取当前日期
传统方法
Date today = new Date();System.out.println(today);
现代方法
LocalDate today = LocalDate.now();System.out.println(today);
-
为日期添加天数
传统方法
Calendar c = Calendar.getInstance();c.add(Calendar.DAY_OF_MONTH, 5);System.out.println(c.getTime());
现代方法
LocalDate today = LocalDate.now();LocalDate future = today.plusDays(5);System.out.println(future);
不可变的原始日期保持不变。
-
解析和格式化
传统方法
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");Date date = sdf.parse("11/01/2025");System.out.println("解析: " + date);String formatted = sdf.format(new Date());System.out.println("格式化: " + formatted);
现代方法
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");LocalDate date = LocalDate.parse("11/01/2025", formatter);System.out.println("解析:"+ date);String formatted = LocalDate.now().format(formatter);System.out.println("格式化: "formatted);
线程安全、不可变且更简洁。
-
时区处理
传统方法
Calendar c = Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai"));System.out.println(c.getTime());
现代方法
ZonedDateTime nyTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));System.out.println(nyTime);
明确、人性化且避免错误。
-
获取当前时间戳
传统方法
long millis = System.currentTimeMillis();System.out.println(millis);
现代方法
Instant now = Instant.now();System.out.println(now);
提供即时时间戳,适用于持久化存储和API调用。
2.4 最佳实践
-
始终使用现代日期/时间 API
理论:传统的 java.util.Date 和 Calendar 类存在根本性的设计缺陷,包括可变性问题、API 设计欠佳、月份索引混乱(1 月 = 0)、时区支持有限以及性能问题。
// 传统API// java.util.Date oldDate = new java.util.Date();// java.util.Calendar calendar java.util.Calendar.getInstance();// 现代APILocalDate today = LocalDate.now();LocalDateTime now = LocalDateTime.now();Instant timestamp = Instant.now();
-
根据使用场景选择正确的类
理论:Java日期/时间API针对不同的时间概念提供了专门的类。使用错误的类可能会导致难以察觉的错误和性能问题。
-
LocalDate — 不包含时间的日期(生日、截止日期、节假日)
-
LocalTime — 不包含日期的时间(营业时间、闹钟时间)
-
LocalDateTime — 不包含时区的日期和时间(适用于同一时区的应用)
-
ZonedDateTime — 包含时区的日期和时间(适用于全球应用、日程安排)
-
Instant — UTC时间点(时间戳、日志记录、系统事件)
-
Duration — 基于时间的量(小时、分钟、秒)
-
Period — 基于日期的量(年、月、日)
public static void demonstrateClasses() {// 当前日期和时间LocalDate today = LocalDate.now();LocalTime currentTime = LocalTime.now();LocalDateTime now = LocalDateTime.now();Instant timestamp = Instant.now();System.out.println("当前日期: " + today);System.out.println("当前时间: " + currentTime); // 14:30:45.123System.out.println("现在时间: " + now); // 2025-05-31T14:30:45.123System.out.println("UTC timestamp: " + timestamp); // 2025-05-31T09:00:45.1232LocalDate christmas = LocalDate.of(2025, Month.DECEMBER, 25);LocalTime meetingTime = LocalTime.of(14, 30);LocalDateTime appointment = LocalDateTime.of(christmas, meetingTime);LocalDate nextWeek = today.plusWeeks(1);LocalDate lastMonth = today.minusMonths(1);}
-
优雅处理解析错误
理论:日期/时间解析因格式变化、区域设置差异、无效日期和模糊格式而固有易出错。请始终通过多次格式尝试实现稳健的错误处理。
// 处理多种格式的灵活解析器public static LocalDate parseFlexibleDate(String dateString) {DateTimeFormatter[] formatters = { DateTimeFormatter.ISO_LOCAL_DATE, // e.g., 2025-10-08DateTimeFormatter.ofPattern("dd-MM-yyyy"), // e.g., 08-10-2025DateTimeFormatter.ofPattern("MM/dd/yyyy"), // e.g., 10/08/2025DateTimeFormatter.ofPattern("dd/MM/yyyy") // e.g., 08/10/2025};for (DateTimeFormatter formatter : formatters) {try {// 尝试使用当前格式化程序进行解析return LocalDate.parse(dateString, formatter);} catch (DateTimeParseException e) {System.out.println("Failed to parse with format: " + formatter + " → " + e.getMessage());}}// 如果没有格式化程序工作,则抛出一个明确的异常throw new IllegalArgumentException("Invalid date format: " + dateString);}public static void main(String[] args) {String[] testDates = { "2025-10-08", "08-10-2025", "10/08/2025", "08/10/2025", "invalid-date" };for (String dateStr : testDates) {try {LocalDate parsed = parseFlexibleDate(dateStr);System.out.println("Parsed Date: " + parsed);} catch (IllegalArgumentException e) {System.out.println(" Error: " + e.getMessage());}}}
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!
更多推荐


所有评论(0)