以原始时区打印日期

lde*_*may 1 java android date simpledateformat timestamp-with-timezone

假设我的设备在澳大利亚(GMT + 10:00)。

我有一个从API收到的日期“ 2016-04-22T17:45:00 + 02:00”。

我想在原始时区(GMT + 02:00)很好地显示它。

因此,我将SimpleDateFormat与“ yyyy-MM-dd'T'HH:mm:ssZZZZZ”一起使用。

String date = "2016-04-22T17:45:00+02:00";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZZZZZ");
Calendar cal = Calendar.getInstance();
cal.setTime(sdf.parse(date));
Run Code Online (Sandbox Code Playgroud)

但是不幸的是,我找不到一种显示日期的干净方法:

sdf.format(cal.getTime());
Run Code Online (Sandbox Code Playgroud)

使用设备时区。

因此,我“手动”解析了日期以提取时区以将其设置在日历中。

cal.setTimeZone(TimeZone.getTimeZone("GMT" + date.substring(date.length() - 6, date.length())));
Run Code Online (Sandbox Code Playgroud)

但这似乎并不能解决问题。

我怎样才能做到这一点?

Bas*_*que 5

时区=偏移量+规则

在原始时区(GMT + 02:00)。

不,不正确。文本GMT+02:00表示相对于UTC偏移量而不是时区。时区是一个偏移量,外加一组用于处理异常的规则,例如夏令时(DST)。

时区以continent/region诸如America/Montreal或的格式命名Asia/Kolkata。始终使用这些专有名称。永远不要在主流媒体中看到3-4个缩写;这些既不是标准化的也不是唯一的。

实际上,UTC是新的GMT

避免使用旧的日期时间类

事实证明,与最早的Java版本捆绑在一起的旧的日期时间类设计不佳,令人困惑且麻烦。避免他们。这些类被非常成功的Joda-Time库及其后继的java.time框架所取代

java.time框架内置的Java 8和更高版本。它的许多功能在ThreeTen-Backport项目中都已反向移植到Java 6和7 ,并在ThreeTenABP中进一步适应了Android 。

Oracle教程中了解更多信息。

OffsetDateTime

输入字符串仅具有与UTC的偏移量,而没有整个时区。因此,我们使用OffsetDateTime类来表示这种价值。

幸运的是,您输入的字符串为标准ISO 8601格式。解析/生成日期时间值的文本表示时,java.time类默认使用ISO 8601格式。因此,我们可以直接解析而无需费心定义格式设置模式。

String input = "2016-04-22T17:45:00+02:00";
OffsetDateTime odt = OffsetDateTime.parse( input );
Run Code Online (Sandbox Code Playgroud)

调用OffsetDateTime::toString()以生成相同格式的字符串。

String output = odt.toString(); // ISO 8601 formatted string.
Run Code Online (Sandbox Code Playgroud)

Instant

程序员不应该考虑这种本地日期时间。通常,您应该在UTC中进行业务逻辑,数据库存储和数据交换。

在java.time中,an Instant表示UTC时间轴上的时刻,分辨率为纳秒。我们可以提取Instant从我们的OffsetDateTime

Instant instant = odt.toInstant();
Run Code Online (Sandbox Code Playgroud)

ZonedDateTime

由于使用了诸如夏令时之类的异常处理规则,使用时区总是比仅使用偏移更好。

您可以轻松地调整或者一个InstantOffsetDateTime通过施加到一个时区ZoneId得到ZonedDateTime。有关更多讨论,请参见我的较长答案和关于在这些类之间进行转换/调整的一般主题的图表

ZoneId zoneId_Montreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId_Montreal );
Run Code Online (Sandbox Code Playgroud)

…要么…

ZonedDateTime zdt_Montreal = odt.atZoneSameInstant( zoneId_Montreal );
Run Code Online (Sandbox Code Playgroud)

请注意,在应用时区时,您可能会看到一天中的时间甚至日期发生变化。可能需要根据该区域的规则进行调整,以适应夏令时(DST)或其他问题。

您可以进一步调整到其他时区。你提到澳大利亚。该国家/地区有很多时区,因此您必须选择适合您(或您的用户)的时区,因为它们现在或过去或将来可能有不同的规则。

ZoneId zoneId_Melbourne = ZoneId.of( "Australia/Melbourne" );
ZonedDateTime zdt_Melbourne = zdt_Montreal.withZoneSameInstant( zoneId_Melbourne );
Run Code Online (Sandbox Code Playgroud)

在调用ZonedDateTime::toString该方法时,您会注意到,该方法通过在方括号中附加时区名称来扩展标准ISO 8601格式。

要生成其他格式的字符串,请在Stack Overflow中搜索java.time.format和的主题DateTimeFormatter。这些ofLocalized…方法与一起Locale可以自动本地化格式,包括翻译人类语言的日期/月份名称,并遵循元素顺序,句点对逗号等的文化规范。


关于java.time

java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.DateCalendar,和SimpleDateFormat

现在处于维护模式Joda-Time项目建议迁移到java.time类。

要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310

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

在哪里获取java.time类?

ThreeTen-额外项目与其他类扩展java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。你可能在这里找到一些有用的类,比如IntervalYearWeekYearQuarter,和更多