KitKat 中的时间格式不起作用

Time format in KitKat not working

我有一个从我的服务器转换时间的函数,它在 android api 24 上工作正常,但现在我正在测试 KitKat (Api 19) 和时间转换不能正常工作。两个设备接收相同的数据,但转换不一样。两台设备都设置了 "Automatic date and time",并且两台设备的时间相同。

从服务器收到字符串:

(Api 19) -> CurrentTimeStamp:2018-02-23T15:50:15.9643834Z
(Api 24) -> CurrentTimeStamp:2018-02-23T15:50:15.9373834Z

转化

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
try {
    Date dateServer = dateFormat.parse(currentTimeStamp);
    String currentDate = dateServer.toString();
} catch (ParseException e) {
    e.printStackTrace();
}

这会打印:

(Api 19) -> DateServer:Fri Feb 23 12:26:28 CST 2018
(Api 24) -> DateServer:Fri Feb 23 09:50:15 CST 2018 <- this time is correct

如有任何帮助或想法,我们将不胜感激

S 格式字符指定毫秒而不是真正的秒数。您的时间戳具有 100 纳秒的分辨率。

然后是实现上的差异:

一些修复选项:

  • Java 8 java.time DateTimeFormatter supports nanoseconds but needs API 26 on Android. For older Androids there's ThreeTenABP.

  • 在尝试使用 SimpleDateFormat.

  • 进行解析之前,您可以自己将时间戳字符串截断为毫秒分辨率小数秒

SimpleDateFormat 的精度为毫秒,因此它不能处理超过 3 位的小数秒。检查这个答案:String-Date conversion with nanoseconds

此外,最后的 "Z" 表示日期是 UTC,因此如果您只是将其视为文字(引号内),则此信息(它是 UTC)将被忽略(的当然,您可以通过在格式化程序中设置时区来解决此问题,但正确的做法是使用 "X" 模式来解析它。

无论如何,要解析超过 3 个小数位,您可以使用 ThreeTen Backport。只需按照此答案中的链接进行安装:How to add Duration to LocalDateTime for API level lower than 26

或者,如果您的 API 等级已经达到 java.time 类,那就更好了。无论如何,java.time 和 ThreeTen Backport 的代码是相同的:

Instant instant = Instant.parse("2018-02-23T15:50:15.9643834Z");

如果您还需要使用java.util.Date,转换起来很容易:

// java.time
Date date = Date.from(instant);

// ThreeTenBackport
Date date = DateTimeUtils.toDate(instant);

请记住,由于 API 的限制,在转换为 Date 时,仅保留前 3 个小数位,其他的将丢失。

java.time

    String currentTimestamp = "2018-02-23T15:50:15.9643834Z";
    String currentDate = Instant.parse(currentTimestamp)
            .atZone(ZoneId.systemDefault())
            .toString();
    System.out.println(currentDate);

在我的电脑上打印

2018-02-23T16:50:15.964383400+01:00[Europe/Berlin]

如果您希望输出格式与从 Date.toString() 获得的格式相同:

    DateTimeFormatter dateFormatter
            = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ENGLISH);
    String currentDate = Instant.parse(currentTimestamp)
            .atZone(ZoneId.systemDefault())
            .format(dateFormatter);

这给出:

Fri Feb 23 16:50:15 CET 2018

其他两个答案都已经给出 java.time 作为解决您问题的第一种方法。我只是想详细说明一下原因和方式。

  • DateSimpleDateFormat 早就过时了。因为意识到它们的设计不佳,所以在 2014 年 java.time 中用 Java 8 替代了它们。这个现代的 API 更好用。
  • SimpleDateFormat 尤其是出了名的麻烦。
  • old-fashioned 类(java.sql.Timestamp 除外)仅支持毫秒精度,因此他们无法解析或仅保留时间戳的完整精度秒上有 7 位小数。
  • 您的字符串采用 ISO 8601 格式。现代 类 将这种格式作为默认格式进行解析和打印,也就是说,没有任何明确的格式化程序。重要的是,他们在正确解析秒数 0 到 9 位小数方面没有任何问题。

你的代码出了什么问题

API19(与文档一致!)将小数点后的数字解析为9643834 milliseoncds,即2小时40分43秒834毫秒。从您的问题和 中获悉,这种不适当的行为已在以后的 Android 版本中得到修复,这让我感到非常惊讶。在我的 Java 9 中它仍然是规则。

问题:我可以在 Android 上使用 java.time 吗?

是的,java.time 在 Android 设备上运行良好。它只需要至少 Java 6.

  • 在 Java 8 和更高版本以及新的 Android 设备上(据我所知,来自 API 级别 26)新的 API 出现 built-in.
  • 在 Java 6 和 7 中获取 ThreeTen Backport,新 类 的 backport(ThreeTen 用于 JSR 310,其中现代 API 首次被描述)。
  • 在(较旧的)Android 上,使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP。确保从包 org.threeten.bp 和子包中导入日期和时间 类。

链接