【问题标题】:What is this date format? 2011-08-12T20:17:46.384Z这是什么日期格式? 2011/8/12 20:17:46
【发布时间】:2012-01-14 08:39:12
【问题描述】:

我有以下日期:2011-08-12T20:17:46.384Z。这是什么格式?我正在尝试通过DateFormat.getDateInstance().parse(dateStr) 使用 Java 1.4 解析它,我得到了

java.text.ParseException:无法解析的日期:“2011-08-12T20:17:46.384Z”

我想我应该使用SimpleDateFormat 进行解析,但我必须先知道格式字符串。到目前为止,我所拥有的只是yyyy-MM-dd,因为我不知道T 在这个字符串中的含义——与时区有关吗?此日期字符串来自Files CMIS download history media type 上显示的lcmis:downloadedOn 标签。

【问题讨论】:

  • 我是ISO 8601
  • @TomaszNurkiewicz,不是。 ISO8601最后没有Z。
  • ISO8601 确实允许 Z 结尾。请参阅上面的链接,查找 UTC。
  • @t1gor 末尾的ZZulu 的缩写,意思是UTC。这种格式肯定标准日期时间文本格式ISO 8601 集合的一部分。顺便说一句,这些标准格式在 java.time 类中默认使用。
  • 仅供参考,麻烦的旧日期时间类,如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 现在是 legacy,被 Java 8 和 Java 中内置的 java.time 类所取代9. 见Tutorial by Oracle

标签: java date time format simpledateformat


【解决方案1】:

java.time

您不需要DateTimeFormatter 来解析给定的日期时间字符串。

Java SE 8 日期时间 API(java.time API 或现代日期时间 API)基于 ISO 8601 并且不需要明确使用 DateTimeFormatter 对象,只要日期时间字符串符合ISO 8601 标准。

字符串中的Z 是零时区偏移的timezone designator。它代表 Zulu 并指定 Etc/UTC 时区(时区偏移量为 +00:00 小时)。

根据 ISO-8601 标准,字符串中的 T 就是 Date-Time separator

演示:

import java.time.Instant;
import java.time.OffsetDateTime;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        String strDateTime = "2011-08-12T20:17:46.384Z";

        Instant instant = Instant.parse(strDateTime);
        OffsetDateTime odt = OffsetDateTime.parse(strDateTime);
        ZonedDateTime zdt = ZonedDateTime.parse(strDateTime);
        
        System.out.println(instant);
        System.out.println(odt);
        System.out.println(zdt);
    }
}

输出:

2011-08-12T20:17:46.384Z
2011-08-12T20:17:46.384Z
2011-08-12T20:17:46.384Z

Trail: Date Time了解更多关于java.timemodern Date-Time API*的信息。

旧的日期时间 API

旧版日期时间 API(java.util 日期时间 API 及其格式化 API,SimpleDateFormat)已过时且容易出错。建议完全停止使用,改用modern Date-Time API*

为了完整起见,我编写了一个解决方案来使用旧版 API 解析此日期时间字符串。

不要在带有日期时间解析/格式化 API 的模式中使用 'Z'

如上所述,Z(不带引号)是零时区偏移的时区指示符,而 'Z' 只是一个字符文字,它没有任何意义。使用格式y-M-d'T'H:m:s.SSSXXX。查看documentation 了解有关这些符号的更多信息。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class Main {
    public static void main(String[] args) throws ParseException {
        String strDateTime = "2011-08-12T20:17:46.384Z";

        SimpleDateFormat sdf = new SimpleDateFormat("y-M-d'T'H:m:s.SSSXXX", Locale.ENGLISH);
        Date date = sdf.parse(strDateTime);
        // ...
    }
}

注意java.util.Date 对象不是像modern Date-Time types 那样的真实日期时间对象;相反,它表示自称为“纪元”的标准基准时间以来的毫秒数,即January 1, 1970, 00:00:00 GMT(或 UTC)。由于它不包含任何格式和时区信息,因此它应用格式 EEE MMM dd HH:mm:ss z yyyy 和 JVM 的时区来返回从该毫秒值派生的 Date#toString 值。如果您需要以不同的格式和时区打印日期时间,您将需要使用具有所需格式和适用时区的SimpleDateFormat,例如

