以原始时区打印日期

Print date in original 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));

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

sdf.format(cal.getTime());

使用设备时区。

所以我解析 "manually" 日期以提取时区以在日历中设置它。

cal.setTimeZone(TimeZone.getTimeZone("GMT" + date.substring(date.length() - 6, date.length())));

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

我该怎么做?

正如我所见,日期对象没有关于时区的信息。因此,一种但不是最好的方法是从日期字符串解析时区并将其设置为 SimpleDateFormat。类似于:

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

// tz is +02:00
String tz;
if (date.contains("+"))
    tz = date.substring(date.indexOf("+"));
else
    tz = date.substring(date.lastIndexOf("-"));

sdf.setTimeZone(TimeZone.getTimeZone("GMT" + tz));
System.out.println(sdf.format(cal.getTime()));

时区 = 偏移量 + 规则

in the original time zone (GMT+02:00).

不,不正确。文本 GMT+02:00 表示 offset-from-UTC 不是时区 。时区是一个偏移量加上一组处理异常的规则,例如夏令时(DST)。

时区的命名格式为continent/region,如America/MontrealAsia/Kolkata。始终使用 these proper names。从来没有出现在主流媒体上的 3-4 个缩写词;这些既不是标准化的也不是唯一的。

UTC is the new GMT,实际上。

避免使用旧的日期时间 classes

与 Java 最早版本捆绑在一起的旧日期时间 classes 已被证明设计不佳、令人困惑且麻烦。避开他们。这些 classes 被非常成功的 Joda-Time library and its successor the java.time 框架所取代。

java.time framework is built into Java 8 and later. Much of its functionality was back-ported to Java 6 & 7 in the ThreeTen-Backport project, and further adapted to Android in the ThreeTenABP.

Oracle Tutorial 了解更多信息。

OffsetDateTime

输入字符串只有一个相对于 UTC 的偏移量,而不是完整的 time zone. So we use the OffsetDateTime class 来表示这种值。

幸运的是,您输入的字符串是标准 ISO 8601 格式。 java.time classes 在 parsing/generating 日期时间值的文本表示时默认使用 ISO 8601 格式。所以我们可以直接解析而不用定义格式化模式。

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

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

String output = odt.toString(); // ISO 8601 formatted string.

Instant

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

在java.time中,一个Instant represents a moment on the timeline in UTC with a resolution in nanoseconds。我们可以从 OffsetDateTime.

中提取 Instant
Instant instant = odt.toInstant();

ZonedDateTime

由于处理夏令时等异常的规则,使用时区总是比仅仅使用时差更好。

您可以轻松地将 InstantOffsetDateTime 调整到时区,方法是在这些 [=181] 之间的 converting/adjusting 的一般主题上应用 ZoneId to get a ZonedDateTime. For more discussion, see and diagram =]es.

ZoneId zoneId_Montreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId_Montreal );

……或者……

ZonedDateTime zdt_Montreal = odt.atZoneSameInstant( zoneId_Montreal );

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

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

ZoneId zoneId_Melbourne = ZoneId.of( "Australia/Melbourne" );
ZonedDateTime zdt_Melbourne = zdt_Montreal.withZoneSameInstant( zoneId_Melbourne );

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

要生成其他格式的字符串,在 Stack Overflow 中搜索 java.time.format and DateTimeFormatter. The ofLocalized… methods along with a Locale 的主题可以自动本地化格式,包括翻译人类语言 day/month 名称,并遵循元素顺序的文化规范,句号-与逗号等。


关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

  • Java SE 8, Java SE 9,及以后
    • 内置。
    • 标准 Java API 的一部分,带有捆绑实施。
    • Java 9 添加了一些小功能和修复。
  • Java SE 6 and Java SE 7
  • Android
    • Android java.time classes.
    • 捆绑实施的更高版本
    • 对于较早的 Android,ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See

ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval, YearWeek, YearQuarter, and more.