Conversation
Migrate to using the built in Java date time library. Desugar on devices that don't support it natively.
WalkthroughThe pull request migrates the codebase from Joda-Time to Java's native java.time library and enables Android core library desugaring. Changes include enabling desugaring in build configurations, adding the desugar.jdk.libs dependency, replacing Joda-Time imports with java.time equivalents across models and adapters, updating JSON serialization logic, and removing JodaTime initialization. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In
`@emby-lib/src/main/kotlin/us/nineworlds/serenity/emby/moshi/LocalDateJsonAdapter.kt`:
- Around line 13-17: The current date formatter (dateFormatter) expects a
trailing dot which causes LocalDateTime.parse in fromJson to throw for
timestamps without fractional seconds; update dateFormatter to use a pattern
without the trailing dot (e.g., "yyyy-MM-dd'T'HH:mm:ss") and change fromJson in
LocalDateJsonAdapter (and the jellyfin-lib counterpart) to strip fractional
seconds safely by removing everything after the '.' only if present (e.g., split
on '.' and take the first segment) before parsing; also update toJson to format
the LocalDateTime using the same dateFormatter instead of
LocalDateTime.toString() so serialization and parsing are symmetric.
In `@gradle/libs.versions.toml`:
- Line 67: Update the desugar library version constant by changing the
desugarVersion value from "2.1.4" to "2.1.5" in the versions TOML so the project
uses com.android.tools:desugar_jdk_libs v2.1.5; locate the desugarVersion entry
(symbol: desugarVersion) and replace its value accordingly.
In
`@jellyfin-lib/src/main/kotlin/us/nineworlds/serenity/jellyfin/moshi/LocalDateJsonAdapter.kt`:
- Around line 10-14: fromJson in LocalDateJsonAdapter is using a custom pattern
"yyyy-MM-dd'T'HH:mm:ss." and replaceAfter(".", "") which fails for timestamps
without fractional seconds and breaks round-trip; change the adapter to use
DateTimeFormatter.ISO_LOCAL_DATE_TIME for both parsing and formatting (replace
the dateFormatter declaration and have fromJson call LocalDateTime.parse(...,
DateTimeFormatter.ISO_LOCAL_DATE_TIME)) and ensure toJson uses the same
ISO_LOCAL_DATE_TIME formatter (or
LocalDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); apply the
identical change to the LocalDateJsonAdapter in the emby-lib so
parsing/serialization are consistent across both libs.
🧹 Nitpick comments (1)
emby-lib/src/main/kotlin/us/nineworlds/serenity/emby/moshi/LocalDateJsonAdapter.kt (1)
10-10:@SuppressLint("NewApi")is acceptable but worth noting.With
isCoreLibraryDesugaringEnabled = truein the build config, AGP's lint should already recognize that java.time APIs are available via desugaring. The@SuppressLintworks but may mask legitimate API-level warnings on other APIs in this class in the future. If lint still flags java.time despite desugaring being enabled, this is fine as-is.
| private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.") | ||
|
|
||
| override fun fromJson(reader: JsonReader): LocalDateTime? { | ||
| val dateTimeString = reader.nextString()?.replaceAfter(".", "") ?: return null | ||
| return LocalDateTime.parse(dateTimeString, dateFormatter) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd "LocalDateJsonAdapter.kt" -type fRepository: NineWorlds/serenity-android
Length of output: 241
🏁 Script executed:
find . -name "*.kt" -path "*emby*" -o -name "*.kt" -path "*jellyfin*" | head -20Repository: NineWorlds/serenity-android
Length of output: 1765
🏁 Script executed:
cat -n emby-lib/src/main/kotlin/us/nineworlds/serenity/emby/moshi/LocalDateJsonAdapter.ktRepository: NineWorlds/serenity-android
Length of output: 1012
🏁 Script executed:
find . -name "LocalDateJsonAdapter.kt" -path "*jellyfin*"Repository: NineWorlds/serenity-android
Length of output: 163
🏁 Script executed:
cat -n ./jellyfin-lib/src/main/kotlin/us/nineworlds/serenity/jellyfin/moshi/LocalDateJsonAdapter.ktRepository: NineWorlds/serenity-android
Length of output: 931
Critical: fromJson will throw DateTimeParseException for timestamps without fractional seconds.
The pattern "yyyy-MM-dd'T'HH:mm:ss." expects a trailing dot. However, replaceAfter(".", "") returns the input string unchanged if no dot is found (e.g., "2024-01-15T10:30:00"). Parsing then fails because the formatter requires the trailing dot.
Additionally, toJson serializes via LocalDateTime.toString(), which omits the dot when fractional seconds are zero, causing round-trip failures.
The same critical bug exists in the jellyfin-lib counterpart (./jellyfin-lib/src/main/kotlin/us/nineworlds/serenity/jellyfin/moshi/LocalDateJsonAdapter.kt).
Fix: Use a pattern without the trailing dot and strip fractional seconds cleanly:
Proposed fix
- private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.")
+ private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss")
override fun fromJson(reader: JsonReader): LocalDateTime? {
- val dateTimeString = reader.nextString()?.replaceAfter(".", "") ?: return null
+ val dateTimeString = reader.nextString() ?: return null
+ val truncated = dateTimeString.substringBefore(".")
- return LocalDateTime.parse(dateTimeString, dateFormatter)
+ return LocalDateTime.parse(truncated, dateFormatter)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.") | |
| override fun fromJson(reader: JsonReader): LocalDateTime? { | |
| val dateTimeString = reader.nextString()?.replaceAfter(".", "") ?: return null | |
| return LocalDateTime.parse(dateTimeString, dateFormatter) | |
| private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss") | |
| override fun fromJson(reader: JsonReader): LocalDateTime? { | |
| val dateTimeString = reader.nextString() ?: return null | |
| val truncated = dateTimeString.substringBefore(".") | |
| return LocalDateTime.parse(truncated, dateFormatter) |
🤖 Prompt for AI Agents
In
`@emby-lib/src/main/kotlin/us/nineworlds/serenity/emby/moshi/LocalDateJsonAdapter.kt`
around lines 13 - 17, The current date formatter (dateFormatter) expects a
trailing dot which causes LocalDateTime.parse in fromJson to throw for
timestamps without fractional seconds; update dateFormatter to use a pattern
without the trailing dot (e.g., "yyyy-MM-dd'T'HH:mm:ss") and change fromJson in
LocalDateJsonAdapter (and the jellyfin-lib counterpart) to strip fractional
seconds safely by removing everything after the '.' only if present (e.g., split
on '.' and take the first segment) before parsing; also update toJson to format
the LocalDateTime using the same dateFormatter instead of
LocalDateTime.toString() so serialization and parsing are symmetric.
| espressoCoreVersion = "3.5.1" | ||
| lifecycleProcessVersion = "2.10.0" | ||
| datastore = "1.1.2" | ||
| desugarVersion = "2.1.4" |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
desugar_jdk_libs latest version 2026 Android tools
💡 Result:
As of February 15, 2026, the latest published Google Maven version of:
com.android.tools:desugar_jdk_libsis2.1.5(released Feb 25, 2025) [1]- (and the matching config artifact)
com.android.tools:desugar_jdk_libs_configurationis also2.1.5[2]
Gradle usage:
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
}Sources: [1] [2]
References
- [1] Maven Repository —
com.android.tools:desugar_jdk_libslatest versions list (shows 2.1.5, Feb 25, 2025) (mvnrepository.com) - [2] Maven Repository —
com.android.tools:desugar_jdk_libs_configurationlatest versions list (shows 2.1.5, Feb 25, 2025) (mvnrepository.com)
Citations:
- 1: https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs?utm_source=openai
- 2: https://mvnrepository.com/artifact/com.android.tools/desugar_jdk_libs_configuration?utm_source=openai
Update desugar library to version 2.1.5.
The module coordinates (com.android.tools:desugar_jdk_libs) are correct, but version 2.1.4 is outdated. Version 2.1.5 was released on February 25, 2025, and is now the latest available. Update desugarVersion = "2.1.4" to desugarVersion = "2.1.5" in gradle/libs.versions.toml (line 67).
🤖 Prompt for AI Agents
In `@gradle/libs.versions.toml` at line 67, Update the desugar library version
constant by changing the desugarVersion value from "2.1.4" to "2.1.5" in the
versions TOML so the project uses com.android.tools:desugar_jdk_libs v2.1.5;
locate the desugarVersion entry (symbol: desugarVersion) and replace its value
accordingly.
| private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.") | ||
|
|
||
| return LocalDateTime.parse(dateTime, dateformater) | ||
| override fun fromJson(reader: JsonReader): LocalDateTime? { | ||
| val dateTimeString = reader.nextString()?.replaceAfter(".", "") ?: return null | ||
| return LocalDateTime.parse(dateTimeString, dateFormatter) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find and examine the LocalDateJsonAdapter.kt file in jellyfin-lib
find . -name "LocalDateJsonAdapter.kt" -type fRepository: NineWorlds/serenity-android
Length of output: 248
🏁 Script executed:
# Search for all JsonAdapter files to understand the structure
find . -path "*/jellyfin/*" -name "*JsonAdapter.kt" -type fRepository: NineWorlds/serenity-android
Length of output: 163
🏁 Script executed:
# Check if emby-lib directory exists
fd "emby-lib" -type dRepository: NineWorlds/serenity-android
Length of output: 241
🏁 Script executed:
# Read the jellyfin-lib LocalDateJsonAdapter.kt file
cat -n ./jellyfin-lib/src/main/kotlin/us/nineworlds/serenity/jellyfin/moshi/LocalDateJsonAdapter.ktRepository: NineWorlds/serenity-android
Length of output: 931
🏁 Script executed:
# Read the emby-lib LocalDateJsonAdapter.kt file
cat -n ./emby-lib/src/main/kotlin/us/nineworlds/serenity/emby/moshi/LocalDateJsonAdapter.ktRepository: NineWorlds/serenity-android
Length of output: 1012
Parsing breaks for timestamps without fractional seconds and round-trip is broken in both jellyfin-lib and emby-lib.
Two critical issues with this approach:
-
fromJsonfails when there are no fractional seconds.replaceAfter(".", "")returns the original string unchanged when no dot is found (e.g.,"2024-01-15T10:30:00"), which won't match the formatter pattern that requires a trailing dot — causing aDateTimeParseException. -
Round-trip is broken.
toJsonemitsLocalDateTime.toString()(e.g.,"2024-01-15T10:30:00") with no trailing dot, so re-reading the serialized value hits the same parse failure.
The same issues exist in emby-lib/src/main/kotlin/us/nineworlds/serenity/emby/moshi/LocalDateJsonAdapter.kt.
Use DateTimeFormatter.ISO_LOCAL_DATE_TIME instead, which handles optional fractional seconds natively and is compatible with LocalDateTime.toString():
🐛 Proposed fix
class LocalDateJsonAdapter : JsonAdapter<LocalDateTime>() {
- private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.")
override fun fromJson(reader: JsonReader): LocalDateTime? {
- val dateTimeString = reader.nextString()?.replaceAfter(".", "") ?: return null
- return LocalDateTime.parse(dateTimeString, dateFormatter)
+ val dateTimeString = reader.nextString() ?: return null
+ return LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_LOCAL_DATE_TIME)
}
override fun toJson(writer: JsonWriter, value: LocalDateTime?) {
writer.value(value?.toString())
}
}Apply the same fix to both jellyfin-lib and emby-lib adapters.
🤖 Prompt for AI Agents
In
`@jellyfin-lib/src/main/kotlin/us/nineworlds/serenity/jellyfin/moshi/LocalDateJsonAdapter.kt`
around lines 10 - 14, fromJson in LocalDateJsonAdapter is using a custom pattern
"yyyy-MM-dd'T'HH:mm:ss." and replaceAfter(".", "") which fails for timestamps
without fractional seconds and breaks round-trip; change the adapter to use
DateTimeFormatter.ISO_LOCAL_DATE_TIME for both parsing and formatting (replace
the dateFormatter declaration and have fromJson call LocalDateTime.parse(...,
DateTimeFormatter.ISO_LOCAL_DATE_TIME)) and ensure toJson uses the same
ISO_LOCAL_DATE_TIME formatter (or
LocalDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); apply the
identical change to the LocalDateJsonAdapter in the emby-lib so
parsing/serialization are consistent across both libs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #518 +/- ##
=============================================
- Coverage 53.23% 53.21% -0.03%
Complexity 518 518
=============================================
Files 69 69
Lines 2303 2302 -1
Branches 329 329
=============================================
- Hits 1226 1225 -1
Misses 911 911
Partials 166 166
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|



Migrate to using the built in Java date time library. Desugar on devices that don't support it natively.
Summary by CodeRabbit
Release Notes
Chores
Refactor