在Java里Calendar类如何操作日期_Java日期计算方式说明

发布时间 - 2026-02-02 00:00:00    点击率:
set() 是设成某值,add() 是加减若干单位;set() 不触发进位,add() 自动处理溢出;设每月1日需用 set(Calendar.DAY_OF_MONTH, 1);getActualMaximum() 比硬编码31更可靠;getTimeInMillis() 需先 get() 触发重算;Calendar 非线程安全,须每次新建或用 ThreadLocal;Java 8+ 应优先使用 LocalDateTime/ZonedDateTime。

Calendar 的 set() 和 add() 到底该用哪个

直接说结论:set() 是“设成某值”,add() 是“加减若干单位”。混淆两者是 Java 日期计算中最常见的逻辑错误来源。

比如想把一个 Calendar 对象的月份设为 12 月(即 Calendar.DECEMBER),用 set(Calendar.MONTH, 11) 没问题;但若误用 add(Calendar.MONTH, 11),就会在当前月份基础上加 11 个月,结果完全不可控。

  • set() 不会触发自动进位或借位:调用 cal.set(Calendar.DAY_OF_MONTH, 32) 后,cal.get(Calendar.DAY_OF_MONTH) 可能返回 1(下月1号),这取决于底层实现和时区,行为不直观
  • add() 会自动处理溢出:如 1 月 31 日 + 1 个月 → 自动变成 2 月 28 日(或 29 日)
  • 如果要“清零某字段再设新值”,不能只靠 set();例如想设成每月 1 日,得先 set(Calendar.DAY_OF_MONTH, 1),而不是 set(Calendar.DATE, 1)(二者等价,但别混用别名)

为什么 getActualMaximum(Calendar.DAY_OF_MONTH) 比直接写 31 更可靠

因为不是每个月都有 31 天。硬编码 31 在 4 月、6 月、9 月、11 月或 2 月会出错——尤其是跨年、跨闰年时。

getActualMaximum() 返回当前 Calendar 所处月份的实际最大天数,它考虑了年份、月份、甚至时区(极少数情况下影响月末判断)。

Calendar cal = Calendar.getInstance();
cal.set(2025, Calendar.FEBRUARY, 15); // 注意:月份从 0 开始
int lastDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH); // 返回 29
cal.set(Calendar.DAY_OF_MONTH, lastDay); // 安全设为当月最后一天
  • 别用 getMaximum() ——它返回该字段理论最大值(如 DAY_OF_MONTH 总是返回 31)
  • 调用 getActualMaximum() 前,确保 Calendar 的年/月已明确设定,否则可能基于当前时间,导致意外结果
  • 这个方法不修改日历状态,只读取,可放心多次调用

Calendar.getTimeInMillis() 和 new Date().getTime() 的精度陷阱

两者都返回毫秒数,但关键区别在于:前者依赖 Calendar 内部字段是否已“计算完成”。如果刚调用 set() 就立刻取时间戳,可能拿到过期缓存值。

典型表现:设了年月日,但没设时分秒,getTimeInMillis() 返回的时间却包含旧的时分秒甚至毫秒值,导致时间偏移。

  • 安全做法是调用 cal.getTimeInMillis() 前,先执行 cal.get(Calendar.YEAR) 或任意 get() ——这会强制触发内部重算(computeTime()
  • 更稳妥的是直接调用 cal.getTime().getTime()getTime() 方法内部已保证时间同步
  • 注意:Calendar 默认时区是 JVM 启动时确定的,跨时区操作必须显式 cal.setTimeZone(TimeZone.getTimeZone("UTC"))

Calendar 的线程不安全性怎么避坑

Calendar 实例不是线程安全的。多个线程共用同一个实例,同时调用 set()add()get(),会导致字段错乱、时间计算错误,且极难复现。

这不是“尽量避免共享”的建议,而是“必须隔离”的硬性

要求。

  • 不要将 Calendar 设为静态变量或单例成员
  • 每次需要时用 Calendar.getInstance() 创建新实例(开销极小)
  • 如果频繁创建,可用 ThreadLocal 缓存,但需注意内存泄漏风险(尤其在 Web 容器中未清理)
  • Java 8+ 强烈建议迁移到 LocalDateTime / ZonedDateTime ——它们不可变、无状态、天然线程安全

Calendar 类的设计初衷是作为“可变日期计算器”,但它把状态管理、时区、历法规则全揉在一起,稍有不慎就掉进坑里。真正复杂的日期逻辑,别硬扛,尽早切到 java.time 包。


# java  # 编码  # 区别  # 为什么  # lmax  # jvm  # date  # Calendar  # 线程  # 对象  # 设为  # 个月  # 加减  # 的是  # 都有  # 尤其是  # 多个  # 会在  # 这不是  # 当月 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 宙斯浏览器视频悬浮窗怎么开启 边看视频边操作其他应用教程  Laravel如何获取当前用户信息_Laravel Auth门面获取用户ID  HTML5空格和nbsp有啥关系_nbsp的作用及使用场景【说明】  香港服务器如何优化才能显著提升网站加载速度?  小米17系列还有一款新机?主打6.9英寸大直屏和旗舰级影像  jQuery中的100个技巧汇总  西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?  Laravel PHP版本要求一览_Laravel各版本环境要求对照  做企业网站制作流程,企业网站制作基本流程有哪些?  Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】  Windows驱动无法加载错误解决方法_驱动签名验证失败处理步骤  rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted  如何确保FTP站点访问权限与数据传输安全?  Linux后台任务运行方法_nohup与&使用技巧【技巧】  使用C语言编写圣诞表白程序  如何快速生成高效建站系统源代码?  Laravel如何实现密码重置功能_Laravel密码找回与重置流程  利用python获取某年中每个月的第一天和最后一天  香港服务器网站测试全流程:性能评估、SEO加载与移动适配优化  Android仿QQ列表左滑删除操作  Laravel Livewire是什么_使用Laravel Livewire构建动态前端界面  微信h5制作网站有哪些,免费微信H5页面制作工具?  Laravel如何使用Vite进行前端资源打包?(配置示例)  php结合redis实现高并发下的抢购、秒杀功能的实例  如何快速搭建自助建站会员专属系统?  Laravel怎么使用Collection集合方法_Laravel数组操作高级函数pluck与map【手册】  JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)  详解Android图表 MPAndroidChart折线图  php打包exe后无法访问网络共享_共享权限设置方法【教程】  详解Nginx + Tomcat 反向代理 如何在高效的在一台服务器部署多个站点  怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  香港服务器网站推广:SEO优化与外贸独立站搭建策略  如何在IIS中新建站点并解决端口绑定冲突?  如何快速生成凡客建站的专业级图册?  零服务器AI建站解决方案:快速部署与云端平台低成本实践  Laravel如何实现用户密码重置功能?(完整流程代码)  如何彻底删除建站之星生成的Banner?  网页设计与网站制作内容,怎样注册网站?  如何在阿里云服务器自主搭建网站?  网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?  制作电商网页,电商供应链怎么做?  如何快速搭建虚拟主机网站?新手必看指南  php中::能调用final静态方法吗_final修饰静态方法调用规则【解答】  Laravel如何部署到服务器_线上部署Laravel项目的完整流程与步骤  *服务器网站为何频现安全漏洞?  Linux系统命令中tree命令详解  Laravel怎么为数据库表字段添加索引以优化查询  韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南  北京网站制作费用多少,建立一个公司网站的费用.有哪些部分,分别要多少钱?