【问题标题】:How to convert UTC Date String and remove the T and Z in Java?如何转换 UTC 日期字符串并删除 Java 中的 T 和 Z?
【发布时间】:2018-11-03 01:37:28
【问题描述】:

我正在使用 Java 1.7。

尝试转换:

2018-05-23T23:18:31.000Z 

进入

2018-05-23 23:18:31

DateUtils 类:

public class DateUtils {

    public static String convertToNewFormat(String dateStr) throws ParseException {
        TimeZone utc = TimeZone.getTimeZone("UTC");
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        sdf.setTimeZone(utc);
        Date convertedDate = sdf.parse(dateStr);
        return convertedDate.toString();
    }
}

尝试使用时:

String convertedDate = DateUtils.convertToNewFormat("2018-05-23T23:18:31.000Z");
System.out.println(convertedDate);

得到以下异常:

Exception in thread "main" java.text.ParseException: Unparseable date: "2018-05-23T23:22:16.000Z"
   at java.text.DateFormat.parse(DateFormat.java:366)
   at com.myapp.utils.DateUtils.convertToNewFormat(DateUtils.java:7)

我可能做错了什么?

有没有更简单的方法(例如 Apache Commons lib)?

【问题讨论】:

  • 虽然您可以删除T,但Z 是偏移量,应该按原样解析,否则很可能出现错误结果。顺便考虑一下扔掉长期过时且臭名昭著的麻烦 SimpleDateFormat 和朋友,并将 ThreeTen Backport 添加到您的项目中,以便使用现代 Java 日期和时间 API java.time。使用起来感觉好多了。
  • 巧合的是,我昨天刚刚写了this answer,它显示了解析字符串的正确、现代、良好和简单的方法。
  • 您输入的日期时间字符串采用 UTC(由 Z 后缀表示)。你想要你的输出在你当地的时区吗?或者哪个时区?
  • 仅供参考,麻烦的旧日期时间类,例如 java.util.Datejava.util.Calendarjava.text.SimpleDateFormat 现在是 legacy,被 Java 8 及更高版本中内置的 java.time 类所取代.见Tutorial by Oracle

标签: java java-7 utc datetime-parsing parseexception


【解决方案1】:

tl;博士

Instant.parse( "2018-05-23T23:18:31.000Z" )                // Parse this String in standard ISO 8601 format as a `Instant`, a point on the timeline in UTC. The `Z` means UTC.
.atOffset( ZoneOffset.UTC )                                // Change from `Instant` to the more flexible `OffsetDateTime`.
.format(                                                   // Generate a String representing the value of this `OffsetDateTime` object.
    DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm:ss" )   // Specify a formatting pattern as desired.
)                                                          // Returns a `String` object.

2018-05-23 23:18:31

ISO 8601

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

java.time 类在解析/生成字符串时默认使用这些标准格式。

T 将年-月-日部分与时-分-秒分开。 Z 发音为 Zulu,意思是 UTC

java.time

您正在使用多年前被 java.time 类取代的麻烦的旧日期时间类。 Apache DateUtils 也不再需要,因为您也可以在 java.time 中找到它的功能。

将该输入字符串解析为Instant 对象。 Instant 类表示 UTC 时间线上的时刻,分辨率为 nanoseconds(最多九 (9) 位小数)。

String input = "2018-05-23T23:18:31.000Z" ;
Instant instant = Instant.parse( input ) ;

