在日历中设置分钟因TimeZone而异

时间:2016-06-28 08:33:10

标签: java calendar timezone

我刚刚发现了JAVA Calendar Class的一个奇怪的行为。 当我将分钟设置为0时,结果因使用的TimeZone而异。

有谁知道为什么?

示例代码

import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;


public class CalendarTest {

    public static void main(String[] args) {
        Date date = new Timestamp(1477780200000L);
        System.out.println(date); // 2016-10-30 00:30:00.0

        testMethod(date, Calendar.getInstance(TimeZone.getTimeZone("CET"))); // Sun Oct 30 02:00:00 CET 2016
        testMethod(date, Calendar.getInstance(TimeZone.getTimeZone("GMT+1"))); // Sun Oct 30 02:00:00 CEST 2016
    }

    private static void testMethod(Date date, Calendar c) {
        c.setTime(date);
        c.add(Calendar.HOUR_OF_DAY, 2);

        c.set(Calendar.MINUTE, 0);

        System.out.println(c.getTime());
    }
}




编辑(使我的问题更容易理解):
更改了testMethod
这个改变的方法在BOTH(!)情况下计算 Sun Oct 30 02:30:00 CEST 2016

private static void testMethod(Date date, Calendar c) {
    c.setTime(date);
    c.add(Calendar.HOUR_OF_DAY, 2);

    System.out.println(c.getTime());
}

1 个答案:

答案 0 :(得分:3)

您可以在下面的示例代码中看到,显示的日期与实时(以毫秒为单位)有很大不同。问题是2016年10月30日02:00:00 CET存在于CETCEST时区。切换CEST =>时CET你要回去一小时(产生小时重叠)。对于Calendar对象,这非常棘手,因为您在所选时区中表达时间变化。如果您想在此特定时间内重置分钟,Calendar必须发现您在哪个时区表示更改。

我不是100%Calendar处理此问题的方法,但在第一个示例CET中,您明确建议您使用CET。因此,当发生重叠时Calendar可以选择您的建议(CET)。当您使用GMT表达时,Calendar必须选择其中一个时区CETCEST并选择CEST

public static void main(String[] args) {
    Date date = new Timestamp(1477780200000L);
    System.out.println(date); // 2016-10-30 00:30:00.0
    Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"));
    calendar.setTime(date);
    print(1, calendar); // 1: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
    calendar.add(Calendar.HOUR_OF_DAY, 2);
    print(2, calendar); // 2: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
    calendar.set(Calendar.MINUTE, 0); // minutes expressed in CET
    print(3, calendar); // 3: Sun Oct 30 02:00:00 CET 2016 | 1477789200000

    System.out.println("--");

    calendar = Calendar.getInstance(TimeZone.getTimeZone("CET"));
    calendar.setTime(date);
    print(4, calendar); // 4: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
    calendar.add(Calendar.HOUR_OF_DAY, 2);
    print(5, calendar); // 5: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
    calendar.set(Calendar.MINUTE, 0); // minutes expressed in CET
    print(6, calendar); // 6: Sun Oct 30 02:00:00 CET 2016 | 1477789200000

    System.out.println("--");

    calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT+1"));
    calendar.setTime(date);
    print(4, calendar); // 4: Sun Oct 30 00:30:00 CEST 2016 | 1477780200000
    calendar.add(Calendar.HOUR_OF_DAY, 2);
    print(5, calendar); // 5: Sun Oct 30 02:30:00 CEST 2016 | 1477787400000
    calendar.set(Calendar.MINUTE, 0); // minutes expressed in GMT
    print(6, calendar); // 6: Sun Oct 30 02:00:00 CEST 2016 | 1477785600000
}

private static void print(int prefix, Calendar calendar) {
    System.out.println(prefix + ": " + calendar.getTime() + " | " + calendar.getTimeInMillis());
}

更新

更有趣的是Calendar方法getTimeInMillis

public long getTimeInMillis() {
    if (!isTimeSet) {
        updateTime();
    }
    return time;
}

正如您所知,时间会在您获得时更新!每次使用isTimeSet方法false时,都会设置一个set标记calendar.set(Calendar.MINUTE, 0)。这意味着您的时间在正确的时区/纪元等方面变为无效。此方法只是将给定的日历字段设置为给定的值,就是这样。此外,如果您的设置有效,此方法不会进行任何其他检查。另一方面,add方法尊重日历规则并优雅地移动日期。

总结一下。您将分钟设置为0会强制日历重新计算日期。你是对的,set是有问题的。