我正在努力解决时间问题,而且我偶然发现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
表示的时间,采用以下两种形式之一:
Epoch - 4 * oneHourMilliseconds
)的毫秒数。这两种算法都应该产生比utcCal
落后4小时的结果,但运行代码会返回4
。
有人可以向我解释为什么cal
,尽管被设置为utcCal
后4小时的时区,最终在4小时后达到毫秒值 utcCal ?代码不应该返回-4
吗?
答案 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).
如果你可以管理它,避免定义和使用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:00
和UTC
之间的差异为4小时,因此您会看到4张正在打印。
答案 2 :(得分:0)
其他答案是正确的。在各种上下文中,与UTC值偏移的+
/ -
具有相反的含义。
在Java 8及更高版本中内置的现代 java.time 类的上下文中:
+
-
因此,目前在印度(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/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用诸如EST
或IST
之类的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 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance 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的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。