
Calendar.getInstance() 返回的是当前时间,但要注意时区陷阱
调用 Calendar.getInstance() 确实会返回一个封装了当前系统时间的 Calendar 实例,但它默认使用 JVM 启动时读取的系统默认时区(不是 UTC,也不是你本地手机设置的时区,而是 TimeZone.getDefault() 当时的快照)。如果你在服务器上部署、又没显式设置时区,很可能拿到的是 UTC+0 或容器所在主机的时区,和你本地开发环境不一致。
常见错误现象:get(Calendar.HOUR_OF_DAY) 在上海机器上返回 14,但日志里写进数据库却变成 6 —— 很可能是数据库连接或 JDBC 驱动把 Timestamp 按 UTC 解释了。
- 始终显式传入时区:用
Calendar.getInstance(TimeZone.getTimeZone("Asia/Shanghai")) - 避免依赖
TimeZone.setDefault(),它会影响整个 JVM,其他模块可能出问题 - 如果只是取“此刻毫秒数”,直接用
System.currentTimeMillis()更轻量、无歧义
get() 和 set() 方法操作年月日时分秒的典型误用
Calendar 的字段名和直觉有偏差,比如 Calendar.MONTH 是从 0 开始的(0 表示一月),Calendar.DAY_OF_WEEK 周日是 1(SUNDAY),但某些 locale 下周一才是第一天 —— 这些细节不看文档很容易错。
使用场景:想设成“2023 年 10 月 1 日 0 点 0 分”,结果写成 cal.set(2023, 10, 1, 0, 0),实际成了 2023 年 11 月 1 日。
立即学习“Java免费学习笔记(深入)”;
-
set(year, month, day)中month必须减 1,推荐用常量:cal.set(2023, Calendar.OCTOBER, 1) - 修改单个字段时,用
set(field, value);批量设多个字段,用set(year, month, ...)重载方法 - 设完别忘了
cal.getTimeInMillis()或cal.getTime()触发内部计算,否则后续get()可能返回旧值(lazy compute)
Calendar.getTime() 返回 Date 对象,但 Date 本身已过时
Calendar.getTime() 返回的是 Date,而 Date 的大部分构造器和 setter(如 setYear())早在 Java 1.1 就被标记为 @Deprecated。它内部只存毫秒,不带时区信息,打印出来却带系统默认时区格式化,极易误导。
常见错误现象:把 Calendar.getTime() 结果传给 SimpleDateFormat 格式化,却发现输出时间和你 set() 的不一致 —— 因为 SimpleDateFormat 默认也用系统时区解析/格式化,两层时区叠加出错了。
- 不要用
Date做业务逻辑,仅作兼容桥接;优先用Instant(UTC 时间线)或ZonedDateTime(带时区) - 若必须转
Date,用calendar.getTime().toInstant()再处理,比直接用Date字段安全 -
SimpleDateFormat不是线程安全的,别作为静态变量复用
为什么现在不该再用 Calendar 做新代码
Java 8 引入的 java.time 包不是“增强”,而是对 Calendar/Date 设计缺陷的彻底重写:不可变、线程安全、语义清晰、时区明确。继续用 Calendar 的唯一合理理由是维护老项目且不能升级 JDK。
性能影响:每次 get() 或 set() 都触发内部字段同步计算,比 LocalDateTime.now() 多几倍开销;兼容性上,Android API 26+ 才完整支持 java.time,但可用 ThreeTenABP 兼容库补足。
- 替代方案:当前时间用
Instant.now()(UTC),本地时间用LocalDateTime.now(),带时区用ZonedDateTime.now(ZoneId.of("Asia/Shanghai")) - 和数据库交互:JDBC 4.2+ 支持直接传
LocalDateTime/OffsetDateTime,无需转java.sql.Timestamp - 旧接口无法避免时,用
GregorianCalendar.from(ZonedDateTime)或Date.from(Instant)做最小转换
真正麻烦的从来不是“怎么写对”,而是“哪一行悄悄改了时区”或者“谁把 Calendar 实例共享到多线程里了”。这些隐性耦合,在 java.time 里根本不存在。










