Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions buildSrc/src/main/kotlin/zoneInfosResourcesGenerator.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import org.gradle.api.file.Directory
import java.io.File

/*
Expand Down Expand Up @@ -51,7 +52,7 @@ private fun loadTzBinaries(
}
}

fun generateZoneInfosResources(zoneInfoDir: File, outputDir: File, version: String) {
fun generateZoneInfosResources(zoneInfoDir: File, outputDir: Directory, version: String) {
val header = buildString {
appendLine()
append("/* AUTOGENERATED FROM ZONE INFO DATABASE v.$version */")
Expand All @@ -70,7 +71,7 @@ fun generateZoneInfosResources(zoneInfoDir: File, outputDir: File, version: Stri
loadedZones.forEachIndexed { id, tzData ->
val tzDataName = "tzData$id"
val data = generateByteArrayProperty(tzData, header, tzDataName)
File(outputDir, "$tzDataName.kt").writeText(data)
outputDir.file("$tzDataName.kt").asFile.writeText(data)
tzData.fullTzNames.forEach { name ->
zoneDataByNameBody.appendLine(" \"$name\" -> $tzDataName")
getTimeZonesBody.appendLine(" \"$name\",")
Expand All @@ -97,5 +98,5 @@ fun generateZoneInfosResources(zoneInfoDir: File, outputDir: File, version: Stri
append("}")
}

File(outputDir, "tzData.kt").writeText(content)
outputDir.file("tzData.kt").asFile.writeText(content)
}
22 changes: 10 additions & 12 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import kotlinx.team.infra.mavenPublicationsPom
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.net.URL
import javax.xml.parsers.DocumentBuilderFactory
import java.io.ByteArrayOutputStream
import java.io.PrintWriter
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JsModuleKind
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.konan.target.Family
import java.io.ByteArrayOutputStream
import java.io.PrintWriter
import java.net.URL
import javax.xml.parsers.DocumentBuilderFactory

plugins {
kotlin("multiplatform")
Expand All @@ -22,10 +22,6 @@ mavenPublicationsPom {
description.set("Kotlin Datetime Library")
}

base {
archivesBaseName = "kotlinx-datetime" // doesn't work
}

val mainJavaToolchainVersion: String by project
val modularJavaToolchainVersion: String by project
val serializationVersion: String by project
Expand Down Expand Up @@ -112,6 +108,7 @@ kotlin {
}
}

@OptIn(ExperimentalWasmDsl::class)
wasmJs {
nodejs {
testTask {
Expand All @@ -122,6 +119,7 @@ kotlin {
}
}

@OptIn(ExperimentalWasmDsl::class)
wasmWasi {
nodejs()
}
Expand Down Expand Up @@ -295,7 +293,7 @@ tasks {
// Workaround for https://youtrack.jetbrains.com/issue/KT-58303:
// the `clean` task can't delete the expanded.lock file on Windows as it's still held by Gradle, failing the build
val clean by existing(Delete::class) {
setDelete(fileTree(buildDir) {
setDelete(fileTree(layout.buildDirectory) {
exclude("tmp/.cache/expanded/expanded.lock")
})
}
Expand All @@ -322,7 +320,7 @@ val downloadWindowsZonesMapping by tasks.registering {
val output = "$projectDir/windows/src/internal/WindowsZoneNames.kt"
outputs.file(output)
doLast {
val initialFileContents = try { File(output).readBytes() } catch(e: Throwable) { ByteArray(0) }
val initialFileContents = try { File(output).readBytes() } catch(_: Throwable) { ByteArray(0) }
val documentBuilderFactory = DocumentBuilderFactory.newInstance()
// otherwise, parsing fails since it can't find the dtd
documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false)
Expand Down
8 changes: 3 additions & 5 deletions core/common/src/format/DateTimeComponents.kt
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ public class DateTimeComponents internal constructor(internal val contents: Date
truncatedDate.year = requireParsedField(truncatedDate.year, "year") % 10_000
val totalSeconds = try {
val secDelta = safeMultiply((year!! / 10_000).toLong(), SECONDS_PER_10000_YEARS)
val epochDays = truncatedDate.toLocalDate().toEpochDays().toLong()
val epochDays = truncatedDate.toLocalDate().toEpochDays()
safeAdd(secDelta, epochDays * SECONDS_PER_DAY + time.toSecondOfDay() - offset.totalSeconds)
} catch (e: ArithmeticException) {
throw DateTimeFormatException("The parsed date is outside the range representable by Instant", e)
Expand Down Expand Up @@ -654,10 +654,8 @@ internal class DateTimeComponentsFormat(override val actualFormat: CachedFormatS
override fun timeZoneId() =
actualBuilder.add(BasicFormatStructure(TimeZoneIdDirective()))

@Suppress("NO_ELSE_IN_WHEN")
override fun dateTimeComponents(format: DateTimeFormat<DateTimeComponents>) = when (format) {
is DateTimeComponentsFormat -> actualBuilder.add(format.actualFormat)
}
override fun dateTimeComponents(format: DateTimeFormat<DateTimeComponents>) =
format.withForceCast { format: DateTimeComponentsFormat -> actualBuilder.add(format.actualFormat) }

override fun createEmpty(): Builder = Builder(AppendableFormatStructure())
}
Expand Down
2 changes: 1 addition & 1 deletion core/common/src/format/DateTimeFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ internal sealed class AbstractDateTimeFormat<T, U : Copyable<U>> : DateTimeForma

open fun valueFromIntermediateOrNull(intermediate: U): T? = try {
valueFromIntermediate(intermediate)
} catch (e: IllegalArgumentException) {
} catch (_: IllegalArgumentException) {
null
}

Expand Down
5 changes: 2 additions & 3 deletions core/common/src/format/DateTimeFormatBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,8 @@ internal fun DateTimeFormatBuilder.WithTime.secondFractionInternal(
maxLength: Int,
grouping: List<Int>
) {
@Suppress("NO_ELSE_IN_WHEN")
when (this) {
is AbstractWithTimeBuilder -> addFormatStructureForTime(
withForceCast { value: AbstractWithTimeBuilder ->
value.addFormatStructureForTime(
BasicFormatStructure(FractionalSecondDirective(minLength, maxLength, grouping))
)
}
Expand Down
6 changes: 2 additions & 4 deletions core/common/src/format/LocalDateFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,8 @@ internal interface AbstractWithDateBuilder : AbstractWithYearMonthBuilder, DateT
override fun dayOfYear(padding: Padding) =
addFormatStructureForDate(BasicFormatStructure(DayOfYearDirective(padding)))

@Suppress("NO_ELSE_IN_WHEN")
override fun date(format: DateTimeFormat<LocalDate>) = when (format) {
is LocalDateFormat -> addFormatStructureForDate(format.actualFormat)
}
override fun date(format: DateTimeFormat<LocalDate>) =
format.withForceCast { format: LocalDateFormat -> addFormatStructureForDate(format.actualFormat) }
}

// these are constants so that the formats are not recreated every time they are used
Expand Down
7 changes: 3 additions & 4 deletions core/common/src/format/LocalDateTimeFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package kotlinx.datetime.format
import kotlinx.datetime.*
import kotlinx.datetime.internal.format.*
import kotlinx.datetime.internal.format.parser.Copyable
import kotlinx.datetime.internal.withForceCast

internal interface DateTimeFieldContainer : DateFieldContainer, TimeFieldContainer

Expand Down Expand Up @@ -67,10 +68,8 @@ internal interface AbstractWithDateTimeBuilder:
addFormatStructureForDateTime(structure)
}

@Suppress("NO_ELSE_IN_WHEN")
override fun dateTime(format: DateTimeFormat<LocalDateTime>) = when (format) {
is LocalDateTimeFormat -> addFormatStructureForDateTime(format.actualFormat)
}
override fun dateTime(format: DateTimeFormat<LocalDateTime>) =
format.withForceCast { format: LocalDateTimeFormat -> addFormatStructureForDateTime(format.actualFormat) }
}

// these are constants so that the formats are not recreated every time they are used
Expand Down
7 changes: 3 additions & 4 deletions core/common/src/format/LocalTimeFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import kotlinx.datetime.DateTimeFormatException
import kotlinx.datetime.internal.DecimalFraction
import kotlinx.datetime.internal.format.*
import kotlinx.datetime.internal.format.parser.Copyable
import kotlinx.datetime.internal.withForceCast

/**
* The AM/PM marker that indicates whether the hour in range `1..12` is before or after noon.
Expand Down Expand Up @@ -122,10 +123,8 @@ internal interface AbstractWithTimeBuilder : DateTimeFormatBuilder.WithTime {
override fun secondFraction(minLength: Int, maxLength: Int) =
addFormatStructureForTime(BasicFormatStructure(FractionalSecondDirective(minLength, maxLength)))

@Suppress("NO_ELSE_IN_WHEN")
override fun time(format: DateTimeFormat<LocalTime>) = when (format) {
is LocalTimeFormat -> addFormatStructureForTime(format.actualFormat)
}
override fun time(format: DateTimeFormat<LocalTime>) =
format.withForceCast { format: LocalTimeFormat -> addFormatStructureForTime(format.actualFormat) }
}

private class HourDirective(private val padding: Padding) :
Expand Down
9 changes: 3 additions & 6 deletions core/common/src/format/Unicode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

package kotlinx.datetime.format

import kotlin.native.concurrent.*

/**
* Marks declarations in the datetime library that use format strings to define datetime formats.
*
Expand Down Expand Up @@ -241,8 +239,7 @@ internal sealed interface UnicodeFormat {
data class StringLiteral(val literal: String) : UnicodeFormat {
override fun toString(): String = if (literal == "'") "''" else
if (literal.any { it.isLetter() }) "'$literal'"
else if (literal.isEmpty()) ""
else literal
else literal.ifEmpty { "" }
}

sealed class Directive : UnicodeFormat {
Expand All @@ -257,8 +254,8 @@ internal sealed interface UnicodeFormat {
sealed class YearMonthBased : DateBased() {
abstract fun addToFormat(builder: DateTimeFormatBuilder.WithYearMonth)
override fun addToFormat(builder: DateTimeFormatBuilder.WithDate) {
val downcastedBuilder: DateTimeFormatBuilder.WithYearMonth = builder
addToFormat(downcastedBuilder)
val downcastBuilder: DateTimeFormatBuilder.WithYearMonth = builder
addToFormat(downcastBuilder)
}

class Era(override val formatLength: Int) : YearMonthBased() {
Expand Down
7 changes: 3 additions & 4 deletions core/common/src/format/UtcOffsetFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package kotlinx.datetime.format
import kotlinx.datetime.*
import kotlinx.datetime.internal.format.*
import kotlinx.datetime.internal.format.parser.Copyable
import kotlinx.datetime.internal.withForceCast
import kotlin.math.*

internal interface UtcOffsetFieldContainer {
Expand All @@ -34,10 +35,8 @@ internal interface AbstractWithOffsetBuilder : DateTimeFormatBuilder.WithUtcOffs
override fun offsetSecondsOfMinute(padding: Padding) =
addFormatStructureForOffset(BasicFormatStructure(UtcOffsetSecondOfMinuteDirective(padding)))

@Suppress("NO_ELSE_IN_WHEN")
override fun offset(format: DateTimeFormat<UtcOffset>) = when (format) {
is UtcOffsetFormat -> addFormatStructureForOffset(format.actualFormat)
}
override fun offset(format: DateTimeFormat<UtcOffset>) =
format.withForceCast { format: UtcOffsetFormat -> addFormatStructureForOffset(format.actualFormat) }
}

internal class UtcOffsetFormat(override val actualFormat: CachedFormatStructure<UtcOffsetFieldContainer>) :
Expand Down
16 changes: 6 additions & 10 deletions core/common/src/format/YearMonthFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,8 @@ private const val YEAR_OF_ERA_COMMENT =
* additional comment and explain that the behavior was not preserved exactly.
*/
internal fun DateTimeFormatBuilder.WithYearMonth.yearOfEra(padding: Padding) {
@Suppress("NO_ELSE_IN_WHEN")
when (this) {
is AbstractWithYearMonthBuilder -> addFormatStructureForYearMonth(
withForceCast { value: AbstractWithYearMonthBuilder ->
value.addFormatStructureForYearMonth(
BasicFormatStructure(YearDirective(padding, isYearOfEra = true))
)
}
Expand All @@ -208,9 +207,8 @@ internal fun DateTimeFormatBuilder.WithYearMonth.yearOfEra(padding: Padding) {
* additional comment and explain that the behavior was not preserved exactly.
*/
internal fun DateTimeFormatBuilder.WithYearMonth.yearOfEraTwoDigits(baseYear: Int) {
@Suppress("NO_ELSE_IN_WHEN")
when (this) {
is AbstractWithYearMonthBuilder -> addFormatStructureForYearMonth(
withForceCast { value: AbstractWithYearMonthBuilder ->
value.addFormatStructureForYearMonth(
BasicFormatStructure(ReducedYearDirective(baseYear, isYearOfEra = true))
)
}
Expand Down Expand Up @@ -284,10 +282,8 @@ internal interface AbstractWithYearMonthBuilder : DateTimeFormatBuilder.WithYear
override fun monthName(names: MonthNames) =
addFormatStructureForYearMonth(BasicFormatStructure(MonthNameDirective(names)))

@Suppress("NO_ELSE_IN_WHEN")
override fun yearMonth(format: DateTimeFormat<YearMonth>) = when (format) {
is YearMonthFormat -> addFormatStructureForYearMonth(format.actualFormat)
}
override fun yearMonth(format: DateTimeFormat<YearMonth>) =
format.withForceCast { format: YearMonthFormat -> addFormatStructureForYearMonth(format.actualFormat) }
}

private val emptyIncompleteYearMonth = IncompleteYearMonth()
Expand Down
12 changes: 9 additions & 3 deletions core/common/src/internal/util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,16 @@ private fun removeLeadingZerosFromLongYearForm(input: String, minStringLengthAft
}

internal fun removeLeadingZerosFromLongYearFormLocalDate(input: String) =
removeLeadingZerosFromLongYearForm(input.toString(), 6) // 6 = "-01-02".length
removeLeadingZerosFromLongYearForm(input, 6) // 6 = "-01-02".length

internal fun removeLeadingZerosFromLongYearFormLocalDateTime(input: String) =
removeLeadingZerosFromLongYearForm(input.toString(), 12) // 12 = "-01-02T23:59".length
removeLeadingZerosFromLongYearForm(input, 12) // 12 = "-01-02T23:59".length

internal fun removeLeadingZerosFromLongYearFormYearMonth(input: String) =
removeLeadingZerosFromLongYearForm(input.toString(), 3) // 3 = "-01".length
removeLeadingZerosFromLongYearForm(input, 3) // 3 = "-01".length

internal inline fun <reified T : S, reified S, R> S.withForceCast(block: (T) -> R): R = when {
this is T -> block(this)
else -> error("Expected ${T::class} but found ${this::class}. " +
"Please report this to the kotlinx-datetime issue tracker.")
}
2 changes: 1 addition & 1 deletion core/common/test/ClockTimeSourceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class ClockTimeSourceTest {
assertEquals(1.days, mark.elapsedNow())

clock.instant -= 2.days
assertEquals(-1.days, mark.elapsedNow())
assertEquals((-1).days, mark.elapsedNow())

clock.instant = Instant.MAX
assertEquals(Duration.INFINITE, mark.elapsedNow())
Expand Down
2 changes: 1 addition & 1 deletion core/common/test/DeprecatedClockTimeSourceTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class DeprecatedClockTimeSourceTest {
assertEquals(1.days, mark.elapsedNow())

clock.instant -= 2.days
assertEquals(-1.days, mark.elapsedNow())
assertEquals((-1).days, mark.elapsedNow())

clock.instant = Instant.fromEpochSeconds(Long.MAX_VALUE)
assertEquals(Duration.INFINITE, mark.elapsedNow())
Expand Down
9 changes: 3 additions & 6 deletions core/common/test/InstantTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,17 @@
package kotlinx.datetime.test

import kotlinx.datetime.*
import kotlinx.datetime.format.*
import kotlinx.datetime.internal.*
import kotlin.random.*
import kotlinx.datetime.internal.NANOS_PER_ONE
import kotlin.random.Random
import kotlin.test.*
import kotlin.time.*
import kotlin.time.Clock
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.Duration.Companion.seconds
import kotlin.time.Clock
import kotlin.time.Instant
import kotlin.time.isDistantFuture
import kotlin.time.isDistantPast

class InstantTest {

Expand Down
13 changes: 7 additions & 6 deletions core/common/test/LocalDateTimeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package kotlinx.datetime.test

import kotlinx.datetime.*
import kotlinx.datetime.LocalDateTime
import kotlin.time.Clock
import kotlin.time.Instant
import kotlin.test.*
Expand All @@ -20,14 +21,14 @@ class LocalDateTimeTest {
@Test
fun localDateTimeParsing() {
fun checkParsedComponents(value: String, year: Int, month: Int, day: Int, hour: Int, minute: Int, second: Int, nanosecond: Int, dayOfWeek: Int? = null, dayOfYear: Int? = null) {
checkComponents(value.toLocalDateTime(), year, month, day, hour, minute, second, nanosecond, dayOfWeek, dayOfYear)
checkComponents(LocalDateTime.parse(value), year, month, day, hour, minute, second, nanosecond, dayOfWeek, dayOfYear)
}
checkParsedComponents("2019-10-01T18:43:15.100500", 2019, 10, 1, 18, 43, 15, 100500000, 2, 274)
checkParsedComponents("2019-10-01T18:43:15", 2019, 10, 1, 18, 43, 15, 0, 2, 274)
checkParsedComponents("2019-10-01T18:12", 2019, 10, 1, 18, 12, 0, 0, 2, 274)

assertFailsWith<DateTimeFormatException> { LocalDateTime.parse("x") }
assertFailsWith<DateTimeFormatException> { "+1000000000-03-26T04:00:00".toLocalDateTime() }
assertFailsWith<DateTimeFormatException> { LocalDateTime.parse("+1000000000-03-26T04:00:00") }

for (i in 1..30) {
checkComponents(LocalDateTime.parse("+${"0".repeat(i)}2024-01-01T23:59"), 2024, 1, 1, 23, 59)
Expand All @@ -46,8 +47,8 @@ class LocalDateTimeTest {

@Test
fun localDtToInstantConversion() {
val ldt1 = "2019-10-01T18:43:15.100500".toLocalDateTime()
val ldt2 = "2019-10-01T19:50:00.500600".toLocalDateTime()
val ldt1 = LocalDateTime.parse("2019-10-01T18:43:15.100500")
val ldt2 = LocalDateTime.parse("2019-10-01T19:50:00.500600")

val diff = with(TimeZone.UTC) { ldt2.toInstant() - ldt1.toInstant() }
assertEquals(with(Duration) { 1.hours + 7.minutes - 15.seconds + 400100.microseconds }, diff)
Expand All @@ -57,8 +58,8 @@ class LocalDateTimeTest {

@Test
fun localDtToInstantConversionRespectsTimezones() {
val ldt1 = "2011-03-26T04:00:00".toLocalDateTime()
val ldt2 = "2011-03-27T04:00:00".toLocalDateTime()
val ldt1 = LocalDateTime.parse("2011-03-26T04:00:00")
val ldt2 = LocalDateTime.parse("2011-03-27T04:00:00")
val diff = with(TimeZone.of("Europe/Moscow")) { ldt2.toInstant() - ldt1.toInstant() }
assertEquals(23.hours, diff)
}
Expand Down
Loading