Java日历:为什么UTC偏移被反转?

时间:2012-04-18 14:41:28

标签: java datetime timezone

我正在努力解决时间问题,而且我偶然发现Java中的某些东西让我感到有些困惑。拿这个示例代码:

public static void main(String[] args)
{
    //Calendar set to 12:00 AM of the current day (Eastern Daylight Time)
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT-4"));
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);
    /////

    //Calendar set to 12:00 AM of the current day (UTC time)
    Calendar utcCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    utcCal.set(Calendar.HOUR_OF_DAY, 0);
    utcCal.set(Calendar.MINUTE, 0);
    utcCal.set(Calendar.SECOND, 0);
    utcCal.set(Calendar.MILLISECOND, 0);
    /////

    long oneHourMilliseconds = 3600000;
    System.out.println((cal.getTimeInMillis() - utcCal.getTimeInMillis()) / oneHourMilliseconds);
}

我可视化算法,用于计算由cal表示的时间,采用以下两种形式之一:

  1. 计算Epoch的毫秒数,添加偏移量(添加-4)
  2. 从(Epoch + offset)计算毫秒数。所以来自(Epoch - 4 * oneHourMilliseconds)的毫秒数。
  3. 这两种算法都应该产生比utcCal落后4小时的结果,但运行代码会返回4

    有人可以向我解释为什么cal,尽管被设置为utcCal 后4小时的时区,最终在4小时后达到毫秒值 utcCal ?代码不应该返回-4吗?

3 个答案:

答案 0 :(得分:10)

这是一段不幸的历史。 ID为“GMT-4”的时区是您期望为“UTC + 4”的时区,即它是UTC前4小时

来自tzdb:

的etcetera文件
# We use POSIX-style signs in the Zone names and the output abbreviations,
# even though this is the opposite of what many people expect.
# POSIX has positive signs west of Greenwich, but many people expect
# positive signs east of Greenwich.  For example, TZ='Etc/GMT+4' uses
# the abbreviation "GMT+4" and corresponds to 4 hours behind UTC
# (i.e. west of Greenwich) even though many people would expect it to
# mean 4 hours ahead of UTC (i.e. east of Greenwich).

来自this similar explanation

  

如果你可以管理它,避免定义和使用GMT时区......

答案 1 :(得分:2)

日历cal设置为时区2012-04-18 00:00:00的{​​{1}}。

该时刻对应于GMT-4中的2012-04-18 04:00:00(换句话说,当时区早上12点UTC时,GMT-4上午4点)。

日历UTC设置为时区utcCal的{​​{1}}。

2012-04-18 00:00:00UTC之间的差异为4小时,因此您会看到4张正在打印。

答案 2 :(得分:0)

其他答案是正确的。在各种上下文中,与UTC值偏移的+ / -具有相反的含义。

java.time

在Java 8及更高版本中内置的现代 java.time 类的上下文中:

  • UTC之前,+
  • 在UTC后面,-

因此,目前在印度(Asia/Kolkata)使用的偏移量为+05:30,比UTC早5个半小时。在加拿大的America/Regina区域,当前使用的偏移量为-06:00,比UTC晚6个小时。

使用区域,而不是偏移

这里的一个重要提示是始终使用proper time zone names ,而不仅仅是已知的偏移。

offset-from-UTC只是一小时,分钟和秒;仅此而已。 time zone是某个地区人民使用的偏移的过去,现在和将来变化的历史记录。因此区域总是优于偏移。

continent/region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用诸如ESTIST之类的3-4字母缩写,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。

ZoneId z = ZoneId.of( "Africa/Tunis" ) ; 

时区对于确定日期至关重要。对于任何给定的时刻,日期在全球范围内因地区而异。例如,在Paris France午夜后的几分钟是新的一天,而Montréal Québec中仍然是“昨天”。

LocalDate ld = LocalDate.now( z ) ;

比UTC早4个小时,请使用Asia/Dubai等时区。

ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( `Asia/Dubai` ) ) ;
  

zdt.toString():2018-03-01T02:39:18.801642 + 04:00 [亚洲/迪拜]

在UTC后面四个小时,请使用America/Blanc-Sablon等时区。

ZonedDateTime zdt = ZonedDateTime.now( ZoneId.of( `America/Blanc-Sablon` ) ) ;
  

zdt.toString():2018-02-28T18:39:18.801642-04:00 [America / Blanc-Sablon]

ZoneOffset

如果您不确定预期的时区,并且只有UTC的偏移量,请使用ZoneOffset类。

ZoneOffset offset = ZoneOffset.ofHoursMinutes( 4 , 30 ) ; // Positive hours is ahead of UTC (east), while negative hours is behind UTC (west). 

对于UTC本身(偏移量为零),请使用常量ZoneOffset.UTC

关于 java.time

java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.DateCalendar和& SimpleDateFormat

现在位于Joda-Timemaintenance mode项目建议迁移到java.time类。

要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310

您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*类。

从哪里获取java.time类?

ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如IntervalYearWeekYearQuartermore