Java 日历:凌晨 1 点切换到标准时间(从 DST 开始)

Posted

技术标签:

【中文标题】Java 日历:凌晨 1 点切换到标准时间(从 DST 开始)【英文标题】:Java Calendar: 1am switchover to standard time (from DST) 【发布时间】:2012-10-28 02:14:40 【问题描述】:

我有一些代码使用 Calendar.set() 返回给定日期值的小时开始。我在 2012 年 11 月 4 日星期日(东部时区 - EDT 到 EST 切换)遇到了以下问题:

public void testStartOfHourDST1() 
    
    Calendar cal = Calendar.getInstance();
    
    long time = 1352005200000L; // Nov 4, 2012, 1AM EDT
    cal.setTimeInMillis(time);
        
    cal.set(Calendar.MILLISECOND, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MINUTE, 0);

    System.out.println(new Date(time));
    System.out.println(new Date(cal.getTimeInMillis()));
    System.out.println(System.getProperty("java.version"));
            
    assertEquals(cal.getTimeInMillis(), time); // fails
    
    return;

输出:

2012 年 11 月 4 日星期日 01:00:00 EDT

2012 年 11 月 4 日星期日 01:00:00 EST

1.6.0_35

也许这不是使用日历的正确方法,但在下一小时(或前一小时)运行相同的测试可以正常工作。这是 JVM 问题吗?

谢谢

【问题讨论】:

【参考方案1】:

这不是一个真正的问题,因为它是确定性的并且按照它的程序来做。如果您希望它选择两个凌晨 1 点中较早的一个,这是一个问题!

更改日历上的字段后,它拥有的唯一信息是“美国/东部时间凌晨 1 点”。好吧,你的时区那天有两个凌晨 1 点,应该选择哪一个? OpenJDK 的作者做出了一个决定,当出现这种歧义时,他们总是将其解释为标准时间的后者。此评论来自 java.util.GregorianCalendar OpenJDK 6:

    // 2. The transition out of DST.  Here, a designated time of 1:00 am - 1:59 am
    //    can be in standard or DST.  Both are valid representations (the rep
    //    jumps from 1:59:59 DST to 1:00:00 Std).
    //    Again, we assume standard time.

如果您打印出数字的实际值,您将看到 cal.getTimeInMillis() 实际上比 time 的值改变了一个小时。

【讨论】:

好点。因此,Calendar 似乎不支持消除这两个重叠小时的歧义(使用 setTime/setTimeInMillis 或 @Nambari 提到的使用 setTimeZone 除外。 日历是使用最近的时间还是在创建日历时应用默认时区(如果程序在已发生夏令时切换)...我不知道答案。 @jahroy java.util.GregorianCalendar 代码中的 cmets 表明它将始终将当天的 1:00-1:59 解释为标准时间,而不是夏令时任意约定。 " ...在这里,指定时间 1:00 am - 1:59 am 可以是标准时间或 DST。两者都是有效的表示(代表从 DST 1:59:59 跳转到 1:00:00 Std)。同样,我们假设标准时间。”【参考方案2】:

获取正确的时区非常重要。例如,我确信您知道系统当前时间(以毫秒为单位)是从 1970 年 1 月 1 日午夜开始测量的。这是有据可查的。 Date 构造函数 JavaDoc 说:

公开日期(长日期)

分配一个 Date 对象并初始化它以表示指定的 自标准基准时间以来的毫秒数,称为“ 纪元”,即 1970 年 1 月 1 日 00:00:00 GMT。

这很好,但是一开始这个程序的输出有点难以理解:

import java.util.Date;
import java.util.TimeZone;
import java.text.SimpleDateFormat;

public class Demo 
    public static void main(String[] args) 
        TimeZone tz = TimeZone.getTimeZone("Europe/London");
        Date d = new Date(-3600000);

        SimpleDateFormat df = new SimpleDateFormat("MMMM dd, yyyy HH:mm:ss.SSS z");
        df.setTimeZone(tz);
        System.out.println(String.format("%8dms -> %s",
                    Long.valueOf(d.getTime()) ,df.format(d)));

        d.setTime(0);
        System.out.println(String.format("%8dms -> %s",
                    Long.valueOf(d.getTime()) ,df.format(d)));
   

输出是:

-3600000ms -> January 01, 1970 00:00:00.000 GMT
       0ms -> January 01, 1970 01:00:00.000 GMT

如您所见,纪元显然偏移了一个小时!

除非它不是。如果您明确使用“GMT”时区,一切都很好。 “欧洲/伦敦”时区就是这么棘手。

使用日历时,请了解您正在使用的时区,否则会被抓到。

【讨论】:

感谢您提供信息,但这个示例有点令人困惑,因为日期格式会针对不同的时区打印“GMT”。如果您使用“Z”输出格式,它将显示 +0100(很清楚)。因此,您的示例的问题不在于欧洲/伦敦“棘手”,而在于字符串输出不明确。我的错误是没有意识到 set() 函数会导致 Calendar 从头开始​​重新评估其 UTC 时间(因此在必须弄清楚要使用哪个 '1am' 之间感到困惑)。最初的问题只发生在支持 DST 偏移的时区。

以上是关于Java 日历:凌晨 1 点切换到标准时间(从 DST 开始)的主要内容,如果未能解决你的问题,请参考以下文章

CST时间凌晨12点是北京时间几点?

java日历的DST问题

utc14点是北京时间几点

Linux基础命令的操作(时间与日期,日历,计算器)

时间编程

django 中的日期时间错误