Skip to content

Commit

Permalink
Use formatters by kotlinx.datetime instead of ones from java time
Browse files Browse the repository at this point in the history
  • Loading branch information
kunyavskiy committed May 20, 2024
1 parent c65481f commit 07fd02b
Show file tree
Hide file tree
Showing 15 changed files with 306 additions and 262 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
ktor = "2.3.10" # https://ktor.io/
datetime = "0.5.0" # https://github.com/Kotlin/kotlinx-datetime
datetime = "0.6.0" # https://github.com/Kotlin/kotlinx-datetime
serialization = "1.6.3" # https://github.com/Kotlin/kotlinx.serialization
kotlin = "2.0.0-RC1"
ksp = "2.0.0-RC1-1.0.20" # https://github.com/google/ksp
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,36 @@
package org.icpclive.cds.plugins.cats

import kotlinx.datetime.*
import kotlinx.datetime.format.*
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import org.icpclive.cds.*
import org.icpclive.cds.api.*
import org.icpclive.ksp.cds.Builder
import org.icpclive.cds.ktor.*
import org.icpclive.cds.settings.*
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import org.icpclive.cds.util.serializers.*
import kotlin.time.Duration.Companion.seconds

private object ContestTimeSerializer : KSerializer<LocalDateTime> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("InstantConstand", PrimitiveKind.STRING)
private val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")

override fun serialize(encoder: Encoder, value: LocalDateTime) {
encoder.encodeString(formatter.format(value.toJavaLocalDateTime()))
}

override fun deserialize(decoder: Decoder): LocalDateTime {
return java.time.LocalDateTime.parse(decoder.decodeString(), formatter).toKotlinLocalDateTime()
}
}

private object SubmissionTimeSerializer : KSerializer<Instant> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("InstantConstand", PrimitiveKind.STRING)
private val formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssZ")

override fun serialize(encoder: Encoder, value: Instant) {
encoder.encodeString(formatter.format(value.toJavaInstant()))
}

override fun deserialize(decoder: Decoder): Instant {
return ZonedDateTime.parse(decoder.decodeString(), formatter).toInstant().toKotlinInstant()
}
}
private object ContestTimeSerializer : FormatterLocalDateSerializer(LocalDateTime.Format {
dayOfMonth()
char('.')
monthNumber()
char('.')
year()
char(' ')
hour()
char(':')
minute()
})

private object SubmissionTimeSerializer : FormatterInstantSerializer(DateTimeComponents.Format {
date(LocalDate.Formats.ISO_BASIC)
char('T')
hour()
minute()
second()
offset(UtcOffset.Formats.ISO_BASIC)
})

@Builder("cats")
public sealed interface CatsSettings : CDSSettings {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.icpclive.cds.plugins.ejudge

import kotlinx.datetime.*
import kotlinx.datetime.format.alternativeParsing
import kotlinx.datetime.format.char
import org.icpclive.cds.*
import org.icpclive.cds.api.*
import org.icpclive.ksp.cds.Builder
Expand All @@ -10,7 +12,6 @@ import org.icpclive.cds.settings.UrlOrLocalPath
import org.icpclive.cds.util.child
import org.icpclive.cds.util.children
import org.w3c.dom.Element
import java.time.format.DateTimeFormatter
import kotlin.time.Duration
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.nanoseconds
Expand Down Expand Up @@ -71,14 +72,21 @@ internal class EjudgeDataSource(val settings: EjudgeSettings) : FullReloadContes
)
}.toList()

private val timePattern: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
private val timePattern = LocalDateTime.Format {
year()
alternativeParsing({ char('/') }) { char('-') }
monthNumber()
alternativeParsing({ char('/') }) { char('-') }
dayOfMonth()
char(' ')
time(LocalTime.Formats.ISO)
}

private fun parseEjudgeTime(time: String): Instant {
return java.time.LocalDateTime.parse(
time.replace("/", "-"), // snark's ejudge uses '/' instead of '-'
return LocalDateTime.parse(
time,
timePattern
).toKotlinLocalDateTime()
.toInstant(settings.timeZone)
).toInstant(settings.timeZone)
}

private fun parseContestInfo(element: Element): ContestParseResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package org.icpclive.cds.plugins.eolymp
import com.eolymp.graphql.*
import com.expediagroup.graphql.client.ktor.GraphQLKtorClient
import com.expediagroup.graphql.client.types.GraphQLClientRequest
import kotlinx.datetime.toKotlinInstant
import kotlinx.datetime.Instant
import kotlinx.datetime.format.DateTimeComponents
import org.icpclive.cds.*
import org.icpclive.cds.api.*
import org.icpclive.ksp.cds.Builder
Expand All @@ -12,10 +13,6 @@ import org.icpclive.cds.settings.CDSSettings
import org.icpclive.cds.settings.Credential
import org.icpclive.cds.util.getLogger
import java.net.URL
import java.time.chrono.IsoChronology
import java.time.format.DateTimeFormatterBuilder
import java.time.format.ResolverStyle
import java.time.temporal.ChronoField
import kotlin.time.Duration.Companion.ZERO
import kotlin.time.Duration.Companion.seconds

Expand Down Expand Up @@ -91,27 +88,6 @@ internal class EOlympDataSource(val settings: EOlympSettings) : FullReloadContes
else -> error("Unknown contest format: $format")
}

