4

I have been googling around this issue, but could not find clear and definitive documentation.

Assuming I have a UTC time and a TimeZone ID, how do I calculate wall time (= UTC time + Timezone offset + daylight savings) in Java knowing that daylight savings change during the year?

I am looking for a tested code example. Thanks.

4

2 回答 2

3

当您在 java 中使用 Date 时,它​​始终在内部使用 UTC。时区包括 DST 设置,因此实际上非常简单。

public static void main(String[] args) throws ParseException {
    String stringAugust = "2011-08-01 12:00:00";
    String stringNovember = "2011-11-01 12:00:00";

    // Outputting the time in Stockholm and Santiago
    // Stockholm has DST in August and not in November
    // Santiago has DST in November and not in August

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // Parsing the Strings
    sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
    Date dateAugust = sdf.parse(stringAugust);
    Date dateNovember = sdf.parse(stringNovember);

    // outputting the dates for Stockholm
    sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z");
    sdf.setTimeZone(TimeZone.getTimeZone("Europe/Stockholm"));
    System.out.println(sdf.format(dateAugust));
    System.out.println(sdf.format(dateNovember));

    // outputting the dates for Santiago
    sdf.setTimeZone(TimeZone.getTimeZone("America/Santiago"));
    System.out.println(sdf.format(dateAugust));
    System.out.println(sdf.format(dateNovember));

}

输出

2011-08-01 14:00:00 +0200
2011-11-01 13:00:00 +0100
2011-08-01 08:00:00 -0400
2011-11-01 09:00:00 -0300
于 2011-11-27T14:47:06.723 回答
3

当你说你有时间在 UTC 时,我假设你持有它CalendarDate没有时区的概念,尽管有误导性toString())。如果您有时间,例如,String您可以轻松地解析它或日历实例,如下所示:

Calendar summer = new GregorianCalendar(DateUtils.UTC_TIME_ZONE);
summer.set(2011, Calendar.JUNE, 27, 9, 0, 0);

summer表示 2011 年 6 月 27 日上午 9:00 UTC。现在您需要做的就是将时区从 UTC 更改为澳大利亚墨尔本:

summer.setTimeZone(TimeZone.getTimeZone("Australia/Melbourne"));

我将使用FastDateFormat正确打印日期:

final FastDateFormat formatter = FastDateFormat.getDateTimeInstance(FastDateFormat.SHORT, FastDateFormat.SHORT);

System.out.println(formatter.format(summer));

墨尔本的时间是19:00(+10 小时)。但是将日期更改为冬季:

Calendar winter = new GregorianCalendar(DateUtils.UTC_TIME_ZONE);
winter.set(2011, Calendar.DECEMBER, 27, 9, 0, 0);
System.out.println(formatter.format(winter));

突然间,墨尔本的时间是20:00(+11 小时)。

差异证明更改时区时Calendar考虑了 DST。在UTC时区的六月期间,澳大利亚有冬天,因此他们不遵守夏令时。

但是在 UTC 的冬天,澳大利亚有夏天 - 他们通过将时钟移动一小时来切换到 DST。这就是为什么冬季时差为 +11 小时,而夏季 UTC 时差为 +10 小时的原因。


可是等等!当考虑到观察 DST 的多个时区时,它变得更加有趣。首先,我在欧洲/奥斯陆时区创建相同的日期:

Calendar winter = new GregorianCalendar(TimeZone.getTimeZone("Europe/Oslo"));
winter.set(2011, Calendar.DECEMBER, 27, 9, 0, 0);

冬季奥斯陆的 9:00 是 8:00 UTC,但墨尔本是 19:00(+10 小时)。

但在夏天的同一时间:

Calendar summer = new GregorianCalendar(TimeZone.getTimeZone("Europe/Oslo"));
summer.set(2011, Calendar.JUNE, 27, 9, 0, 0);

实际上是世界标准时间7:00和墨尔本的17:00!+8 小时

不知何故,人们假设两个时区之间的差异总是恒定的(“奥斯陆和墨尔本之间的差异总是10 小时)——这是正确的,尤其是考虑到不同的半球时。

实际上,在奥斯陆的冬季(无 DST,UTC+1)墨尔本(UTC+11)观察到 DST。另一方面,虽然奥斯陆有夏季并且观察到 DST (UTC+2),但墨尔本没有观察到 (UTC+10)。现在很明显为什么差异会根据一年中的一天在 8 到 10 小时之间变化。

还要记住,夏令时的第一天和最后一天不是全球性的,而是针对每个时区任意选择的。这意味着 9 小时的差异也是可能的(!)例如检查今年 4 月 1 日。

于 2011-11-27T14:48:50.433 回答