You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Calendar调用clear()方法触发DST夏令时偏移异常问题咨询

我已知晓Java Calendar类正被其他公认更优的类库所替代,本次我遇到了该类在夏令时结束的重叠小时场景下的异常问题,具体复现逻辑如下:

public void annoying_issue()
{
    Calendar midnightPDT = Calendar.getInstance(TimeZone.getTimeZone("US/Pacific"));
    midnightPDT.set(Calendar.YEAR, 2021);
    midnightPDT.set(Calendar.MONTH, 10);
    midnightPDT.set(Calendar.DAY_OF_MONTH, 7);
    midnightPDT.set(Calendar.HOUR_OF_DAY, 0);
    midnightPDT.set(Calendar.MINUTE, 0);
    midnightPDT.set(Calendar.SECOND, 0);
    midnightPDT.set(Calendar.MILLISECOND, 0);

    Calendar oneAMPDT = Calendar.getInstance(TimeZone.getTimeZone("US/Pacific"));
    oneAMPDT.setTimeInMillis(midnightPDT.getTimeInMillis() + (60*60*1000));// 夏令时重叠时段的第一个1点(PDT)

    System.out.println(new Date(midnightPDT.getTimeInMillis()));// 输出符合预期:Sun Nov 7 00:00:00 PDT 2021
    System.out.println(new Date(oneAMPDT.getTimeInMillis()));// 输出符合预期:Sun Nov 7 01:00:00 PDT 2021

    oneAMPDT.clear(Calendar.MINUTE);// 此时MINUTE字段已经是0,理论上不会产生任何变更
   
    // 实际结果不符合预期:时区切换为PST,时间戳增加了3600000毫秒
    System.out.println(new Date(oneAMPDT.getTimeInMillis()));// 输出:Sun Nov 7 01:00:00 PST 2021
}

我首先构造了美国太平洋时区2021年11月7日零点的Calendar实例,累加1小时得到PDT时区的凌晨1点实例,此时打印输出符合预期。但当我对该实例执行clear(Calendar.MINUTE)操作时,此时MINUTE字段本身取值为0,理论上不会产生任何变更,实际却发现实例时区切换为PST,时间戳增加了3600000毫秒,打印输出变为PST时区的11月7日凌晨1点。我调用oneAMPDT.set(Calendar.MINUTE, 0)也会触发相同的异常。请问该行为属于Calendar的预期设计吗?是否有方案可以避免该问题?


问题解答

该行为是否属于预期设计?

是,这是java.util.Calendar的固有设计特性,并非bug。
其底层逻辑是:Calendar内部采用延迟计算机制,且默认对夏令时结束产生的重叠本地时间,采用后出现的时刻作为解析结果:

  1. 通过setTimeInMillis初始化oneAMPDT实例时,传入的时间戳对应的时区偏移是固定的PDT(UTC-7),此时输出正常。
  2. 调用clear(Calendar.MINUTE)或set(Calendar.MINUTE, 0)时,会触发Calendar的本地时间字段重计算逻辑:它会放弃已有的时间戳和偏移量信息,仅用年、月、日、时、分、秒等本地字段重新解析时间戳。而2021年11月7日1:00在太平洋时区对应两个不同的时间戳:PDT的1:00和1小时后PST的1:00,Calendar默认选择后者,因此出现了时间戳增加1小时、时区切换为PST的现象。

可行的规避方案

  • 优先使用Java 8及以上版本内置的java.time包(JSR-310时间API),这也是官方推荐的Calendar替代方案。ZonedDateTime支持自定义夏令时重叠场景的解析策略,可以明确指定使用早/晚的偏移量,不会出现这类非预期的时间跳转。
  • 如果必须兼容旧版本Java无法使用java.time,尽量避免直接修改Calendar的单个时间字段,所有时间调整都通过add()方法或者直接操作时间戳后调用setTimeInMillis()实现,避开本地字段重计算逻辑。
  • 若必须使用set()/clear()操作字段,可以在修改后主动设置DST_OFFSET字段保留夏令时偏移,不过该方案容错性低,仅作为临时备选。

内容的提问来源于stack exchange,提问作者Jonathan Cameron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 10:54:01