private val dateTimeFormatter = DateTimeFormatterBuilder()
.parseCaseInsensitive()
.appendValue(ChronoField.YEAR, 4)
.appendLiteral('-')
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
.appendLiteral('-')
.appendValue(ChronoField.DAY_OF_MONTH, 2)
.appendLiteral('T')
.appendValue(ChronoField.HOUR_OF_DAY, 2)
.appendLiteral(':')
.appendValue(ChronoField.MINUTE_OF_HOUR, 2)
.appendLiteral(':')
.appendValue(ChronoField.SECOND_OF_MINUTE, 2)
.optionalStart()
.appendFraction(ChronoField.NANO_OF_SECOND, 2, 9, true)
.optionalEnd()
.appendOffset("+HH:MM", "Z")
.toFormatter()
.withResolverStyle(ResolverStyle.STRICT)
.withChronology(IsoChronology.INSTANCE)

private var previousDays: List<ContestParseResult> = emptyList()

@OptIn(InefficientContestInfoApi::class)
Expand Down Expand Up @@ -271,7 +247,7 @@ internal class EOlympDataSource(val settings: EOlympSettings) : FullReloadContes
}
}

private fun parseTime(s: String) = java.time.Instant.from(dateTimeFormatter.parse(s)).toKotlinInstant()
private fun parseTime(s: String) = Instant.parse(s, DateTimeComponents.Formats.ISO_DATE_TIME_OFFSET)

companion object {
val log by getLogger()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.datetime.*
import kotlinx.datetime.format.char
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
import org.icpclive.cds.*
Expand All @@ -17,7 +18,6 @@ import org.icpclive.ksp.cds.Builder
import org.icpclive.cds.settings.CDSSettings
import org.icpclive.cds.settings.Credential
import org.icpclive.cds.ktor.*
import java.time.format.DateTimeFormatter
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds

Expand Down Expand Up @@ -198,13 +198,14 @@ internal class NSUDataSource(val settings: NSUSettings) : FullReloadContestDataS
return verdict?.toICPCRunResult()
}

private val timePattern = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
private val timePattern = LocalDateTime.Format {
date(LocalDate.Formats.ISO)
char(' ')
time(LocalTime.Formats.ISO)
}

private fun parseNSUTime(time: String): Instant {
return java.time.LocalDateTime.parse(
time, timePattern
).toKotlinLocalDateTime()
.toInstant(settings.timeZone)
return timePattern.parse(time).toInstant(settings.timeZone)
}

@Serializable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package org.icpclive.cds.plugins.testsys

import kotlinx.datetime.*
import kotlinx.datetime.format.*
import org.icpclive.cds.*
import org.icpclive.cds.api.*
import org.icpclive.ksp.cds.Builder
import org.icpclive.cds.ktor.*
import org.icpclive.cds.settings.CDSSettings
import org.icpclive.cds.settings.UrlOrLocalPath
import java.nio.charset.Charset
import java.time.format.DateTimeFormatter
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds

Expand Down Expand Up @@ -122,10 +122,19 @@ internal class TestSysDataSource(val settings: TestSysSettings) : FullReloadCont
add(builder.toString())
}

private fun String.toDate() =
java.time.LocalDateTime.parse(this, DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"))
.toKotlinLocalDateTime()
.toInstant(settings.timeZone)
private val dateFormat = LocalDateTime.Format {
dayOfMonth()
char('.')
monthNumber()
char('.')
year()
char(' ')
time(LocalTime.Formats.ISO)
}

private fun String.toDate(): Instant {
return dateFormat.parse(this).toInstant(settings.timeZone)
}

private fun String.toStatus() = when (this) {
"RESULTS" -> ContestStatus.OVER
Expand Down
18 changes: 18 additions & 0 deletions src/cds/utils/api/utils.api
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ public final class org/icpclive/cds/util/serializers/DurationInSecondsSerializer
public fun serialize-HG0u8IE (Lkotlinx/serialization/encoding/Encoder;J)V
}

public abstract class org/icpclive/cds/util/serializers/FormatterInstantSerializer : kotlinx/serialization/KSerializer {
public fun <init> (Lkotlinx/datetime/format/DateTimeFormat;)V
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/Instant;
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/Instant;)V
}

public abstract class org/icpclive/cds/util/serializers/FormatterLocalDateSerializer : kotlinx/serialization/KSerializer {
public fun <init> (Lkotlinx/datetime/format/DateTimeFormat;)V
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlinx/datetime/LocalDateTime;
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlinx/datetime/LocalDateTime;)V
}

public final class org/icpclive/cds/util/serializers/HumanTimeSerializer : kotlinx/serialization/KSerializer {
public static final field INSTANCE Lorg/icpclive/cds/util/serializers/HumanTimeSerializer;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.icpclive.cds.util.serializers

import kotlinx.datetime.*
import kotlinx.datetime.format.DateTimeComponents
import kotlinx.datetime.format.DateTimeFormat
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

public abstract class FormatterInstantSerializer(private val formatter: DateTimeFormat<DateTimeComponents>) : KSerializer<Instant> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Instant", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Instant) {
encoder.encodeString(value.format(formatter, TimeZone.currentSystemDefault().offsetAt(value)))
}
override fun deserialize(decoder: Decoder): Instant {
return Instant.parse(decoder.decodeString(), formatter)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.icpclive.cds.util.serializers

import kotlinx.datetime.LocalDateTime
import kotlinx.datetime.format.DateTimeFormat
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder

public abstract class FormatterLocalDateSerializer(private val formatter: DateTimeFormat<LocalDateTime>) : KSerializer<LocalDateTime> {
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: LocalDateTime) {
encoder.encodeString(formatter.format(value))
}
override fun deserialize(decoder: Decoder): LocalDateTime {
return formatter.parse(decoder.decodeString())
}
}
Loading

0 comments on commit 07fd02b

Please sign in to comment.