仅适用于某些用户的 DateTimeFormatter 错误

DateTimeFormatter error for some users only

提问人:Code Poet 提问时间:8/16/2023 最后编辑:Code Poet 更新时间:8/26/2023 访问量:105

问:

我遇到了这种奇怪的情况,我的函数使用 DateTimeFormatter 对我来说非常有效(即 rss 提要列表按其发布的顺序排序),但对于我的应用程序的某些用户来说,它崩溃了。起初,我以为可能存在与语言环境相关的问题,但似乎所有 rss 提要都使用该格式,所以现在我没有想法了。我还收到了设备上设置了美式英语的用户崩溃的报告(我使用英式英语)。我的代码有问题还是其他可能的原因?locale.English

我使用 Rome 解析器,pubDates 是这样解析的:Sat Aug 12 12:51:34 GMT+01:00 2023

    suspend fun sortDateTimeAndSaveLatestHeadline() {
    val dataStore = AppDataStore(getApplication())
    val listOfStringDates: MutableList<String> = mutableListOf()
    val listOfHeadlines: MutableList<String> = mutableListOf()
    val listOfFeedTitles: MutableList<String> = mutableListOf()

    newsFeed.value?.forEach { feed ->
        listOfStringDates.add(feed.feedItem.pubDate)
        listOfHeadlines.add(feed.feedItem.title)
        listOfFeedTitles.add(feed.feedTitle)
    }

    val zippedLists = listOfStringDates.zip(listOfHeadlines)
        .zip(listOfFeedTitles) { (a, b), c -> Triple(a, b, c) }

    val dateTimeFormatter: DateTimeFormatter =
        DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss O yyyy",  Locale.ENGLISH)

    val result = zippedLists.sortedByDescending {
        LocalDateTime.parse(it.first, dateTimeFormatter)
    }

    if (result.isNotEmpty()) {
        dataStore.saveTileHeadline(result[0].second)
        dataStore.saveFeedTitle(result[0].third)
        println("Headline = ${result[0].second}")
    }
}

这是堆栈跟踪:

  Exception java.time.format.DateTimeParseException:
  at java.time.format.DateTimeFormatter.parseResolved0 (DateTimeFormatter.java:1949)
  at java.time.format.DateTimeFormatter.parse (DateTimeFormatter.java:1851)
  at java.time.LocalDateTime.parse (LocalDateTime.java:486)
  at java.util.TimSort.countRunAndMakeAscending (TimSort.java:355)
  at java.util.TimSort.sort (TimSort.java:220)
  at java.util.Arrays.sort (Arrays.java:1424)
  at kotlin.collections.CollectionsKt___CollectionsKt.sortedWith (CollectionsKt___Collections.kt)
  at com.strangerweather.news.presentation.screens.screens.FeedItemsScreenKt$FeedItemsScreen$7$1.invoke (FeedItemsScreen.kt)
  at androidx.wear.compose.foundation.lazy.ScalingLazyColumnKt$ScalingLazyColumn$1$1$2$1.invoke (ScalingLazyColumn.kt)
  at androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent.<init> (LazyLayoutIntervalContent.java)
  at androidx.compose.foundation.lazy.LazyListIntervalContent.<init> (LazyListIntervalContent.java)
  at androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.java)
  at androidx.compose.runtime.DerivedSnapshotState.currentRecord (DerivedSnapshotState.java)
  at androidx.compose.runtime.snapshots.SnapshotKt.getLock (Snapshot.kt)
  at androidx.compose.runtime.DerivedSnapshotState$ResultRecord.readableHash (DerivedSnapshotState.java)
  at androidx.compose.runtime.snapshots.SnapshotKt.getLock (Snapshot.kt)
  at androidx.compose.runtime.DerivedSnapshotState$ResultRecord.isValid (DerivedSnapshotState.java)
  at androidx.compose.runtime.DerivedSnapshotState.currentRecord (DerivedSnapshotState.java)
  at androidx.compose.runtime.DerivedSnapshotState.getCurrentRecord (DerivedSnapshotState.java)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.recordInvalidation (SnapshotStateObserver.java)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver.drainChanges (SnapshotStateObserver.java)
  at androidx.compose.runtime.snapshots.SnapshotStateObserver.access$drainChanges (SnapshotStateObserver.java)
  at androidx.compose.runtime.snapshots.SnapshotKt.advanceGlobalSnapshot (Snapshot.kt)
  at androidx.compose.runtime.snapshots.SnapshotKt.advanceGlobalSnapshot (Snapshot.kt)
  at androidx.compose.runtime.snapshots.SnapshotKt.access$advanceGlobalSnapshot (Snapshot.kt)
  at androidx.compose.runtime.snapshots.GlobalSnapshot.notifyObjectsInitialized$runtime_release (GlobalSnapshot.java)
  at androidx.compose.runtime.DerivedSnapshotState.currentRecord (DerivedSnapshotState.java)
  at androidx.compose.runtime.snapshots.Snapshot$Companion.getCurrent (Snapshot.java)
  at androidx.compose.runtime.DerivedSnapshotState.getValue (DerivedSnapshotState.java)
  at androidx.wear.compose.foundation.lazy.ScalingLazyListState.getLayoutInfo (ScalingLazyListState.java)
  at androidx.wear.compose.foundation.lazy.ScalingLazyListState.scrollToItem$compose_foundation_release (ScalingLazyListState.java)
  at androidx.wear.compose.foundation.lazy.ScalingLazyListState.scrollToInitialItem$compose_foundation_release (ScalingLazyListState.java)
  at androidx.wear.compose.foundation.lazy.ScalingLazyColumnKt$ScalingLazyColumn$1$1$3$1.invokeSuspend (ScalingLazyColumn.kt)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (BaseContinuationImpl.java)
  at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.java)
  at androidx.compose.ui.platform.AndroidUiDispatcher.nextTask (AndroidUiDispatcher.java)
  at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch (AndroidUiDispatcher.java)
  at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch (AndroidUiDispatcher.java)
  at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run (AndroidUiDispatcher.java)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:246)
  at android.app.ActivityThread.main (ActivityThread.java:7690)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:593)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:995)
Kotlin date datetime datetimeFormatter 罗马

评论

1赞 Nick Sherrill 8/16/2023
有没有办法向我们展示有问题的日期字符串?
2赞 Nick Sherrill 8/18/2023
该示例不会导致问题中描述的异常。您是否有某种方法可以捕获导致所提供异常的字符串示例?比如用 替换排序匿名函数。这将帮助您弄清楚哪种字符串破坏了您的解析器并计划如何解决它,因为您应该能够在错误消息中看到有问题的日期字符串。runCatching { LocalDate.parse(it.first, dateTimeFormatter) }.getOrElse { e-> throw IllegalArgumentException("Invalid date str: ${it.first}", e) }
2赞 Nick Sherrill 8/19/2023
虽然您的最终结果应该是 ,但在这种情况下,这并不是问题的真正根源。至少在我看来,这部分工作似乎失败了,从用户时区到系统时区的转换是完成此解析问题要完成的最后一步。我能够将提供的示例解析为 LocalDate 就好了。ZonedDateTimeDateTimeFormatter
1赞 Basil Bourque 8/19/2023
理想情况下,您不会解析此类格式化的字符串。这些应仅用于向用户演示。对于数据交换,仅使用标准 ISO 8601 格式。
1赞 Anonymous 8/26/2023
不要解析为 .您的字符串包含 UTC 偏移量或时区缩写,并且您想要选择它。此外,当 EDT 作为时区缩写不明确时。解析为LocalDateTimeZonedDateTime

答:

2赞 Arvind Kumar Avinash 8/19/2023 #1

更新:

感谢 Ole V.V. 的更新。

DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT)解析原始答案中的两个示例字符串就好了。

在线演示

原文答案:

从您在代码和注释中提供的描述来看,您的应用程序似乎正在以不同的形式获取时区信息,例如 GMT+01:00、EDT 等。因此,您应该对时区使用多个可选模式。

演示:

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss [O][z] uuuu", Locale.ENGLISH);

        // Test
        Stream.of(
                "Sat Aug 12 12:51:34 GMT+01:00 2023",
                "Fri Aug 18 12:34:50 EDT 2023"
        ).forEach(s -> System.out.println(ZonedDateTime.parse(s, dtf)));
    }
}

输出:

2023-08-12T12:51:34+01:00
2023-08-18T12:34:50-04:00[America/New_York]

在线演示

笔记:

  1. 我在演示中只使用了两种可选模式。您可以根据您的要求进一步扩展它,例如 有三种可选模式。"EEE MMM dd HH:mm:ss [O][z][VV] uuuu"
  2. 在这里,您可以使用代替,但我更喜欢 u 而不是 yyu

评论

0赞 Code Poet 8/20/2023
谢谢你!总共有多少种可选模式?是只有三个还是更多?我需要涵盖所有可能性......
0赞 Arvind Kumar Avinash 8/20/2023
@CodePoet,不幸的是,在自定义格式的情况下,我们需要指定所有必需的模式。
0赞 Code Poet 8/21/2023
因此,如果我理解正确,这是否涵盖了我在时区方面可能遇到的大多数情况:“EEE MMM dd HH:mm:ss [O][z][X][VV][V][v][x][Z] uuuu”?
0赞 Arvind Kumar Avinash 8/21/2023
像“EEE MMM dd HH:mm:ss [O][z][X[X[X]]][VV][x][Z[ZZZZ]] uuuu”一样使用它。请注意,需要用 double 表示,并且没有像 这样的符号。Vv
1赞 Arvind Kumar Avinash 8/26/2023
谢谢,@OleV.V.我已将您的评论纳入我的回答中。