我正在尝试使用此代码解析日期字符串和时区:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZZZZZ", Locale.US);
Calendar calendar = Calendar.getInstance();
calendar.setTime(sdf.parse("2017-07-26T06:00-06:00"));
int offset = calendar.getTimeZone().getRawOffset();
我正在尝试将时区从-06
更改为+09
,但offset
始终包含10800000
。
如何正确解析日期与时区(我需要时间和时区)?
答案 0 :(得分:1)
注意:-06:00
是offset, not a timezone - 这2个概念是相关的,但它们是不同的东西(下面有更多内容)。
SimpleDateFormat
和Calendar
的问题在于它们使用系统的默认时区,因此即使您使用不同的偏移量解析日期(例如-06:00
),生成的Calendar
将具有默认时区(您可以通过调用TimeZone.getDefault()
来检查区域是什么。)
这只是这个旧API的众多problems和design issues中的一个。
幸运的是,如果您不介意为项目添加依赖项,那么这是一个更好的选择(在这种情况下,我认为它非常值得)。在Android中,您可以使用ThreeTen Backport,这是Java 8新日期/时间类的绝佳后端。对于 Android ,您还需要ThreeTenABP才能使其正常运行(更多关于如何使用它here)。
要使用偏移量,您可以使用org.threeten.bp.OffsetDateTime
类:
// parse the String
OffsetDateTime odt = OffsetDateTime.parse("2017-07-26T06:00-06:00");
这将正确解析所有字段(日期/时间和偏移量)。要获得偏移值,类似于calendar.getTimeZone().getRawOffset()
,您可以执行以下操作:
// get offset in milliseconds
int totalSeconds = odt.getOffset().getTotalSeconds() * 1000;
我必须乘以1000,因为calendar
以毫秒为单位返回值,但ZoneOffset
以秒为单位返回。
要将此转换为另一个偏移量(+09:00
),它很简单:
// convert to +09:00 offset
OffsetDateTime other = odt.withOffsetSameInstant(ZoneOffset.ofHours(9));
正如我所说,时区和偏移是不同的事情:
-06:00
表示"比UTC晚了6个小时"并且+09:00
表示"提前9小时UTC" 因此,如果您正在使用偏移并希望转换为其他代码,则上述代码可以正常工作。但是,如果您想使用时区,则必须将OffsetDateTime
转换为ZonedDateTime
:
// convert to a timezone
ZonedDateTime zdt = odt.atZoneSameInstant(ZoneId.of("Asia/Tokyo"));
// get the offset
totalSeconds = zdt.getOffset().getTotalSeconds() * 1000;
上面的getOffset()
方法将检查指定时区的历史记录并获取在相应时刻有效的偏移量(因此,如果您在DST期间采用日期,例如,偏移量(以及日期)和时间)将相应调整。)
API使用IANA timezones names(始终采用Region/City
格式,如America/Sao_Paulo
或Europe/Berlin
。
避免使用3个字母的缩写(例如CST
或PST
),因为它们是ambiguous and not standard。
您可以致电ZoneId.getAvailableZoneIds()
获取可用时区列表(并选择最适合您系统的时区)。
您也可以将系统的默认时区与ZoneId.systemDefault()
一起使用,但即使在运行时也可以在不事先通知的情况下进行更改,因此明确使用特定时区会更好。