sdf.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
String formatted = sdf.format(date);
System.out.println(formatted); // 2011-8-12T20:17:46.384Z

Joda 日期时间 API

下面引用的是Home Page of Joda-Time的通知:

请注意,从 Java SE 8 开始,用户被要求迁移到 java.time (JSR-310) - JDK 的核心部分,它取代了这个项目。

再次,为了完整起见,我编写了一个解决方案来使用 Joda Date-Time API 解析这个 Date-Time 字符串。

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class Main {
    public static void main(String[] args) {
        String dateTimeStr = "2011-08-12T20:17:46.384Z";
        DateTimeFormatter dtf = DateTimeFormat.forPattern("y-M-d'T'H:m:s.SSSZ").withOffsetParsed();
        DateTime dateTime = dtf.parseDateTime(dateTimeStr);
        System.out.println(dateTime);
    }
}

输出:

2011-08-12T20:17:46.384Z

* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport,它将大部分 java.time 功能向后移植到 Java 6 和 7 . 如果您正在为一个 Android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaringHow to use ThreeTenABP in Android Project

【讨论】:

    【解决方案2】:

    在 JavaScript 中

    let isoDateTimeString = new Date().toISOString();
    

    说明

    像“YYYY-MM-DDThh:mm:ss.SSSZ”这样的日期/时间格式是ISO 8601日期/时间格式。

    【讨论】:

      【解决方案3】:

      tl;博士

      您的输入字符串使用标准ISO 8601 格式。

      Instant.parse ( "2011-08-12T20:17:46.384Z" ) 
      

      ISO 8601

      此格式由明智的实用标准ISO 8601 定义。

      T 将日期部分与时间部分分开。末尾的 Z 表示 UTC(即与 UTC 的偏移量为零时分秒)。 Zpronounced “Zulu”

      java.time

      与最早的 Java 版本捆绑在一起的旧日期时间类已被证明设计不佳、令人困惑和麻烦。避开他们。

      改为使用 Java 8 及更高版本中内置的 java.time 框架。 java.time 类取代了旧的日期时间类和非常成功的 Joda-Time 库。

      java.time 类在解析/生成日期时间值的文本表示时默认使用ISO 8601

      Instant 类代表UTC 中时间轴上的一个时刻,分辨率为nanoseconds。该类可以直接解析您的输入字符串,而无需定义格式模式。

      Instant instant = Instant.parse ( "2011-08-12T20:17:46.384Z" ) ;
      


      关于java.time

      java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

      要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

      Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

      您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。 Hibernate 5 & JPA 2.2 支持 java.time

      从哪里获取 java.time 类?

      【讨论】:

      • @star “祖鲁”来自军事和航空传统,其中 25 个字母 A-Z(没有“J”),每个字母都有一个可发音的名称,代表 their version of time zones。 “祖鲁”区域与 UTC 的时差为零。见thisthis
      • LocalDateTime.parse("2021-11-22T09:00:00.000Z") 在解析时抛出异常。任何建议,如何使用java.time 格式化相同的输入以格式化为“EEEE,MMMM dd”?谢谢。
      • @Faisal 您尝试将时刻(带有时间和偏移的日期)解析为LocalDateTime 是不合逻辑的。 LocalDateTime 不是时刻,也不是时间线上的特定点,因为它没有与 UTC 或时区偏移的概念。您的输入应解析为Instant,如下所示:Instant.parse("2021-11-22T09:00:00.000Z")
      • 有道理。可以将实例解析为预期格式吗?
      • @Faisal 应用您希望通过其感知日期的时区 (ZoneId)。这会给你一个ZonedDateTime。从中提取LocalDate。使用DateTimeFormatter 以您想要的任何格式生成文本,或自动本地化。所有这些都已经在 Stack Overflow 上讨论过很多次了。
      【解决方案4】:

      如果你们正在寻找适用于 Android 的解决方案,可以使用以下代码从时间戳字符串中获取纪元秒数。

      public static long timestampToEpochSeconds(String srcTimestamp) {
          long epoch = 0;
      
          try {
              if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                  Instant instant = Instant.parse(srcTimestamp);
                  epoch = instant.getEpochSecond();
              } else {
                  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSS'Z'", Locale.getDefault());
                  sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
                  Date date = sdf.parse(srcTimestamp);
                  if (date != null) {
                      epoch = date.getTime() / 1000;
                  }
              }
          } catch (Exception e) {
              e.printStackTrace();
          }
      
          return epoch;
      }
      

      样本输入:2019-10-15T05:51:31.537979Z

      样本输出:1571128673

      【讨论】:

        【解决方案5】:

        @John-Skeet 给了我解决这个问题的线索。作为一个年轻的程序员,这个小问题很容易被忽略,也很难诊断。所以我分享它,希望它对某人有所帮助。

        我的问题是我想从我无法影响的 JSON 中解析以下包含时间戳的字符串,并将其放入更有用的变量中。但我不断收到错误。

        所以给定如下(注意ofPattern()里面的字符串参数;

        String str = "20190927T182730.000Z"
        
        LocalDateTime fin;
        fin = LocalDateTime.parse( str, DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss.SSSZ") );
        

        错误:

        Exception in thread "main" java.time.format.DateTimeParseException: Text 
        '20190927T182730.000Z' could not be parsed at index 19
        

        问题? Pattern 末尾的 Z 需要像 'T' 一样包裹在 'Z' 中。改变 "yyyyMMdd'T'HHmmss.SSSZ""yyyyMMdd'T'HHmmss.SSS'Z'" 并且有效。

        从模式中移除 Z 也会导致错误。

        坦率地说,我希望 Java 类能够预料到这一点。

        【讨论】:

        • herehere 已经提出并回答了这个问题。你的解决方案是错误的。 T 是一个字面量并且需要被引用,Z 是一个偏移量(零)并且需要被解析,否则你会得到错误的结果。我不知道你的意思是它没有被预料到,我相信它有。
        • 不,不,不,不要忽略Z。您正在丢弃有价值的信息。在忽略时区或从 UTC 偏移的情况下处理日期时间值就像在忽略货币的情况下处理金额一样!
        • T 只是将日期部分与时间部分分开,并没有添加任何意义。另一方面,Z 肯定会增加意义。
        • Ole,感谢您的链接。我肯定会读那些。我承认我作为一个年轻人肯定是错的。我所说的只是将 Z 用单引号括起来,就像模式中的字符一样解决了错误。我的批评是,无论是谁设计了课程的“模式”部分,都可能围绕“Z”(或不同时区?)的存在与否以及“T”是否包含在“”中进行编码。因为他们不是独一无二的。我处理的格式来自从商业 API 解析为字符串的 JSON。但我需要将所有这些解析为日期时间日历等,以使它们更有用。
        【解决方案6】:

        T 只是将日期与时间分开的文字,Z 表示“零小时偏移”,也称为“祖鲁时间”(UTC)。如果你的字符串 always 有一个“Z”,你可以使用:

        SimpleDateFormat format = new SimpleDateFormat(
            "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("UTC"));
        

        或者使用Joda Time,你可以使用ISODateTimeFormat.dateTime()

        【讨论】:

        • 为什么我们需要在TZ 周围加上单引号?
        • @MarounMaroun:基本上我们想要那些 literal 字符。 T 可能没有必要(我不记得 SimpleDateFormat 是如何处理未知说明符的),但对于 Z,我们希望它是字符“Z”而不是“UTC 偏移值”(例如“00”) .
        • @nyedidikeke:在您链接到的维基百科页面中,它显示 UTC 的“祖鲁时区”。我不确定您认为自己在纠正什么。
        • @JonSkeet:这可能会引起不必要的争论;不是对您的回答提出异议,而是想引起人们对 Z 的注意,它的字母首字母来自“零 UTC 偏移”。字母 Z 在北约音标中被称为“祖鲁”。反过来,参考零 UTC 偏移量的军事方法锚定在字母 Z 上,他们将其识别为 Zulu,从而获得了他们的代号:Zulu time zone。重要的是要注意 Z 并没有失去其意义,仍然是零 UTC 偏移量的区域指示符,因为祖鲁时区(来自 Z)只是一种继承的编码语言来引用它。
        • @nyedidikeke:我仍然不同意其他人是否会在参考我的答案时关心区别,但我已经更新了它。不过,我不会详细介绍所有细节,因为历史与答案大致无关。
        【解决方案7】:

        此技术将 java.util.Date 转换为 UTC 格式(或任何其他格式),然后再转换回来。

        像这样定义一个类:

        import java.util.Date;
        
        import org.joda.time.DateTime;
        import org.joda.time.format.DateTimeFormat;
        import org.joda.time.format.DateTimeFormatter;
        
        public class UtcUtility {
        
        public static DateTimeFormatter UTC = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZoneUTC();
        
        
        public static Date parse(DateTimeFormatter dateTimeFormatter, String date) {
            return dateTimeFormatter.parseDateTime(date).toDate();
        }
        
        public static String format(DateTimeFormatter dateTimeFormatter, Date date) {
            return format(dateTimeFormatter, date.getTime());
        }
        
        private static String format(DateTimeFormatter dateTimeFormatter, long timeInMillis) {
            DateTime dateTime = new DateTime(timeInMillis);
            String formattedString = dateTimeFormatter.print(dateTime);
            return formattedString;
        }
        

        }

        然后像这样使用它:

        Date date = format(UTC, "2020-04-19T00:30:07.000Z")
        

        String date = parse(UTC, new Date())
        

        如果需要,您还可以定义其他日期格式(不仅仅是 UTC)

        【讨论】:

        • java.util.DateJoda-Time 项目几年前都被 JSR 310 中定义的现代 java.time 类所取代。建议这里已经过时了。
        • java.time 是在 Java 8 中引入的。这个问题专门引用了 Java 1.4,因此我故意避免使用较新的类。该解决方案适用于较旧的代码库。我想作者可以将他的代码库迁移到 Java 8,但这并不总是简单、有效或必要的。
        【解决方案8】:

        您可以使用以下示例。

            String date = "2011-08-12T20:17:46.384Z";
        
            String inputPattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
        
            String outputPattern = "yyyy-MM-dd HH:mm:ss";
        
            LocalDateTime inputDate = null;
            String outputDate = null;
        
        
            DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern(inputPattern, Locale.ENGLISH);
            DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern(outputPattern, Locale.ENGLISH);
        
            inputDate = LocalDateTime.parse(date, inputFormatter);
            outputDate = outputFormatter.format(inputDate);
        
            System.out.println("inputDate: " + inputDate);
            System.out.println("outputDate: " + outputDate);
        

        【讨论】:

        • 您不应该在Z 周围加上撇号。这意味着期待但忽略那封信。但这封信不应该被忽视。该字母提供了有价值的信息,即该字符串用于 UTC,偏移量为零。您的格式正在丢弃这个重要的事实。此外,甚至无需费心定义此格式模式。内置了此模式的格式化程序。
        【解决方案9】:

        除了第一个答案之外,还有其他方法可以解析它。解析它:

        (1) 如果要获取日期和时间的信息,可以将其解析为ZonedDatetime(since Java 8) 或Date(old) 对象:

        // ZonedDateTime's default format requires a zone ID(like [Australia/Sydney]) in the end.
        // Here, we provide a format which can parse the string correctly.
        DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
        ZonedDateTime zdt = ZonedDateTime.parse("2011-08-12T20:17:46.384Z", dtf);
        

        // 'T' is a literal.
        // 'X' is ISO Zone Offset[like +01, -08]; For UTC, it is interpreted as 'Z'(Zero) literal.
        String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX";
        
        // since no built-in format, we provides pattern directly.
        DateFormat df = new SimpleDateFormat(pattern);
        
        Date myDate = df.parse("2011-08-12T20:17:46.384Z");
        

        (2) 如果您不关心日期和时间,只想将信息视为以纳秒为单位的时刻,那么您可以使用Instant

        // The ISO format without zone ID is Instant's default.
        // There is no need to pass any format.
        Instant ins = Instant.parse("2011-08-12T20:17:46.384Z");
        

        【讨论】:

          【解决方案10】:

          不确定 Java 解析,但那是 ISO8601:http://en.wikipedia.org/wiki/ISO_8601

          【讨论】:

          • 这也是“2016-01-27T17:44:55UTC”,ISO8601 吗?
          • 我不这么认为。它很接近,但不允许将 UTC 作为后缀。它必须是 Z 或时区偏移量,例如 +0100。不过,Z 和 UTC 具有相同的含义,因此将 UTC 更改为 Z 将产生有效的 ISO 8601。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-07-19
          • 2021-06-24
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多