Skip to content

fix: Remove Joda Time dependency#518

Merged
kingargyle merged 1 commit intodevelopfrom
dac_migration_from_joda_time
Feb 16, 2026
Merged

fix: Remove Joda Time dependency#518
kingargyle merged 1 commit intodevelopfrom
dac_migration_from_joda_time

Conversation

@kingargyle
Copy link
Contributor

@kingargyle kingargyle commented Feb 15, 2026

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

    • Replaced legacy date/time library with Java Time API for improved maintainability and reduced dependencies.
    • Enabled core library desugaring support to enhance Android compatibility across supported versions.
    • Removed Joda-Time runtime initialization calls.
  • Refactor

    • Updated date/time handling throughout the application to use standard Java Time APIs.

Migrate to using the built in Java date time library.  Desugar on devices
that don't support it natively.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 15, 2026

Walkthrough

The 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

Cohort / File(s) Summary
Build Configuration & Dependencies
emby-lib/build.gradle.kts, jellyfin-lib/build.gradle.kts, serenity-app/build.gradle.kts, gradle/libs.versions.toml
Enabled core library desugaring with isCoreLibraryDesugaringEnabled = true, added coreLibraryDesugaring(libs.desugar.jdk.libs) dependency, removed joda.time dependency, and updated version definitions in libs.versions.toml to add desugar v2.1.4 while removing joda-time reference.
JSON Adapters
emby-lib/src/main/kotlin/.../LocalDateJsonAdapter.kt, jellyfin-lib/src/main/kotlin/.../LocalDateJsonAdapter.kt
Replaced org.joda.time imports with java.time equivalents, added @SuppressLint("NewApi") annotation, introduced private DateTimeFormatter with pattern "yyyy-MM-dd'T'HH:mm:ss.", updated fromJson to parse with new formatter and handle null/empty strings.
Model Files
emby-lib/src/main/kotlin/.../ItemsModel.kt, emby-lib/src/main/kotlin/.../UsersModel.kt, jellyfin-lib/src/main/kotlin/.../ItemsModel.kt, jellyfin-lib/src/main/kotlin/.../UsersModel.kt
Updated LocalDateTime import from org.joda.time to java.time across all data class definitions. No structural changes to data classes or public APIs.
API Clients
emby-lib/src/main/kotlin/.../EmbyAPIClient.kt, jellyfin-lib/src/main/kotlin/.../JellyfinAPIClient.kt
Updated LocalDateTime import from org.joda.time to java.time in Moshi adapter registrations. Adapter behavior remains unchanged.
Application & Test Cleanup
serenity-app/src/main/java/.../SerenityApplication.kt, emby-lib/src/test/kotlin/.../EmbyAPIClientTest.kt
Removed JodaTimeAndroid initialization call and corresponding imports from application startup and test setup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

enhancement

Poem

🐰 Hopping through time with Java's new way,
Joda's goodbye at the end of the day,
Desugaring works with a desugar delight,
Build times are sleek and our format's just right!

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: removing the Joda Time dependency and migrating to java.time with Core Library Desugaring support.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into develop

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dac_migration_from_joda_time

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 = true in the build config, AGP's lint should already recognize that java.time APIs are available via desugaring. The @SuppressLint works 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.

Comment on lines +13 to +17
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

fd "LocalDateJsonAdapter.kt" -type f

Repository: NineWorlds/serenity-android

Length of output: 241


🏁 Script executed:

find . -name "*.kt" -path "*emby*" -o -name "*.kt" -path "*jellyfin*" | head -20

Repository: NineWorlds/serenity-android

Length of output: 1765


🏁 Script executed:

cat -n emby-lib/src/main/kotlin/us/nineworlds/serenity/emby/moshi/LocalDateJsonAdapter.kt

Repository: 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.kt

Repository: 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.

Suggested change
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"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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_libs is 2.1.5 (released Feb 25, 2025) [1]
  • (and the matching config artifact) com.android.tools:desugar_jdk_libs_configuration is also 2.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_libs latest versions list (shows 2.1.5, Feb 25, 2025) (mvnrepository.com)
  • [2] Maven Repository — com.android.tools:desugar_jdk_libs_configuration latest versions list (shows 2.1.5, Feb 25, 2025) (mvnrepository.com)

Citations:


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.

Comment on lines +10 to +14
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Find and examine the LocalDateJsonAdapter.kt file in jellyfin-lib
find . -name "LocalDateJsonAdapter.kt" -type f

Repository: 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 f

Repository: NineWorlds/serenity-android

Length of output: 163


🏁 Script executed:

# Check if emby-lib directory exists
fd "emby-lib" -type d

Repository: 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.kt

Repository: 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.kt

Repository: 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:

  1. fromJson fails 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 a DateTimeParseException.

  2. Round-trip is broken. toJson emits LocalDateTime.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
Copy link

codecov bot commented Feb 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 53.21%. Comparing base (dec7ce5) to head (0152b04).
⚠️ Report is 2 commits behind head on develop.

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              
Flag Coverage Δ
unittests 53.21% <ø> (-0.03%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kingargyle kingargyle merged commit 9ff3d52 into develop Feb 16, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant