Kotlin 无法解析的日期 - ParseException 错误

Kotlin Unparseable date - ParseException error

我在尝试解析日期字符串时看到以下错误,任何人都可以指出正确的方向来解析这个日期字符串吗?

"2019-01-22T12:43:01Z"

错误:

java.text.ParseException: Unparseable date: "2019-01-22T12:43:01Z"

代码:

package ch02.ex1_1_HelloWorld

import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.Date
import java.util.concurrent.TimeUnit

const val SERVER_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss:SS'Z'" 
val sdf = SimpleDateFormat(SERVER_TIME_FORMAT, Locale.US)

fun main(args: Array<String>) {
    timeSince("2019-01-22T12:43:01Z")
}

fun timeSince(dateStr: String) {
    var diff : Long = 0
    try {
        diff = sdf.parse(dateStr).time - Date().time      
    } catch (e: Exception) {
        print(e)
    }
    "${TimeUnit.HOURS.convert(diff, TimeUnit.MILLISECONDS)} h ago"
} 

由于您的输入不包含毫秒,您可以删除模式中的:SS

const val SERVER_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'" 

我建议使用 java.time 包。

java.time

import java.time.Instant;
import java.time.temporal.ChronoUnit;

fun timeSince(dateStr: String): String {
    val diffHours = ChronoUnit.HOURS.between(Instant.parse(dateStr), Instant.now())
    return "%d h ago".format(diffHours)
}

让我们试试看:

fun main() {
    println(timeSince("2019-01-22T12:43:01Z"))
}

这刚刚打印:

236 h ago

我正在使用 java.time,现代 Java 日期和时间 API。与旧的 类 Date 相比,尤其是 SimpleDateFormat 我发现它更好用。如果传递给它的字符串不是 ISO 8601 格式,上面的方法将抛出 DateTimeParseException。对于大多数用途,这可能比返回 0 h ago 更好。该方法容忍秒上存在和不存在(最多 9 个)小数,因此接受例如 2019-01-22T12:43:01.654321Z.

我们不需要格式化程序吗?不,我正在利用您的字符串采用 ISO 8601 格式这一事实,即现代日期和时间 类 解析(并打印)为默认格式的格式。

我希望我可以使用 ChronoUnitInstant

编辑:

I wish I could use ChronoUnit & Instant, but it requires a min V26 of Android. My current min is 23.

java.time 在旧的 Android 设备上也能很好地工作。它只需要至少 Java 6.

  • 在 Java 8 和更新的 Android 设备上(从 API 级别 26)内置现代 API。
  • 在 Java 6 和 7 中获取现代 类 的 ThreeTen Backport(ThreeTen 用于 JSR 310;请参阅底部的链接)。
  • 在(较旧的)Android 使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP。并确保使用子包从 org.threeten.bp 导入日期和时间 类。

你的代码出了什么问题?

我发现你问题中的代码有两个错误:

  1. 正如竹棒芒鞋轻胜马已经指出的那样,你的格式要求在TZ之间有4个部分,用冒号分隔,但你的字符串只有三个零件在那里。 SimpleDateFormat 在秒的小数部分使用两个 SS 也是错误的,因为大写的 S 代表毫秒,所以只有三个 SSS 才有意义(相比之下,现代 DateTimeFormatter S 代表几分之一秒,所以任何数字(最多 9 SSSSSSSSS)都是有意义的。
  2. 您将字符串中的 Z 视为文字。它是零的 UTC 偏移量,需要这样解析,否则你会得到不正确的时间(在绝大多数 JVM 上)。

链接

我从服务器得到的原始值是:

2021-04-08T11:27:40.278Z

您可以简单地缩短它。 使用 kotlin,2021 年:

// Read the value until the minutes only
val pattern = "yyyy-MM-dd'T'HH:mm"
val serverDateFormat = SimpleDateFormat(pattern, Locale.getDefault())
// Format the date output, as you wish to see, after we read the Date() value
val userPattern = "dd MMMM, HH:mm"
val userDateFormat = SimpleDateFormat(userPattern, Locale.getDefault())

val defaultDate = serverDateFormat.parse(INPUT_DATE_STRING)
if ( defaultDate != null ) {
   val userDate = userDateFormat.format(defaultDate)
   textView.text = userDate
}

结果:

08 April, 11:27