【问题标题】:yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z' convert to date formatyyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z' 转换为日期格式
【发布时间】:2017-10-27 16:39:07
【问题描述】:

我以这种格式从 API 接收日期:

yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'

并以这种方式转换:

String rawDate = "2017-05-11T15:46:48.2226756Z";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'", Locale.getDefault());
Date date = simpleDateFormat.parse(rawDate);
System.out.println(date); //Thu May 11 16:23:54 PDT 2017

但是日期输出是这样的:

2017 年 5 月 11 日星期四 16:23:54 PDT

输出应该是:

2017 年 5 月 11 日星期四 15:46:48 PDT

如何正确转换原始日期?

【问题讨论】:

  • 您应该使用 DateFormat。您可以在这里找到示例:stackoverflow.com/questions/454315/…
  • 尝试将 rawData 设置为“yyyy-MM-dd'T'HH:mm:ss”。
  • 为什么你有'Z'作为文字?
  • 这里有人误解了一些东西。 Z 表示祖鲁时区,也称为 UTC。所以输出肯定是 not 应该是 Thu May 11 15:46:48 PDT 2017,而是 15:46:48 UTC,等于太平洋夏令时间 8:46:48。错误是写字符串还是解释错误,我不敢说。

标签: java android date datetime datetime-format


【解决方案1】:

问题是“S”代表毫秒。因此,在您的情况下,您告诉它时间是 15 小时 46 分钟 48 秒和 2226756 毫秒。如果你将 2226756 毫秒,即 2226 秒和 756 毫秒加到 15:46:48,你确实得到了 16:23:54。

最简单的解决方案可能是只在字符串中找到句点,然后将字符串截断三个位置,稍后将其转换为:

2017-05-11T15:46:48.222

您可以通过以下行实现此目的:

rawDate = rawDate.substring(0, rawDate.indexOf('.') + 4);

然后用

解析
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault());

请注意,这不会正确舍入微秒。例如,在您的情况下,222.6756ms 应该向上取整到 223ms,而不是向下取整到 222ms。如果这很重要,您可以手动执行此操作,方法是检查第一个丢弃的数字以查看它是否为 5 或更高,然后在 date 中添加毫秒。

更新(回复:Basil Bourque):

如果您想真正尊重时间字符串中的时区标识符(指示 UTC,如下面的 Ole VV 解释),您可以简单地将“UTC”添加到字符串的末尾并用它进行解析旧版本 Java 中的时区,无需使用任何其他库:

rawDate = rawDate.substring(0, rawDate.indexOf('.') + 4) + "UTC";
SimpleDateFormat sDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSz",
                                            Locale.getDefault());
Date date = sDF.parse(rawDate);

【讨论】:

  • 不正确您忽略了时区Z。您的 JVM 当前的默认时区将被隐式应用,从而改变原始数据的含义。请参阅correct Answer by Ole V.V.
  • @BasilBourque 谢谢!今天学到了一些东西!我什至没有想到“Z”是时区指示器。我以某种方式假设它们始终是 3 个字母(PDT、EST、CET、GMT、UTC ......)。特别是因为 OP 明确表示他希望将时区理解为 PDT(请参阅预期输出示例)。
  • 其实那些3-4个字母的缩写是不是的实时时区。它们没有标准化。他们甚至不是独一无二的! CST 是美国/加拿大中国标准时间的中部时间。 IST 是印度标准时间爱尔兰标准时间,依此类推。 避免使用这些代码。以continent/region 的格式使用real time zone names,例如America/MontrealAsia/KolkataPacific/Auckland。这是避免糟糕的遗留日期时间类的众多原因之一。仅使用 java.time 类。
【解决方案2】:

SimpleDateFormat 无法处理除三(毫秒)以外的任何其他小数位数,因此无法让它正确解析您的字符串。此外,较新的 Java 日期和时间类通常对程序员更加友好和方便。它们具有纳秒精度(秒为 9 位小数)。所以我建议你考虑继续使用它们。

如前所述,Z 表示祖鲁时区,也称为 UTC。所以2017-05-11T15:46:48.2226756Z 表示 15:46:48 UTC,等于太平洋夏令时间 8:46:48。您的格式暂时是 ISO 8601 格式,Instant 类将其理解为默认格式,因此解析很容易:

    Instant instant = Instant.parse(rawDate);

结果是

2017-05-11T15:46:48.222675600Z

唯一需要注意的是添加了两个零。 toString 方法以三个一组的形式打印小数,这些组足以呈现完整的精度。所以用 7 位小数打印 9。

获取太平洋时区的日期:

    ZonedDateTime dateTime = instant.atZone(ZoneId.of("America/Los_Angeles"));

结果如我所料:

2017-05-11T08:46:48.222675600-07:00[America/Los_Angeles]

现在假设您从一个误解并真正意味着 2017 年 5 月 11 日星期四 15:46:48 PDT 的人那里获得了原始日期时间字符串(这不是历史上的第一次)。然后你需要把它转换成那个。同样,虽然这对于老式的类来说会很麻烦,但对于新的类来说却很顺利:

    ZonedDateTime dateTime = instant.atOffset(ZoneOffset.UTC)
            .atZoneSimilarLocal(ZoneId.of("America/Los_Angeles"));

结果是你要求的结果(除了我也给你所有的小数):

2017-05-11T15:46:48.222675600-07:00[America/Los_Angeles]

对于 Android,您可以从 ThreeTenABP 库中获得更新的日期和时间类。

链接

【讨论】:

  • 很好的答案。这是针对 Java 8 的,不是吗?
  • 谢谢。它适用于 Java 8 或 9 或更高版本,或者使用我提到的 ThreeTenABP 的 Android Java 7,或者使用 ThreeTen Backport 的 Java 6 和 7。你说的有点对。 :-)
  • @bkm,答案本身使用了在 Java8 及更高版本中添加的类。然而,整个java.time 包是从另一个库Joda-Time 派生的(其作者在JSR-310 专家组,后来成为java.time 包。)。简而言之,您可以在 Java 8 及更高版本中本机使用此答案,或者在 Java 7 或更低版本中使用 Joda-Time 库中的类似类。
【解决方案3】:

你可以使用下面来解析。

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault());

【讨论】:

  • 请注意,这会将时间戳中的毫秒数向下舍入为 0,以防万一……
  • 不正确 (a) 你忽略了时区。 (b) 你忽略了另一个提到的小数秒。 (c) 您将遇到异常,因为此格式化模式无法匹配输入字符串。
  • @BasilBourque 那么如何在没有库和 Java 7 的情况下制作它? @Ole V.V.答案是使用库或在 Java 8 中
  • @bkm 明智的解决方案是将 java.time 的 back-port 添加到您的项目中,即 ThreeTen-Backport 项目,该项目在 ThreeTenABP 项目中进一步适配 Android。是的,请参阅Answer by Ole V.V.。遗留的日期时间类是一团糟,现在完全被 java.time 取代。永远不要再输入“SimpleDateFormat”!
  • 我在写答案之前确实看到了 android 标签。我知道较新的类尚未内置到 Android Java 中。我仍然认为我建议使用 ThreeTenABP 提供一个很好的解决方案。并且面向未来。
猜你喜欢
  • 1970-01-01
  • 2017-06-24
  • 2011-02-09
  • 2011-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 2022-11-17
相关资源
最近更新 更多