首页
学习
活动
专区
圈层
工具
发布

强烈建议你不要再使用Date类了!!!

很多人第一次接触 Java 操作时间时,都是从Date开始的。那会儿还在学校写作业,能把new Date()打印出来看到一串数字就挺开心的。但等到真进了公司,遇到一点时间计算、时区、格式化的需求……基本上十个里有九个被坑得体无完肤。

1. Date 的设计真的很“古早”

Date类最早是 1995 年跟着 Java 1.0 一起诞生的。那时候没有考虑多线程、国际化、时区差异这些复杂的东西,它的职责又多又杂,设计极其反人类。

比如:

Date date = new Date(2025, 10, 3);  // 输出啥你猜?

System.out.println(date);

结果你会发现打印出来的年份比你输入的多了1900年。是的,Date的年份是从 1900 开始算的。传 2025 其实是 3925 年。

还有月份,Date的月份是从 0 开始的。你写 10,实际是 11 月。

这些历史包袱让Date的 API 既不直观,又极容易出错。要不是兼容老项目,现在没人会用它。

2. SimpleDateFormat 线程不安全的“神坑”

最常见的翻车点之一,就是多线程格式化时间。

SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

ExecutorService pool = Executors.newFixedThreadPool(5);

for (int i = 0; i < 10; i++) {

  pool.submit(() -> {

      System.out.println(sdf.format(new Date()));

  });

}

这段代码在单线程下没问题,但多线程时就会出现各种奇怪的格式,比如日期错乱、时间乱码甚至抛异常。 原因是SimpleDateFormat内部维护了可变状态,多线程共享同一个实例时会互相干扰。

解决办法? 要么每次都新建一个SimpleDateFormat,要么加锁(性能差),要么用ThreadLocal包一层(麻烦)。 所以后来才有了更安全的替代品:DateTimeFormatter。

3. Calendar:从坑里走出来,又掉进了另一个坑

Java 1.1 为了解决Date不够用的问题,引入了Calendar。结果越补越乱。 看下面这段代码:

Calendar calendar = Calendar.getInstance();

calendar.set(2025, 10, 3, 14, 30, 0);

Date date = calendar.getTime();

System.out.println(date);

打印结果依旧是 11 月(月份依旧从 0 开始),而且Calendar的 API 极其臃肿,字段多得离谱:YEAR、MONTH、WEEK_OF_YEAR、DAY_OF_MONTH、HOUR_OF_DAY……每个方法都像雷区,稍不注意就会算错。

再比如,你想让时间加一天:

calendar.add(Calendar.DAY_OF_MONTH, 1);

表面上没问题,但如果这一天跨了月或者跨了年,那行为可能完全不是你想的那样。 更别提它和Date之间频繁转换的麻烦程度,真是一言难尽。

4. Java 8 的救世主:LocalDateTime 系列

直到 Java 8 的java.time包出现,这些问题才算真正解决。LocalDate、LocalTime、LocalDateTime、Instant、ZoneId、ZonedDateTime等类不仅线程安全,而且设计直观,API 语义清晰。

举个例子,同样是获取当前时间并加一天:

LocalDateTime now = LocalDateTime.now();

LocalDateTime tomorrow = now.plusDays(1);

System.out.println("今天:" + now);

System.out.println("明天:" + tomorrow);

就这么简单,而且不会有时区错乱、月份偏移的坑。

如果你要转成时间戳,也很方便:

long timestamp = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();

System.out.println("当前时间戳:" + timestamp);

格式化和解析也有安全又优雅的写法:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

String formatted = now.format(formatter);

LocalDateTime parsed = LocalDateTime.parse(formatted, formatter);

System.out.println(formatted);

System.out.println(parsed);

再也不用担心线程安全问题,也不用在脑子里算“月份减一”这种反人类的逻辑。

5. 兼容老项目怎么办?

如果项目里到处都是Date,也不是非得一夜之间改完。 你可以逐步过渡,比如利用Instant进行转换:

// Date -> LocalDateTime

Date date = new Date();

LocalDateTime ldt = date.toInstant()

      .atZone(ZoneId.systemDefault())

      .toLocalDateTime();

// LocalDateTime -> Date

Date newDate = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

这样你就可以慢慢替换旧逻辑,而不用一下推翻全部代码。

6. 总结

Date和Calendar是“能跑但不该用”的遗产。 在现代 Java 开发中,时间操作首选java.time,这是线程安全、语义清晰、国际化友好的标准库。

如果你还在用Date来做时间计算、格式化,那真该停下来想一想了。 毕竟,时间问题一旦出错,影响的往往不只是日志,而是系统逻辑、财务计算,甚至跨时区的业务结算。

别等到线上多加一天工资的时候,才意识到这锅原来是Date背的。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OeoDZPUJnh0Gn226_Pvb4miA0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。
领券