mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
parse Subsonic dates with java.time via core library desugaring
This commit is contained in:
@@ -32,6 +32,7 @@ android {
|
|||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
lint {
|
lint {
|
||||||
@@ -128,6 +129,8 @@ dependencies {
|
|||||||
implementation(libs.koin.androidx.compose)
|
implementation(libs.koin.androidx.compose)
|
||||||
ksp(libs.androidx.room3.compiler)
|
ksp(libs.androidx.room3.compiler)
|
||||||
|
|
||||||
|
coreLibraryDesugaring(libs.desugar.jdk.libs)
|
||||||
|
|
||||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import com.subtracks.data.model.PlaylistSong
|
|||||||
import com.subtracks.data.model.Song
|
import com.subtracks.data.model.Song
|
||||||
import org.w3c.dom.Document
|
import org.w3c.dom.Document
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
import java.time.format.DateTimeParseException
|
||||||
|
|
||||||
object SubsonicXml {
|
object SubsonicXml {
|
||||||
fun artists(
|
fun artists(
|
||||||
@@ -128,46 +132,19 @@ private fun Element.longAttr(name: String): Long? = getAttribute(name).takeIf {
|
|||||||
private fun Element.dateAttr(name: String): Long? = getAttribute(name).takeIf { it.isNotEmpty() }?.let(IsoDate::parse)
|
private fun Element.dateAttr(name: String): Long? = getAttribute(name).takeIf { it.isNotEmpty() }?.let(IsoDate::parse)
|
||||||
|
|
||||||
internal object IsoDate {
|
internal object IsoDate {
|
||||||
private val pattern =
|
private val timezone = Regex("(Z|[+-]\\d{2}:?\\d{2})$", RegexOption.IGNORE_CASE)
|
||||||
Regex(
|
|
||||||
"^(\\d{4})-(\\d{2})-(\\d{2})(?:[T ](\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d+)?\\s*(Z|[+-]\\d{2}:?\\d{2})?)?$",
|
|
||||||
)
|
|
||||||
|
|
||||||
fun parse(value: String): Long? {
|
fun parse(value: String): Long? {
|
||||||
val match = pattern.matchEntire(value.trim()) ?: return null
|
val text = value.trim()
|
||||||
val year = match.groupValues[1].toIntOrNull() ?: return null
|
return try {
|
||||||
val month = match.groupValues[2].toIntOrNull() ?: return null
|
val zoned = if (timezone.containsMatchIn(text)) text else text + "Z"
|
||||||
val day = match.groupValues[3].toIntOrNull() ?: return null
|
OffsetDateTime.parse(zoned).toEpochSecond()
|
||||||
val hour = match.groupValues[4].toIntOrNull() ?: 0
|
} catch (_: DateTimeParseException) {
|
||||||
val minute = match.groupValues[5].toIntOrNull() ?: 0
|
try {
|
||||||
val second = match.groupValues[6].toIntOrNull() ?: 0
|
LocalDate.parse(text).atStartOfDay(ZoneOffset.UTC).toEpochSecond()
|
||||||
if (month !in 1..12 || day !in 1..31 || hour !in 0..23 || minute !in 0..59 || second !in 0..59) {
|
} catch (_: DateTimeParseException) {
|
||||||
return null
|
null
|
||||||
}
|
}
|
||||||
val days = daysFromCivil(year, month, day)
|
|
||||||
return days * 86400L + hour * 3600L + minute * 60L + second - offsetSeconds(match.groupValues[7])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun offsetSeconds(zone: String): Long {
|
|
||||||
if (zone.isEmpty() || zone.equals("Z", ignoreCase = true)) return 0
|
|
||||||
val digits = zone.drop(1).replace(":", "")
|
|
||||||
if (digits.length != 4) return 0
|
|
||||||
val hours = digits.take(2).toLongOrNull() ?: return 0
|
|
||||||
val minutes = digits.drop(2).toLongOrNull() ?: return 0
|
|
||||||
val sign = if (zone[0] == '-') -1L else 1L
|
|
||||||
return sign * (hours * 3600 + minutes * 60)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun daysFromCivil(
|
|
||||||
year: Int,
|
|
||||||
month: Int,
|
|
||||||
day: Int,
|
|
||||||
): Long {
|
|
||||||
val y = if (month <= 2) year - 1 else year
|
|
||||||
val era = (if (y >= 0) y else y - 399) / 400
|
|
||||||
val yearOfEra = y - era * 400
|
|
||||||
val dayOfYear = (153 * (if (month > 2) month - 3 else month + 9) + 2) / 5 + day - 1
|
|
||||||
val dayOfEra = yearOfEra * 365 + yearOfEra / 4 - yearOfEra / 100 + dayOfYear
|
|
||||||
return era * 146097L + dayOfEra - 719468L
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ coil = "3.6.3"
|
|||||||
koin = "4.2.2"
|
koin = "4.2.2"
|
||||||
media3 = "1.11.1"
|
media3 = "1.11.1"
|
||||||
okhttp = "5.5.0"
|
okhttp = "5.5.0"
|
||||||
|
desugarJdkLibs = "2.1.5"
|
||||||
ktlint = "1.8.0"
|
ktlint = "1.8.0"
|
||||||
ktlintGradle = "14.2.0"
|
ktlintGradle = "14.2.0"
|
||||||
roborazzi = "1.74.0"
|
roborazzi = "1.74.0"
|
||||||
@@ -63,6 +64,7 @@ koin-androidx-compose = { group = "io.insert-koin", name = "koin-androidx-compos
|
|||||||
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
|
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
|
||||||
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
|
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
|
||||||
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
|
okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" }
|
||||||
|
desugar-jdk-libs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "desugarJdkLibs" }
|
||||||
okhttp-mockwebserver = { group = "com.squareup.okhttp3", name = "mockwebserver", version.ref = "okhttp" }
|
okhttp-mockwebserver = { group = "com.squareup.okhttp3", name = "mockwebserver", version.ref = "okhttp" }
|
||||||
roborazzi = { group = "io.github.takahirom.roborazzi", name = "roborazzi", version.ref = "roborazzi" }
|
roborazzi = { group = "io.github.takahirom.roborazzi", name = "roborazzi", version.ref = "roborazzi" }
|
||||||
roborazzi-compose = { group = "io.github.takahirom.roborazzi", name = "roborazzi-compose", version.ref = "roborazzi" }
|
roborazzi-compose = { group = "io.github.takahirom.roborazzi", name = "roborazzi-compose", version.ref = "roborazzi" }
|
||||||
|
|||||||
Reference in New Issue
Block a user