SimpleDateFormat.parse()忽略时区?

时间:2017-07-29 13:20:30

标签: android simpledateformat timezone-offset datetime-parsing

我正在尝试使用此代码解析日期字符串和时区:

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

如何正确解析日期与时区(我需要时间和时区)?

1 个答案:

答案 0 :(得分:1)

注意:-06:00offset, not a timezone - 这2个概念是相关的,但它们是不同的东西(下面有更多内容)。

SimpleDateFormatCalendar的问题在于它们使用系统的默认时区,因此即使您使用不同的偏移量解析日期(例如-06:00),生成的Calendar将具有默认时区(您可以通过调用TimeZone.getDefault()来检查区域是什么。)

这只是这个旧API的众多problemsdesign 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));

正如我所说,时区和偏移是不同的事情:

  • 偏移是与UTC的差异:-06:00表示"比UTC晚了6个小时"并且+09:00表示"提前9小时UTC"
  • timezone是一个区域在其历史记录中(以及发生这些变化时)具有,拥有和将要拥有的所有不同偏移的集合。最常见的情况是Daylight Saving Time次移位,当时钟在某个区域中向后或向前变化1小时。关于何时更改的所有这些规则(以及变更之前和之后的偏移量)都由时区概念封装。

因此,如果您正在使用偏移并希望转换为其他代码,则上述代码可以正常工作。但是,如果您想使用时区,则必须将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_PauloEurope/Berlin。 避免使用3个字母的缩写(例如CSTPST),因为它们是ambiguous and not standard

您可以致电ZoneId.getAvailableZoneIds()获取可用时区列表(并选择最适合您系统的时区)。

您也可以将系统的默认时区与ZoneId.systemDefault()一起使用,但即使在运行时也可以在不事先通知的情况下进行更改,因此明确使用特定时区会更好。