【问题标题】:Converting a time String to ISO 8601 format将时间字符串转换为 ISO 8601 格式
【发布时间】:2015-11-16 18:39:26
【问题描述】:

我正在尝试以 2015-08-20T08:26:21.000Z
之类的格式创建字符串 到 2015-08-20T08:26:21Z

我知道它可以通过一些字符串拆分技术来完成,但我想知道是否有一个优雅的解决方案(只需最少的代码更改)。

以上两个都是时间字符串,我需要的最后一个是 ISO 8601 中的 Date 。 https://www.rfc-editor.org/rfc/rfc3339#section-5.6

我尝试了一些类似的问题,例如converting a date string into milliseconds in java,但它们实际上并没有解决目的。

也尝试过使用:

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mmZ");
String nowAsString = df.format(new Date());

但它仍然没有进行任何字符串到字符串的转换。收到以下错误:

23:04:13,829 WARN [RuntimeExceptionMapper] 捕获 RuntimeException:{}:java.lang.IllegalArgumentException:无法将给定对象格式化为日期

有人可以推荐一些图书馆吗?

谢谢。

【问题讨论】:

  • 使用Joda-Time。见:jodatime
  • 输入和输出都是字符串。但这些字符串实际上代表不同的时区。
  • A DateFormat 执行字符串到日期和日期到字符串的转换。您想要字符串到字符串的转换。因此,您可以使用 DateFormat 进行 String 到 Date 的转换,并使用另一个 DateFormat 进行 Date 到 String 的转换。不,它们不代表不同的时区。一个有毫秒部分,另一个没有。这是唯一的区别。最后的 Z 表示祖鲁时间,即 UTC 时区。
  • 那么 org.joda.time.format.DateTimeFormatter ?
  • @CodeMonkey 所以你的目标是消除.000?您的标题令人困惑,因为您第一句中的两个日期时间字符串都是有效的ISO 8601 字符串。

标签: java string time


【解决方案1】:

tl;博士

Instant.parse( "2015-08-20T08:26:21.000Z" )
       .toString()

2015-08-20T08:26:21Z

日期时间格式化程序

如果您只想消除.000,则使用日期时间对象解析您的输入字符串值,然后以不同格式生成该日期时间值的新字符串表示形式。

ISO 8601

顺便说一句,如果这是您的目标,那么问题的标题毫无意义,因为第一句中提到的两个字符串都是有效的ISO 8601 格式化字符串。

  • 2015-08-20T08:26:21.000Z
  • 2015-08-20T08:26:21Z

java.time

Java 8 及更高版本具有新的java.time package。这些新类取代了旧的 java.util.Date/.Calendar 和 java.text.SimpleDateFormat 类。那些旧课程令人困惑、麻烦且有缺陷。

即时

如果您只需要 UTC 时区,那么您可以使用 Instant 类。此类表示时间轴上的一个点,不考虑任何特定时区(基本上是 UTC)。

DateTimeFormatter.ISO_INSTANT

调用 Instant 的 toString 使用 DateTimeFormatter.ISO_INSTANT 格式化程序实例生成日期时间值的字符串表示形式。此格式化程序自动灵活处理小数秒。如果该值有一整秒,则不会生成小数位(显然是问题想要的)。对于小数秒,数字出现在 3、6 或 9 组中,根据需要以表示高达纳秒分辨率的值。注意:此格式可能超过 ISO 8601 毫秒限制(小数点后 3 位)。

示例代码

这是 Java 8 Update 51 中的一些示例代码。

String output = Instant.parse( "2015-08-20T08:26:21.000Z" ).toString( );
System.out.println("output: " + output );

输出:2015-08-20T08:26:21Z

更改为小数秒,.08

String output = Instant.parse( "2015-08-20T08:26:21.08Z" ).toString( );

输出:2015-08-20T08:26:21.080Z

如果对 UTC 以外的任何时区感兴趣,则从该 Instant 创建一个 ZonedDateTime 对象。

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

【讨论】:

    【解决方案2】:

    将未知格式的日期 String 转换为使用已知格式的日期 String 可以使用两个 DateFormat 对象完成 - 一个动态配置为解析输入 String 的格式,另一个配置为生成格式化输出String。对于您的情况,输入String 格式未指定,必须由调用者提供,但是,可以将输出String 格式配置为使用ISO 8601 格式而无需额外输入。本质上,生成 ISO 8601 格式化日期 String 输出需要调用者提供两个输入 - 一个包含格式化日期的 String 和另一个包含 SimpleDateFormat 格式的 String

    这是描述为 Java 代码的转换(我故意省略了 null 检查和验证,请根据您的代码添加这些):

    private String formatDateAsIso8601(final String inputDateAsString, final String inputStringFormat) throws ParseException {
    
         final DateFormat iso8601DateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.ENGLISH);
         iso8601DateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
    
         final DateFormat inputDateFormatter = new SimpleDateFormat(inputStringFormat, Locale.ENGLISH);
         final Date inputDate = inputDateFormatter.parse(inputDateAsString);
    
         return iso8601DateFormatter.format(inputDate);
    }
    

    如果您想修改该方法,请注意 SimpleDateFormat 不是线程安全的,并且您不应该在没有多线程代码解决方法的情况下从静态上下文中使用它(ThreadLocal 通常用于执行SimpleDateFormat 就是这样的解决方法)。

    另一个“陷阱”是在构造SimpleDateFormat 对象期间使用Locale - 不要删除Locale 配置。允许系统选择使用默认的Locale 是不安全的,因为这是特定于用户/机器的。如果您确实允许它使用默认的Locale,那么您将面临暂时性错误的风险,因为您的开发机器使用的Locale 与最终用户的Locale 不同。您不必使用我选择的 ENGLISH Locale,使用不同的语言环境完全可以(您应该了解该 Locale 的规则并根据需要修改代码)。然而,没有 Locale 的规范和系统默认值的使用是不正确的,并且可能会导致尝试诊断难以捉摸的错误的许多令人沮丧的时间。

    请理解此解决方案在 Java 8 中并不理想,并且包含基于 JodaTime 的类,例如 Instant。我选择使用过时的 API 来回答,因为这些是您在问题中似乎关心的问题。如果您使用的是 Java 8,我强烈建议您学习和使用这些新类,因为它们几乎在所有可以想象的方式上都有改进。

    【讨论】:

      【解决方案3】:

      你的格式不对,试试这个:-

      try {
              String s = "2015-08-20T08:26:21.000Z";
               SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
          Date d = df.parse(s);
              SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
              System.out.println(sdf.format(d));
          } catch (ParseException e) {
              e.printStackTrace();
          }
      

      【讨论】:

      • 不要使用“Z”。使用 X。最后的 Z 表示这是一个时间,是 UTC 时区。所以结果完全不同。顺便说一句,由于您的默认时区中存在 DST,而 UTC 中不存在 DST,因此某些字符串不代表本地时区中的任何现有日期,而是代表 UTC。
      • @JBNizet 很有用,谢谢我会编辑代码
      猜你喜欢
      • 2021-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-27
      • 2017-07-25
      • 1970-01-01
      相关资源
      最近更新 更多