Use & write Year, Month, Day info in ComicInfo (#3967)

* Use & write Year, Month, Day info in ComicInfo

Currently, users can only modify the date Mihon displays in the
chapter list by manipulating the _last modified date_ of the file.
This is obviously inconvenient, especially since the ComicInfo spec
includes Year, Month, and Day fields for this exact purpose.

If present, this reads the date at start of day in the current default
system time zone (which matches how we use Chapter.dateUpload with
relativeDateText, for example).

If Year is not specified, the entire date process is skipped.
If Month is not specified, it is set to January.
If Day is not specified, it is set to the first.

The parsed date (if any) is set as the Chapter's `date_upload` which
is used for display in the app.

Additionally, Mihon now includes this info in the written
ComicInfo.xml file for downloaded chapters. This will be convenient
for many more users, not least of which those who move
source-downloaded chapters to Local Source to read from there.

---

I used kotlinx.datetime because it's what we use for date parsing in
non-i18n places already.

* Changelog
This commit is contained in:
MajorTanya
2026-09-16 04:23:25 +02:00
committed by GitHub
parent 1c43addeca
commit a7179805ad
5 changed files with 78 additions and 26 deletions
+2
View File
@@ -31,4 +31,6 @@ dependencies {
implementation(libs.injekt)
implementation(libs.jsoup)
implementation(libs.kotlinx.datetime)
}
@@ -17,6 +17,9 @@ import eu.kanade.tachiyomi.util.lang.compareToCaseInsensitiveNaturalOrder
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.supervisorScope
import kotlinx.datetime.LocalDate
import kotlinx.datetime.TimeZone
import kotlinx.datetime.atStartOfDayIn
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import logcat.LogPriority
@@ -267,6 +270,19 @@ class LocalSource(
comicInfo.title?.let { chapter.name = it.value }
comicInfo.number?.value?.toFloatOrNull()?.let { chapter.chapter_number = it }
comicInfo.translator?.let { chapter.scanlator = it.value }
// only bother with partial dates if the year is not null, abandon date parsing otherwise
val year = comicInfo.year?.value?.takeIf { it > 0 }?.toString()?.padStart(2, '0') ?: return
val month = (comicInfo.month?.value?.coerceIn(1, 31) ?: 1).toString().padStart(2, '0')
val day = (comicInfo.day?.value?.coerceIn(1, 31) ?: 1).toString().padStart(2, '0')
val dateInstant = try {
LocalDate.parse("$year-$month-$day").atStartOfDayIn(TimeZone.currentSystemDefault())
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
null
}
dateInstant?.let { chapter.date_upload = it.toEpochMilliseconds() }
}
// Chapters