2

我有一些代码使用 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问题吗?

谢谢

4

2 回答 2

5

从某种意义上说,这不是一个真正的问题,它是确定性的,并且按照它的编程去做。如果您希望它选择两个凌晨 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

于 2012-11-07T23:12:17.657 回答
0

获取正确的时区非常重要。例如,我确信您知道系统当前时间(以毫秒为单位)是从 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”时区,一切都很好。“欧洲/伦敦”时区就是这么棘手。

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

于 2012-11-08T00:42:43.460 回答