要生成另一种格式的字符串,我们需要一个更灵活的对象。 Instant 类是一个基本的构建块。 Lets convert it to a OffsetDateTime`,使用 UTC 本身作为指定的 offset-from-UTC。

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; 

定义格式模式以匹配所需的输出。

DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu-MM-dd HH:mm:ss" ) ;
String output = odt.format( f ) ;

提示:考虑使用DateTimeFormatter::ofLocalized… 方法自动本地化每个Locale 的字符串生成,而不是硬编码格式模式。


关于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 类?

【讨论】:

    【解决方案2】:

    试试这个。您必须使用一种模式进行解析,另一种模式进行格式化。

    public static String convertToNewFormat(String dateStr) throws ParseException {
        TimeZone utc = TimeZone.getTimeZone("UTC");
        SimpleDateFormat sourceFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        SimpleDateFormat destFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sourceFormat.setTimeZone(utc);
        Date convertedDate = sourceFormat.parse(dateStr);
        return destFormat.format(convertedDate);
    }
    

    【讨论】:

    • kotlin 中的相同代码:val utc = TimeZone.getTimeZone("UTC") val sourceFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") val destFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") sourceFormat.setTimeZone(utc) val convertDate : Date = sourceFormat.parse(dateStr) val result = destFormat.format(convertedDate)
    【解决方案3】:

    对于没有 Java 1.7 限制的其他人:

    从 Java 1.8 开始,您可以使用 java.time 包中的 LocalDateTimeZonedDateTime 来完成此操作

    public static void main(String[] args) {
        String sourceDateTime           = "2018-05-23T23:18:31.000Z";
        DateTimeFormatter sourceFormat  = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        DateTimeFormatter targetFormat  = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
        LocalDateTime dateTime          = LocalDateTime.parse(sourceDateTime, sourceFormat);
        String formatedDateTime         = dateTime.atZone(ZoneId.of("UTC")).format(targetFormat);
        System.out.println(formatedDateTime);
    }
    

    编辑:(见评论)

    引用自 LocalDateTime 的 Oracle Java 文档:

    LocalDateTime 是一个不可变的日期时间对象,它代表一个 日期-时间,通常被视为年-月-日-时-分-秒。其他 日期和时间字段,例如日期、星期几和 一年中的一周,也可以访问。时间以纳秒表示 精确。例如,值“2007 年 10 月 2 日 13:45.30.123456789" 可以存储在 LocalDateTime 中。

    此类不存储或表示时区。相反,它是一个 日期的描述,用于生日,结合 挂钟上的当地时间。它不能代表一个瞬间 没有附加信息的时间线,例如偏移量或 时区。

    OP 要求仅将输入字符串解析为日期时间(如年-月-日-时-分-秒)并且文档说

    LocalDateTime ... 表示日期时间,通常被视为 年-月-日-时-分-秒

    所以这里不会丢失任何重要信息。而dateTime.atZone(ZoneId.of("UTC")) 部分返回ZonedDateTime,因此如果用户需要使用时区...等,则在此时再次处理 ZimeZone。

    所以不要试图强迫用户使用您在回答中提出的“唯一”解决方案。

    【讨论】:

    • 使用LocalDateTime 意味着您丢弃了重要信息,却一无所获。
    • 当您将ThreeTen Backport 添加到您的项目时,这也将在Java 1.7 中很好地工作。您不需要用于输入的格式化程序。 Instant.parse 和单参数 OffsetDateTime.parse 将在没有任何显式格式化程序的情况下解析源字符串。
    • @BasilBourque 查看我的编辑! @ OleV.V:如果用户问了这么简单的问题,那么最好给他/她直接和信息丰富的答案,这样他/她才能理解功能。之后,他/她可以比较其他可用的类/方法。您说“您不需要输入的格式化程序”,只有当输入始终采用这种“标准格式”时,这才是正确的,否则您需要格式化程序,所以我在我的回答中提到了它。谢谢
    【解决方案4】:

    YYYY 与年份部分不匹配。在 Java 7 中,您需要 yyyy

    对于T,使用'T'来匹配它

    你也错过了毫秒部分的派系:.SSS

    试试这个:

    String dateStr="2018-05-23T23:18:31.000Z";
    TimeZone utc = TimeZone.getTimeZone("UTC");
    SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    sdf.setTimeZone(utc);
    Date convertedDate = sdf.parse(dateStr);
    convertedDate.toString();
    

    【讨论】:

    • 谢谢 Manh,但打印出来的日期为 2018 年 5 月 23 日 11:18:31 PDT - 我只希望它是 2018-05-23 23:18:31
    • 您必须为其提供格式。 SimpleDateFormat sdf2 = new SimpleDateFormat('yyyy-MM-dd HH:mm:ss'); return sdf2.format(convertedDate);
    • 在之前回复你之前试过了,它仍然给出了 ParseException。
    • 它按预期工作。你卡在哪一部分?我在上面SimpleDateFormat 有一个错字,它需要4 个y 而不是yyy
    【解决方案5】:

    在 Kotlin 中并使用 ThreeTenABP,

    fun getIsoString(year: Int, month: Int, day: Int): String {
        val localTime = ZonedDateTime.of(year, month, day, 0, 0, 0, 0, ZoneId.of("Z"))
    
        val utcTime = localTime.toOffsetDateTime().withOffsetSameInstant(ZoneOffset.UTC)
    
        val isoString = utcTime.toInstant().toString() // 1940-15-12T00:00:00Z
    
        val formattedIsoString = val formattedIsoString =
            Instant.parse(isoString)
                .atOffset(ZoneOffset.UTC)
                .format(DateTimeFormatter
                    .ofPattern("uuuu-MM-dd'T'HH:mm:ss")) // 'T' in quotes so that it is retained.
    
        return formattedIsoString
    }
    
    // print it
    print(getIsoString(1940, 15, 12)) // 1940-15-12T00:00:00
    

    【讨论】:

      【解决方案6】:

      你可以在这个想法下面试试这个。 我不是 JAVA 专家,但我是在 javascript/node.js 中完成的

      import * as momentTimeZone from 'moment-timezone';
      
      let d = new Data(); // d = '2018-05-23T23:18:31.000Z'; or we can take this date
      let finalOutput = momentTimeZone(d).tz(this.locationService.locationTimeZone).utc().format('YYYY-MM-DD HH:mm:ss');
      console.log('Result: ', finalOutput); // Result: "2018-05-23 23:18:31";
      

      它也适用于moment.js。 这里有更多关于format的信息。

      【讨论】:

        猜你喜欢
        • 2022-12-09
        • 2012-07-02
        • 2021-04-19
        • 1970-01-01
        • 1970-01-01
        • 2013-07-15
        • 1970-01-01
        • 2022-11-15
        • 1970-01-01
        相关资源
        最近更新 更多