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

Java日历:凌晨1点切换到标准时间(从DST),java,calendar,Java,Calendar,我有一些代码使用Calendar.set()返回给定日期值的小时开始时间。我在2012年11月4日星期日遇到了以下问题(东部时区-EDT至EST转换): 输出: 2012年11月4日星期日美国东部时间01:00:00 美国东部时间2012年11月4日星期日01:00:00 1.6.0_35 也许这不是使用日历的正确方法,但在下一个小时(或前一个小时)运行相同的测试也可以。这是JVM问题吗 谢谢这并不是一个真正的问题,因为它是确定性的,并且做它被编程要做的事情。这是一个问题,如果你希望它选择两个1

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

输出:

2012年11月4日星期日美国东部时间01:00:00

美国东部时间2012年11月4日星期日01:00:00

1.6.0_35

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


谢谢

这并不是一个真正的问题,因为它是确定性的,并且做它被编程要做的事情。这是一个问题,如果你希望它选择两个1上午较早

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


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

获得正确的时区非常重要。例如,我确信您知道系统当前时间(以毫秒为单位)是从1970年1月1日午夜开始测量的。这是有据可查的。日期记录显示:

公开日期(长日期)

分配日期对象并将其初始化以表示指定的日期 自标准基准时间(称为“基准时间”)起的毫秒数 “纪元”,即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”时区,一切都很好。“欧洲/伦敦”时区就是这样微妙的


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

这一点很好。因此,日历似乎不支持消除这两个重叠小时的歧义(除非使用setTime/setTimeInMillis,或者使用@Nambari提到的setTimeZone。日历是使用最近的时间还是在创建日历时应用默认时区(如果程序是在夏时制转换发生后运行的,这将是最好的)……我不知道答案。@jahroy java.util.GregorianCalendar代码中的注释表明,它总是将当天的1:00-1:59解释为标准时间,而不是将夏时制解释为任意约定。“…这里,指定的1:00 am-1:59 am时间可以是标准时间或DST。两者都是有效的表示形式(代表从DST的1:59:59跳到标准时间的1:00:00)。同样,我们假设为标准时间。“感谢您提供的信息,但是这个示例有点混乱,因为日期格式为不同的时区打印'GMT'。如果使用'Z'输出格式,它将显示+0100(这很清楚)。因此,您的示例的问题不在于欧洲/伦敦“棘手”,而在于字符串输出不明确。我的错误是没有意识到set()函数会导致日历从头开始重新计算UTC时间(因此会混淆必须确定使用哪一个“凌晨1点”)。最初的问题仅出现在支持DST偏移的时区。
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