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

问题描述 投票:5回答:3

我正在努力解决时间问题,我偶然发现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)的毫秒数。

这两种算法都应该产生比utcCal落后4小时的结果,但运行代码会返回4

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

java datetime timezone
3个回答
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时区......


2
投票

日历cal设置为时区2012-04-18 00:00:00GMT-4

那个时刻对应于2012-04-18 04:00:00UTC(换句话说,当它在时区GMT-4上午12点时,它是在UTC上午4点)。

日历utcCal设置为时区2012-04-18 00:00:00UTC

2012-04-18 04:00:002012-04-18 00:00:00之间的差异是4小时,所以你看到4被打印。


0
投票

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

java.time

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

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

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

使用区域,而不是偏移

这里的一个重要提示是,每当知道时,总是使用proper time zone names而不是仅仅偏移。

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

proper time zone name的格式指定continent/region,例如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


About java.time

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

现在在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

© www.soinside.com 2019 - 2024. All rights reserved.