无法将 ISO 8601 日期转换为 Android 中的字符串

Can't convert ISO 8601 date to String in Android

我有一个 Android 应用程序,它显示了票证的创建日期以及我从服务器获得的信息。

问题是我无法解析我得到的字符串当我尝试使用 SimpleDateFormat 解析它时出现异常

这是我从服务器得到的:

2017-07-04 23:59:51.486559-05

这是我用来解析它的格式:

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSX", Locale.ENGLISH);

我用这种方法得到的例外是:

java.lang.IllegalArgumentException: Unknown pattern character 'X'
 at java.text.SimpleDateFormat.validatePatternCharacter(SimpleDateFormat.java:323)
 at java.text.SimpleDateFormat.validatePattern(SimpleDateFormat.java:312)
 at java.text.SimpleDateFormat.<init>(SimpleDateFormat.java:365)
 at com.unipagos.app.wallet.payment.receipts.datamodels.PaymentReceipt.dateFromString(PaymentReceipt.java:320)

我已经用了几个小时了,我也试过

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.ENGLISH);

无济于事,得到这个异常:

java.text.ParseException: Unparseable date: "2016-05-30 20:41:19.934959+00" (at offset 23)
 at java.text.DateFormat.parse(DateFormat.java:579)
 at com.unipagos.app.wallet.payment.receipts.datamodels.PaymentReceipt.dateFromString(PaymentReceipt.java:326)
 at com.unipagos.app.wallet.payment.receipts.datamodels.PaymentReceipt.loadReceiptJSONObject(PaymentReceipt.java:164)
 at com.unipagos.app.history.HistoryListFragment.populateTransactionList(HistoryListFragment.java:371)
 at com.unipagos.app.history.HistoryListFragment.access00(HistoryListFragment.java:83)

我是不是漏掉了什么?

SimpleDateFormat 无法解析您的日期时间字符串。字符串有微秒(秒上有6位小数),其中SimpleDateFormat只支持毫秒(3位小数)。

三十ABP

所以这似乎是您放弃早已过时的 类 DateSimpleDateFormat 的好时机。一般经验表明,现代 Java 日期和时间 API 更易于使用。对于 Android,您可以在下面的 ThreeTenABP、link 中找到它。当你知道怎么做时,剩下的就很简单了:

    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSX", Locale.ENGLISH);
    OffsetDateTime odt = OffsetDateTime.parse(creationDateFromServer, dtf);
    System.out.println(odt);

这会打印

2017-07-04T23:59:51.486559-05:00

这与您的输入一致,2017-07-04 23:59:51.486559-05。顺便说一下,打印的字符串是 ISO 8601;这就是现代 类' toString() 方法产生的结果。

老式解决方案

如果您坚持使用过时的 类,一种方法是去掉最后三位小数,这样只有三位,在时区偏移量上加上两位数,所以有四位,并且使用 Z 来解析它:

    creationDateFromServer = creationDateFromServer
            .replaceFirst("(\.\d{3})\d{3}((?:\+|-)\d{2})$", "0");
    DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ", Locale.ENGLISH);
    try {
        Date d = df.parse(creationDateFromServer);
        System.out.println(d);
    } catch (ParseException pe) {
        System.out.println(pe + " at offset " + pe.getErrorOffset());
    }

请注意阅读正则表达式有多么困难。此外,如果有一天出现偏移量,例如 +0530,这也会中断。输出为:

Wed Jul 05 06:59:51 CEST 2017

Date 在我的本地时区 打印,在本例中偏移量为 +0200,因此打印的时间与 23:59:51.486 一致前一天 -0500。这意味着 Date 对象在内部持有正确的时间点。

Link