Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the same ranges for all platforms #453

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
9 changes: 7 additions & 2 deletions core/androidNative/src/internal/TimeZoneNative.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@
package kotlinx.datetime.internal

import kotlinx.cinterop.*
import kotlinx.datetime.TimeZone
import platform.posix.*

internal actual val systemTzdb: TimeZoneDatabase get() = tzdb.getOrThrow()
internal actual fun timeZoneById(zoneId: String): TimeZone =
RegionTimeZone(tzdb.getOrThrow().rulesForId(zoneId), zoneId)

internal actual fun getAvailableZoneIds(): Set<String> =
tzdb.getOrThrow().availableTimeZoneIds()

private val tzdb = runCatching { TzdbBionic() }

internal actual fun currentSystemDefaultZone(): Pair<String, TimeZoneRules?> = memScoped {
internal actual fun currentSystemDefaultZone(): Pair<String, TimeZone?> = memScoped {
val name = readSystemProperty("persist.sys.timezone")
?: throw IllegalStateException("The system property 'persist.sys.timezone' should contain the system timezone")
return name to null
Expand Down
26 changes: 13 additions & 13 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -174,22 +174,33 @@ kotlin {
}
}

val commonKotlinMain by creating {
dependsOn(commonMain.get())
dependencies {
api("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")
}
}

val commonKotlinTest by creating {
dependsOn(commonTest.get())
}

val jvmMain by getting {
}

val jvmTest by getting {
}

val commonJsMain by creating {
dependsOn(commonMain.get())
dependsOn(commonKotlinMain)
dependencies {
api("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")
implementation(npm("@js-joda/core", "3.2.0"))
}
}

val commonJsTest by creating {
dependsOn(commonTest.get())
dependsOn(commonKotlinTest)
dependencies {
implementation(npm("@js-joda/timezone", "2.3.0"))
}
Expand All @@ -211,17 +222,6 @@ kotlin {
dependsOn(commonJsTest)
}

val commonKotlinMain by creating {
dependsOn(commonMain.get())
dependencies {
api("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")
}
}

val commonKotlinTest by creating {
dependsOn(commonTest.get())
}

val nativeMain by getting {
dependsOn(commonKotlinMain)
}
Expand Down
44 changes: 23 additions & 21 deletions core/common/src/DateTimePeriod.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import kotlinx.datetime.serializers.DateTimePeriodComponentSerializer
import kotlin.math.*
import kotlin.time.Duration
import kotlinx.serialization.Serializable
import kotlin.text.toLong
qurbonzoda marked this conversation as resolved.
Show resolved Hide resolved

/**
* A difference between two [instants][Instant], decomposed into date and time components.
Expand Down Expand Up @@ -72,7 +73,7 @@ import kotlinx.serialization.Serializable
@Serializable(with = DateTimePeriodIso8601Serializer::class)
// TODO: could be error-prone without explicitly named params
public sealed class DateTimePeriod {
internal abstract val totalMonths: Int
internal abstract val totalMonths: Long

/**
* The number of calendar days. Can be negative.
Expand All @@ -90,14 +91,14 @@ public sealed class DateTimePeriod {
*
* @sample kotlinx.datetime.test.samples.DateTimePeriodSamples.valueNormalization
*/
public val years: Int get() = totalMonths / 12
public val years: Int get() = (totalMonths / 12).toInt()

/**
* The number of months in this period that don't form a whole year, so this value is always in `(-11..11)`.
*
* @sample kotlinx.datetime.test.samples.DateTimePeriodSamples.valueNormalization
*/
public val months: Int get() = totalMonths % 12
public val months: Int get() = (totalMonths % 12).toInt()

/**
* The number of whole hours in this period. Can be negative.
Expand Down Expand Up @@ -131,7 +132,7 @@ public sealed class DateTimePeriod {
public open val nanoseconds: Int get() = (totalNanoseconds % NANOS_PER_ONE).toInt()

private fun allNonpositive() =
totalMonths <= 0 && days <= 0 && totalNanoseconds <= 0 && (totalMonths or days != 0 || totalNanoseconds != 0L)
totalMonths <= 0 && days <= 0 && totalNanoseconds <= 0 && (totalMonths or totalNanoseconds != 0L || days != 0)

/**
* Converts this period to the ISO 8601 string representation for durations, for example, `P2M1DT3H`.
Expand Down Expand Up @@ -186,7 +187,7 @@ public sealed class DateTimePeriod {
}

override fun hashCode(): Int {
var result = totalMonths
var result = totalMonths.hashCode()
result = 31 * result + days
result = 31 * result + totalNanoseconds.hashCode()
return result
Expand Down Expand Up @@ -314,7 +315,7 @@ public sealed class DateTimePeriod {
while (i < text.length && text[i] in '0'..'9') {
try {
number = safeAdd(safeMultiply(number, 10), (text[i] - '0').toLong())
} catch (e: ArithmeticException) {
} catch (_: ArithmeticException) {
parseException("The number is too large", iStart)
}
i += 1
Expand Down Expand Up @@ -432,7 +433,7 @@ public fun String.toDateTimePeriod(): DateTimePeriod = DateTimePeriod.parse(this
*/
@Serializable(with = DatePeriodIso8601Serializer::class)
public class DatePeriod internal constructor(
internal override val totalMonths: Int,
internal override val totalMonths: Long,
override val days: Int,
) : DateTimePeriod() {
/**
Expand All @@ -448,7 +449,8 @@ public class DatePeriod internal constructor(
* (like "yearly" or "quarterly"), please consider using a multiple of [DateTimeUnit.DateBased] instead.
* For example, instead of `DatePeriod(months = 6)`, one can use `DateTimeUnit.MONTH * 6`.
*
* @throws IllegalArgumentException if the total number of months in [years] and [months] overflows an [Int].
* @throws IllegalArgumentException if the total number of years
* (together with full years in [months]) overflows an [Int].
* @sample kotlinx.datetime.test.samples.DatePeriodSamples.construction
*/
public constructor(years: Int = 0, months: Int = 0, days: Int = 0): this(totalMonths(years, months), days)
Expand All @@ -464,7 +466,7 @@ public class DatePeriod internal constructor(

/** The number of nanoseconds in this period. Always equal to zero. */
override val nanoseconds: Int get() = 0
internal override val totalNanoseconds: Long get() = 0
override val totalNanoseconds: Long get() = 0
ilya-g marked this conversation as resolved.
Show resolved Hide resolved

public companion object {
/**
Expand Down Expand Up @@ -494,17 +496,16 @@ public class DatePeriod internal constructor(
public fun String.toDatePeriod(): DatePeriod = DatePeriod.parse(this)

private class DateTimePeriodImpl(
internal override val totalMonths: Int,
override val totalMonths: Long,
override val days: Int,
internal override val totalNanoseconds: Long,
override val totalNanoseconds: Long,
) : DateTimePeriod()

// TODO: these calculations fit in a JS Number. Possible to do an expect/actual here.
private fun totalMonths(years: Int, months: Int): Int =
when (val totalMonths = years.toLong() * 12 + months.toLong()) {
in Int.MIN_VALUE..Int.MAX_VALUE -> totalMonths.toInt()
else -> throw IllegalArgumentException("The total number of months in $years years and $months months overflows an Int")
private fun totalMonths(years: Int, months: Int): Long = (years.toLong() * 12 + months.toLong()).also {
require(it / 12 in Int.MIN_VALUE..Int.MAX_VALUE) {
"The total number of years in $years years and $months months overflows an Int"
}
}

private fun totalNanoseconds(hours: Int, minutes: Int, seconds: Int, nanoseconds: Long): Long {
val totalMinutes: Long = hours.toLong() * 60 + minutes
Expand All @@ -517,12 +518,12 @@ private fun totalNanoseconds(hours: Int, minutes: Int, seconds: Int, nanoseconds
// absolute value at most 2^44 + 2^31 < 2^45
return try {
multiplyAndAdd(totalSeconds, 1_000_000_000, nanoseconds % NANOS_PER_ONE)
} catch (e: ArithmeticException) {
} catch (_: ArithmeticException) {
throw IllegalArgumentException("The total number of nanoseconds in $hours hours, $minutes minutes, $seconds seconds, and $nanoseconds nanoseconds overflows a Long")
}
}

internal fun buildDateTimePeriod(totalMonths: Int = 0, days: Int = 0, totalNanoseconds: Long): DateTimePeriod =
internal fun buildDateTimePeriod(totalMonths: Long = 0, days: Int = 0, totalNanoseconds: Long): DateTimePeriod =
if (totalNanoseconds != 0L)
DateTimePeriodImpl(totalMonths, days, totalNanoseconds)
else
Expand All @@ -541,9 +542,10 @@ internal fun buildDateTimePeriod(totalMonths: Int = 0, days: Int = 0, totalNanos
* (like "yearly" or "quarterly"), please consider using a multiple of [DateTimeUnit] instead.
* For example, instead of `DateTimePeriod(months = 6)`, one can use `DateTimeUnit.MONTH * 6`.
*
* @throws IllegalArgumentException if the total number of months in [years] and [months] overflows an [Int].
* @throws IllegalArgumentException if the total number of months in [hours], [minutes], [seconds] and [nanoseconds]
* overflows a [Long].
* @throws IllegalArgumentException if the total number of years
* (together with full years in [months]) overflows an [Int].
* @throws IllegalArgumentException if the total number of nanoseconds in
* [hours], [minutes], [seconds] and [nanoseconds] overflows a [Long].
* @sample kotlinx.datetime.test.samples.DateTimePeriodSamples.constructorFunction
*/
public fun DateTimePeriod(
Expand Down
Loading