From e0c3bc242b67f84e834d6f1c36eb71be064fa3fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ga=C3=ABtan=20Muller?= <m.gaetan89@gmail.com>
Date: Mon, 2 Sep 2024 07:35:36 +0200
Subject: [PATCH] Use `kotlinx-datetime` instead of Java APIs

---
 data/build.gradle.kts                         |  5 +-
 .../data/ISO8601DateParser.kt                 | 46 -------------------
 .../integrationlayer/data/ImageUrl.kt         |  1 +
 .../data/remote/BroadCastInformation.kt       | 10 ++--
 .../integrationlayer/data/remote/Chapter.kt   | 18 +++-----
 .../integrationlayer/data/remote/Episode.kt   |  8 +---
 .../integrationlayer/data/remote/Media.kt     | 12 ++---
 .../data/remote/MediaAggregations.kt          |  8 +---
 .../integrationlayer/data/remote/Program.kt   | 14 ++----
 .../data/remote/SRGMediaMetadata.kt           | 23 ++++++----
 .../integrationlayer/data/remote/Section.kt   | 10 ++--
 .../integrationlayer/data/remote/Segment.kt   | 23 ++++------
 .../integrationlayer/data/remote/Song.kt      |  8 +---
 .../data/remote/SpriteSheet.kt                |  3 +-
 .../data/serializer/DateSerializer.kt         | 22 ---------
 .../integrationlayer/data/DateParserTest.kt   | 40 ----------------
 .../data/TestAspectRatioSerializer.kt         |  4 +-
 .../data/TestBlockReasonSerializer.kt         |  4 +-
 .../data/TestDateSerializer.kt                | 29 ------------
 .../data/TestImageUrlSerializer.kt            |  4 +-
 gradle/libs.versions.toml                     |  3 ++
 21 files changed, 64 insertions(+), 231 deletions(-)
 delete mode 100644 data/src/main/java/ch/srg/dataProvider/integrationlayer/data/ISO8601DateParser.kt
 delete mode 100644 data/src/main/java/ch/srg/dataProvider/integrationlayer/data/serializer/DateSerializer.kt
 delete mode 100644 data/src/test/java/ch/srg/dataProvider/integrationlayer/data/DateParserTest.kt
 delete mode 100644 data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestDateSerializer.kt

diff --git a/data/build.gradle.kts b/data/build.gradle.kts
index 821209c..8981b5e 100644
--- a/data/build.gradle.kts
+++ b/data/build.gradle.kts
@@ -52,11 +52,10 @@ android {
 }
 
 dependencies {
+    api(libs.kotlinx.datetime)
     api(libs.kotlinx.serialization.json)
 
-    testRuntimeOnly(libs.robolectric)
-    testImplementation(libs.junit)
-    testImplementation(libs.androidx.test.ext.junit)
+    testImplementation(libs.kotlin.test)
 }
 
 publishing {
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/ISO8601DateParser.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/ISO8601DateParser.kt
deleted file mode 100644
index f834d36..0000000
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/ISO8601DateParser.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-package ch.srg.dataProvider.integrationlayer.data
-
-import java.text.ParseException
-import java.text.SimpleDateFormat
-import java.util.Date
-import java.util.Locale
-
-/**
- * DateParser to convert Integration Layer String date to date (ISO_8601)
- *
- * DateParser isn't Thread Safe, each thread must use its own instance.
- * Because SimpleDateFormat isn't Thread Safe @link(https://developer.android.com/reference/java/text/SimpleDateFormat)
- *
- *
- * Copyright (c) SRG SSR. All rights reserved.
- *
- *
- * License information is available from the LICENSE file.
- */
-class ISO8601DateParser {
-    // SimpleDateFormat use Object that aren't thread-safe, what may lead to IndexOutOfBoundException.
-    private val sdf: SimpleDateFormat = SimpleDateFormat(ISO_8601_FORMAT, Locale.US)
-
-    @Throws(ParseException::class)
-    fun parseDate(s: String): Date {
-        val fixed = fix8601ForSimpleDateFormat(s)
-        return sdf.parse(fixed) ?: throw ParseException("Can't parse $s", 0)
-    }
-
-    fun format(date: Date): String {
-        return sdf.format(date)
-    }
-
-    companion object {
-        const val ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
-
-        fun fix8601ForSimpleDateFormat(s: String): String {
-            var output = s
-            if (output.endsWith("Z")) {
-                output = output.substring(0, output.length - 1) + "+00:00"
-            }
-            output = output.replace("\\.\\d+".toRegex(), "")
-            return output
-        }
-    }
-}
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/ImageUrl.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/ImageUrl.kt
index 790d8ce..2cca03e 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/ImageUrl.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/ImageUrl.kt
@@ -16,6 +16,7 @@ import java.io.Serializable
  */
 @Suppress("SerialVersionUIDInSerializableClass")
 @kotlinx.serialization.Serializable(with = ImageUrlSerializer::class)
+// TODO Why is this class 'java.io.Serializable'?
 data class ImageUrl(
     /**
      * Only for internal use! Please use a Decorator!
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/BroadCastInformation.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/BroadCastInformation.kt
index d7fa551..6b3f05a 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/BroadCastInformation.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/BroadCastInformation.kt
@@ -1,11 +1,7 @@
-@file:UseSerializers(DateSerializer::class)
-
 package ch.srg.dataProvider.integrationlayer.data.remote
 
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
+import kotlinx.datetime.Instant
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.UseSerializers
-import java.util.Date
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -17,6 +13,6 @@ import java.util.Date
 data class BroadCastInformation(
     val hintText: String? = null,
     val url: String? = null,
-    val startDate: Date? = null,
-    val endDate: Date? = null
+    val startDate: Instant? = null,
+    val endDate: Instant? = null
 )
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Chapter.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Chapter.kt
index 8b31859..c61b6ed 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Chapter.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Chapter.kt
@@ -1,13 +1,9 @@
-@file:UseSerializers(DateSerializer::class)
-
 package ch.srg.dataProvider.integrationlayer.data.remote
 
 import ch.srg.dataProvider.integrationlayer.data.ImageUrl
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
+import kotlinx.datetime.Instant
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.UseSerializers
-import java.util.Date
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -29,12 +25,12 @@ data class Chapter(
     override val blockReason: BlockReason? = null,
     override val youthProtectionColor: YouthProtectionColor? = null,
     override val type: Type,
-    override val date: Date,
+    override val date: Instant,
     override val duration: Long,
     override val podcastSdUrl: String? = null,
     override val podcastHdUrl: String? = null,
-    override val validFrom: Date? = null,
-    override val validTo: Date? = null,
+    override val validFrom: Instant? = null,
+    override val validTo: Instant? = null,
     override val assignedBy: Referrer? = null,
     override val playableAbroad: Boolean = false,
     override val relatedContentList: List<RelatedContent>? = null,
@@ -53,14 +49,14 @@ data class Chapter(
     val segmentList: List<Segment>? = null,
     val resourceList: List<Resource>? = null,
     val spriteSheet: SpriteSheet? = null,
-    val preTrailerStart: Date? = null,
-    val postTrailerStart: Date? = null,
+    val preTrailerStart: Instant? = null,
+    val postTrailerStart: Instant? = null,
     /**
      *  The reference date corresponding to the beginning of the stream, if any. You can use this date to map a time
      *  position relative to the stream (e.g., a segment mark in or mark out) to a date.
      */
     @SerialName("dvrReferenceDate")
-    val resourceReferenceDate: Date? = null,
+    val resourceReferenceDate: Instant? = null,
     val timeIntervalList: List<TimeInterval>? = null,
     override val imageFocalPoint: FocalPoint? = null
 
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Episode.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Episode.kt
index 9ae0fdd..b12cd88 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Episode.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Episode.kt
@@ -1,12 +1,8 @@
-@file:UseSerializers(DateSerializer::class)
-
 package ch.srg.dataProvider.integrationlayer.data.remote
 
 import ch.srg.dataProvider.integrationlayer.data.ImageUrl
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
+import kotlinx.datetime.Instant
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.UseSerializers
-import java.util.Date
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -22,7 +18,7 @@ data class Episode(
     override val description: String? = null,
     override val imageTitle: String? = null,
     override val imageCopyright: String? = null,
-    val publishedDate: Date? = null,
+    val publishedDate: Instant? = null,
     val fullLengthUrn: String? = null,
     val seasonNumber: Int? = null,
     val number: Int? = null,
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Media.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Media.kt
index a54525c..55e483a 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Media.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Media.kt
@@ -1,13 +1,9 @@
-@file:UseSerializers(DateSerializer::class)
-
 package ch.srg.dataProvider.integrationlayer.data.remote
 
 import ch.srg.dataProvider.integrationlayer.data.ImageUrl
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
+import kotlinx.datetime.Instant
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.UseSerializers
-import java.util.Date
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -22,7 +18,7 @@ data class Media(
     override val urn: String,
     override val title: String,
     override val type: Type,
-    override val date: Date,
+    override val date: Instant,
     override val duration: Long,
     override val imageUrl: ImageUrl,
     override val imageFocalPoint: FocalPoint? = null,
@@ -34,8 +30,8 @@ data class Media(
     override val youthProtectionColor: YouthProtectionColor? = null,
     override val podcastSdUrl: String? = null,
     override val podcastHdUrl: String? = null,
-    override val validFrom: Date? = null,
-    override val validTo: Date? = null,
+    override val validFrom: Instant? = null,
+    override val validTo: Instant? = null,
     override val assignedBy: Referrer? = null,
     override val playableAbroad: Boolean,
     override val relatedContentList: List<RelatedContent>? = null,
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/MediaAggregations.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/MediaAggregations.kt
index fc3f930..897608f 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/MediaAggregations.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/MediaAggregations.kt
@@ -1,11 +1,7 @@
-@file:UseSerializers(DateSerializer::class)
-
 package ch.srg.dataProvider.integrationlayer.data.remote
 
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
+import kotlinx.datetime.Instant
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.UseSerializers
-import java.util.Date
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -42,7 +38,7 @@ data class ShowBucket(val urn: String, val title: String, override val count: In
 data class DurationInMinutesBucket(val duration: Long, override val count: Int) : Bucket
 
 @Serializable
-data class DateBucket(val date: Date, override val count: Int) : Bucket
+data class DateBucket(val date: Instant, override val count: Int) : Bucket
 
 @Serializable
 data class MediaAggregations(
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Program.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Program.kt
index 19c881d..11166ca 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Program.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Program.kt
@@ -1,13 +1,9 @@
-@file:UseSerializers(DateSerializer::class)
-
 package ch.srg.dataProvider.integrationlayer.data.remote
 
 import ch.srg.dataProvider.integrationlayer.data.ImageUrl
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
+import kotlinx.datetime.Instant
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.UseSerializers
-import java.util.Date
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -17,8 +13,8 @@ import java.util.Date
 @Serializable
 data class Program(
     override val title: String,
-    val startTime: Date,
-    val endTime: Date,
+    val startTime: Instant,
+    val endTime: Instant,
     override val lead: String? = null,
     override val description: String? = null,
     val imageUrl: ImageUrl? = null,
@@ -56,8 +52,8 @@ data class Program(
     val channelUrn: String? = null
 ) : SRGMetadata {
 
-    fun isDateInProgramTime(date: Date): Boolean {
-        return date.after(startTime) && date.before(endTime)
+    fun isDateInProgramTime(instant: Instant): Boolean {
+        return instant > startTime && instant < endTime
     }
 }
 
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/SRGMediaMetadata.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/SRGMediaMetadata.kt
index 8a8d523..2795a9c 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/SRGMediaMetadata.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/SRGMediaMetadata.kt
@@ -1,6 +1,7 @@
 package ch.srg.dataProvider.integrationlayer.data.remote
 
-import java.util.Date
+import kotlinx.datetime.Clock
+import kotlinx.datetime.Instant
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -10,11 +11,11 @@ import java.util.Date
 interface SRGMediaMetadata : SRGIdentifierMetadata, SRGImageMetadata, SRGMetadata {
     val mediaType: MediaType
     val type: Type
-    val date: Date
+    val date: Instant
     val duration: Long
     val blockReason: BlockReason?
-    val validFrom: Date?
-    val validTo: Date?
+    val validFrom: Instant?
+    val validTo: Instant?
     val assignedBy: Referrer?
     val playableAbroad: Boolean
     val youthProtectionColor: YouthProtectionColor?
@@ -34,26 +35,28 @@ interface SRGMediaMetadata : SRGIdentifierMetadata, SRGImageMetadata, SRGMetadat
     /**
      * isBlocked if it has a blockReason or blocked by TimeAvailability at a given time
      */
-    fun isBlocked(at: Date = Date()): Boolean {
+    fun isBlocked(at: Instant = Clock.System.now()): Boolean {
         return blockReason != null || getTimeAvailability(at) != TimeAvailability.AVAILABLE
     }
 
-    fun getTimeAvailability(at: Date = Date()): TimeAvailability {
+    fun getTimeAvailability(at: Instant = Clock.System.now()): TimeAvailability {
+        val validTo = validTo
+        val validFrom = validFrom
         return when {
             blockReason == BlockReason.STARTDATE -> TimeAvailability.NOT_YET_AVAILABLE
             blockReason == BlockReason.ENDDATE -> TimeAvailability.NOT_AVAILABLE_ANYMORE
-            validTo != null && at.after(validTo) -> TimeAvailability.NOT_AVAILABLE_ANYMORE
-            validFrom != null && at.before(validFrom) -> TimeAvailability.NOT_YET_AVAILABLE
+            validTo != null && at > validTo -> TimeAvailability.NOT_AVAILABLE_ANYMORE
+            validFrom != null && at < validFrom -> TimeAvailability.NOT_YET_AVAILABLE
             else -> TimeAvailability.AVAILABLE
         }
     }
 
     fun isBlockedValidFromTime(currentTime: Long = System.currentTimeMillis()): Boolean {
-        return validFrom != null && validFrom!!.time > currentTime
+        return validFrom != null && validFrom!!.toEpochMilliseconds() > currentTime
     }
 
     fun isBlockValidToTime(currentTime: Long = System.currentTimeMillis()): Boolean {
-        return validTo != null && currentTime > validTo!!.time
+        return validTo != null && currentTime > validTo!!.toEpochMilliseconds()
     }
 
     fun isTimeBlocked(currentTime: Long): Boolean {
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Section.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Section.kt
index b974f06..268a7cc 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Section.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Section.kt
@@ -1,12 +1,8 @@
-@file:UseSerializers(DateSerializer::class)
-
 package ch.srg.dataProvider.integrationlayer.data.remote
 
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
+import kotlinx.datetime.Instant
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.UseSerializers
-import java.util.Date
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -22,8 +18,8 @@ data class Section(
     val representation: Representation,
     val isPublished: Boolean,
     @SerialName("start")
-    val startDate: Date? = null,
+    val startDate: Instant? = null,
     @SerialName("end")
-    val endDate: Date? = null,
+    val endDate: Instant? = null,
     val hasPersonalizedContent: Boolean? = null,
 ) : ILObject
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Segment.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Segment.kt
index b262d51..24584a1 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Segment.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Segment.kt
@@ -1,13 +1,10 @@
-@file:UseSerializers(DateSerializer::class)
-
 package ch.srg.dataProvider.integrationlayer.data.remote
 
 import ch.srg.dataProvider.integrationlayer.data.ImageUrl
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
+import kotlinx.datetime.Instant
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.UseSerializers
-import java.util.Date
+import kotlin.time.Duration.Companion.milliseconds
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -30,7 +27,7 @@ data class Segment @JvmOverloads constructor(
      */
     val markOut: Long,
     override val type: Type,
-    override val date: Date,
+    override val date: Instant,
     override val duration: Long,
     override val displayable: Boolean,
     override val playableAbroad: Boolean,
@@ -44,8 +41,8 @@ data class Segment @JvmOverloads constructor(
     override val youthProtectionColor: YouthProtectionColor? = null,
     override val podcastSdUrl: String? = null,
     override val podcastHdUrl: String? = null,
-    override val validFrom: Date? = null,
-    override val validTo: Date? = null,
+    override val validFrom: Instant? = null,
+    override val validTo: Instant? = null,
     override val assignedBy: Referrer? = null,
     override val relatedContentList: List<RelatedContent>? = null,
     override val socialCountList: List<SocialCountEntry>? = null,
@@ -65,23 +62,23 @@ data class Segment @JvmOverloads constructor(
      *  The date corresponding to the mark in time, `null` if no such relationship exists.
      *  @see resourceReferenceDate
      */
-    var markInDate: Date? = null
+    var markInDate: Instant? = null
 
     /**
      * The date corresponding to the mark out time, `null` if no such relationship exists.
      *  @see resourceReferenceDate
      */
-    var markOutDate: Date? = null
+    var markOutDate: Instant? = null
 
     /**
      * Reference date to compute mark in and mark out date, `null` if no such relationship exists.
      */
-    var resourceReferenceDate: Date? = null
+    var resourceReferenceDate: Instant? = null
         set(value) {
             field = value
             if (value != null) {
-                markInDate = Date(value.time + markIn)
-                markOutDate = Date(value.time + markOut)
+                markInDate = value + markIn.milliseconds
+                markOutDate = value + markOut.milliseconds
             } else {
                 markInDate = null
                 markOutDate = null
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Song.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Song.kt
index 4dd547f..d3a19c2 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Song.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/Song.kt
@@ -1,12 +1,8 @@
-@file:UseSerializers(DateSerializer::class)
-
 package ch.srg.dataProvider.integrationlayer.data.remote
 
 import ch.srg.dataProvider.integrationlayer.data.ImageUrl
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
+import kotlinx.datetime.Instant
 import kotlinx.serialization.Serializable
-import kotlinx.serialization.UseSerializers
-import java.util.Date
 
 /**
  * Copyright (c) SRG SSR. All rights reserved.
@@ -16,7 +12,7 @@ import java.util.Date
 @Serializable
 data class Song(
     val isPlayingNow: Boolean,
-    val date: Date,
+    val date: Instant,
     val title: String,
     val artist: Artist,
     val duration: Int? = null,
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/SpriteSheet.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/SpriteSheet.kt
index 0abe659..b655f4e 100644
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/SpriteSheet.kt
+++ b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/remote/SpriteSheet.kt
@@ -1,6 +1,5 @@
 package ch.srg.dataProvider.integrationlayer.data.remote
 
-import android.util.Rational
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.Transient
 
@@ -23,5 +22,5 @@ data class SpriteSheet(
     val count: Int = rows * columns
 
     @Transient
-    val aspectRatio = Rational(thumbnailWidth, thumbnailHeight)
+    val aspectRatio = AspectRatio(thumbnailWidth, thumbnailHeight)
 }
diff --git a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/serializer/DateSerializer.kt b/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/serializer/DateSerializer.kt
deleted file mode 100644
index e3efcb4..0000000
--- a/data/src/main/java/ch/srg/dataProvider/integrationlayer/data/serializer/DateSerializer.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ch.srg.dataProvider.integrationlayer.data.serializer
-
-import ch.srg.dataProvider.integrationlayer.data.ISO8601DateParser
-import kotlinx.serialization.KSerializer
-import kotlinx.serialization.descriptors.PrimitiveKind
-import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
-import java.util.Date
-
-class DateSerializer : KSerializer<Date> {
-    override val descriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING)
-
-    override fun deserialize(decoder: Decoder): Date {
-        val string = decoder.decodeString()
-        return ISO8601DateParser().parseDate(string)
-    }
-
-    override fun serialize(encoder: Encoder, value: Date) {
-        encoder.encodeString(ISO8601DateParser().format(value))
-    }
-}
diff --git a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/DateParserTest.kt b/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/DateParserTest.kt
deleted file mode 100644
index 8d1f949..0000000
--- a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/DateParserTest.kt
+++ /dev/null
@@ -1,40 +0,0 @@
-package ch.srg.dataProvider.integrationlayer.data
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import org.junit.Assert
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import java.text.ParseException
-import java.util.Date
-
-/**
- * [Testing Fundamentals](http://d.android.com/tools/testing/testing_android.html)
- */
-@RunWith(AndroidJUnit4::class)
-class DateParserTest {
-
-    @Ignore("robolectric date parsing doesn't use same format")
-    @Test
-    fun test8601() {
-        val parser = ISO8601DateParser()
-        Assert.assertEquals(Date(1496126175000L), parser.parseDate("2017-05-30T08:36:15.079+02:00"))
-        Assert.assertEquals(Date(1496126175000L), parser.parseDate("2017-05-30T08:36:15+02:00"))
-        Assert.assertEquals(Date(1496126175000L), parser.parseDate("2017-05-30T06:36:15Z"))
-    }
-
-    @Test(expected = ParseException::class)
-    fun testEmptyString() {
-        ISO8601DateParser().parseDate("")
-    }
-
-    @Test(expected = ParseException::class)
-    fun testInvalidString() {
-        ISO8601DateParser().parseDate("Not_Date")
-    }
-
-    @Test(expected = ParseException::class)
-    fun testInvalidDateString() {
-        ISO8601DateParser().parseDate("2017-05-30AB08:36:15.079+02:00")
-    }
-}
diff --git a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestAspectRatioSerializer.kt b/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestAspectRatioSerializer.kt
index 41f5f09..274df94 100644
--- a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestAspectRatioSerializer.kt
+++ b/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestAspectRatioSerializer.kt
@@ -2,8 +2,8 @@ package ch.srg.dataProvider.integrationlayer.data
 
 import ch.srg.dataProvider.integrationlayer.data.remote.AspectRatio
 import kotlinx.serialization.encodeToString
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import kotlin.test.Test
+import kotlin.test.assertEquals
 
 class TestAspectRatioSerializer {
 
diff --git a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestBlockReasonSerializer.kt b/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestBlockReasonSerializer.kt
index 22d5ac8..95564ed 100644
--- a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestBlockReasonSerializer.kt
+++ b/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestBlockReasonSerializer.kt
@@ -2,8 +2,8 @@ package ch.srg.dataProvider.integrationlayer.data
 
 import ch.srg.dataProvider.integrationlayer.data.remote.BlockReason
 import kotlinx.serialization.encodeToString
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import kotlin.test.Test
+import kotlin.test.assertEquals
 
 class TestBlockReasonSerializer {
 
diff --git a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestDateSerializer.kt b/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestDateSerializer.kt
deleted file mode 100644
index a7b4391..0000000
--- a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestDateSerializer.kt
+++ /dev/null
@@ -1,29 +0,0 @@
-package ch.srg.dataProvider.integrationlayer.data
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import ch.srg.dataProvider.integrationlayer.data.serializer.DateSerializer
-import org.junit.Assert
-import org.junit.Ignore
-import org.junit.Test
-import org.junit.runner.RunWith
-import java.util.Date
-
-@RunWith(AndroidJUnit4::class)
-class TestDateSerializer {
-
-    @Ignore("robolectric date parsing doesn't use same format")
-    @Test
-    fun testToJSon() {
-        val expectedJson = "\"2017-05-30T08:36:15+02:00\""
-        val input = Date(1496126175000L)
-        Assert.assertEquals(expectedJson, DataProviderJson.encodeToString(serializer = DateSerializer(), input))
-    }
-
-    @Ignore("robolectric date parsing doesn't use same format")
-    @Test
-    fun testFromJson() {
-        val input = "\"2017-05-30T08:36:15+02:00\""
-        val expectedDate = Date(1496126175000L)
-        Assert.assertEquals(expectedDate, DataProviderJson.decodeFromString(deserializer = DateSerializer(), input))
-    }
-}
diff --git a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestImageUrlSerializer.kt b/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestImageUrlSerializer.kt
index 9d42b54..b552930 100644
--- a/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestImageUrlSerializer.kt
+++ b/data/src/test/java/ch/srg/dataProvider/integrationlayer/data/TestImageUrlSerializer.kt
@@ -1,8 +1,8 @@
 package ch.srg.dataProvider.integrationlayer.data
 
 import kotlinx.serialization.encodeToString
-import org.junit.Assert.assertEquals
-import org.junit.Test
+import kotlin.test.Test
+import kotlin.test.assertEquals
 
 class TestImageUrlSerializer {
 
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 22fc96f..bf4e9fa 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -8,6 +8,7 @@ androidx-test-ext-junit = "1.2.1"
 detekt = "1.23.6"
 junit = "4.13.2"
 kotlin = "2.0.20"
+kotlinx-datetime = "0.6.1"
 kotlinx-serialization = "1.7.2"
 okhttp = "4.12.0"
 retrofit = "2.11.0"
@@ -21,6 +22,8 @@ androidx-paging-common = { module = "androidx.paging:paging-common", version.ref
 androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-test-ext-junit" }
 detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
 junit = { module = "junit:junit", version.ref = "junit" }
+kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
+kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx-datetime" }
 kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
 okhttp-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
 retrofit = { module = "com.squareup.retrofit2:retrofit" }