Skip to content

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
dkhalanskyjb committed Nov 5, 2024
1 parent 17bfddc commit e1a2c61
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 69 deletions.
17 changes: 12 additions & 5 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

/**
* A difference between two [instants][Instant], decomposed into date and time components.
Expand Down Expand Up @@ -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 Down Expand Up @@ -499,7 +501,11 @@ private class DateTimePeriodImpl(
override val totalNanoseconds: Long,
) : DateTimePeriod()

private fun totalMonths(years: Int, months: Int): Long = years.toLong() * 12 + months.toLong()
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 Down Expand Up @@ -536,9 +542,10 @@ internal fun buildDateTimePeriod(totalMonths: Long = 0, days: Int = 0, totalNano
* (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
2 changes: 1 addition & 1 deletion core/common/test/DateTimePeriodTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class DateTimePeriodTest {

assertFailsWith<IllegalArgumentException> { DateTimePeriod.parse("P") }

// overflow of `Long.MAX_VALUE` months
// overflow of `Int.MAX_VALUE` years
assertFailsWith<IllegalArgumentException> { DateTimePeriod.parse("P768614336404564651Y") }
assertFailsWith<IllegalArgumentException> { DateTimePeriod.parse("P1Y9223372036854775805M") }

Expand Down
10 changes: 6 additions & 4 deletions core/common/test/InstantTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import kotlinx.datetime.internal.*
import kotlin.random.*
import kotlin.test.*
import kotlin.time.*
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.hours
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.nanoseconds
Expand Down Expand Up @@ -268,9 +269,8 @@ class InstantTest {
val offset3 = instant3.offsetIn(zone)
assertEquals(offset1, offset3)

// TODO: fails on JS
// // without the minus, this test fails on JVM
// (Instant.MAX - (2 * 365).days).offsetIn(zone)
// without the minus, this test fails on JVM
(Instant.MAX - (2 * 365).days).offsetIn(zone)
}

@Test
Expand Down Expand Up @@ -313,7 +313,9 @@ class InstantTest {
val diff2 = date1.periodUntil(date2)

if (diff1 != diff2)
println("start: $instant1, end: $instant2, diff by instants: $diff1, diff by dates: $diff2")
throw AssertionError(
"start: $instant1, end: $instant2, diff by instants: $diff1, diff by dates: $diff2"
)
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/commonJs/src/PlatformSpecifics.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ public expect interface InteropInterface

@OptIn(ExperimentalMultiplatform::class)
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE)
@Target(AnnotationTarget.FILE)
@OptionalExpectation
public expect annotation class JsNonModule()

@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FILE)
@Target(AnnotationTarget.FILE)
public expect annotation class JsModule(val import: String)
1 change: 0 additions & 1 deletion core/commonJs/src/internal/Platform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ private val tzdb: Result<TimeZoneDatabase?> = runCatching {
(unpackBase60(it) * SECONDS_PER_MINUTE * MILLIS_PER_ONE).roundToLong() / // minutes to milliseconds
MILLIS_PER_ONE // but we only need seconds
}
println("Zone ${components[0]}: $offsets with ${components[2]} raw data")
zones[components[0]] = TimeZoneRules(
transitionEpochSeconds = lengthsOfPeriodsWithOffsets.partialSums().take<Long>(indices.size - 1),
offsets = indices.map { UtcOffset(null, null, -(offsets[it] * 60).roundToInt()) },
Expand Down
50 changes: 27 additions & 23 deletions core/commonJs/test/JsJodaTimezoneTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import kotlin.math.roundToInt
import kotlin.test.*
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
import kotlinx.datetime.test.JSJoda.Instant as jtInstant
import kotlinx.datetime.test.JSJoda.ZoneId as jtZoneId

class JsJodaTimezoneTest {
@Test
Expand All @@ -25,33 +27,35 @@ class JsJodaTimezoneTest {
fun iterateOverAllTimezones() {
for (id in TimeZone.availableZoneIds) {
val rules = rulesForId(id) ?: throw AssertionError("No rules for $id")
val jodaZone = kotlinx.datetime.test.JSJoda.ZoneId.of(id)
assertNull(rules.recurringZoneRules)
val jodaZone = jtZoneId.of(id)
assertNull(rules.recurringZoneRules) // js-joda doesn't expose recurring rules
fun checkAtInstant(instant: Instant) {
val jodaInstant = kotlinx.datetime.test.JSJoda.Instant.ofEpochMilli(instant.toEpochMilliseconds().toDouble())
val zdt = jodaInstant.atZone(jodaZone)
val offset = rules.infoAtInstant(instant)
val ourLdt = instant.toLocalDateTime(offset)
val theirLdt = LocalDateTime(
zdt.year(),
zdt.monthValue(),
zdt.dayOfMonth(),
zdt.hour(),
zdt.minute(),
zdt.second(),
zdt.nano().roundToInt()
)
if ((ourLdt.toInstant(TimeZone.UTC) - theirLdt.toInstant(TimeZone.UTC)).absoluteValue > 1.seconds) {
// It seems that sometimes, js-joda interprets its data incorrectly by at most one second,
// and we don't want to replicate that.
// Example: America/Noronha at 1914-01-01T02:09:39.998Z:
// - Computed 1913-12-31T23:59:59.998 with offset -02:09:40
// - 1914-01-01T00:00:00.998-02:09:39[America/Noronha] is js-joda's interpretation
// The raw data representing the offset is `29.E`, which is `2 * 60 + 9 + (ord 'E' - 29) / 60`,
// and `ord 'E'` is 69, so the offset is -2:09:40.
// Thus, we allow a difference of 1 second.
throw AssertionError("Failed for $id at $instant: computed $ourLdt with offset $offset, but $zdt is correct")
val zdt = jtInstant.ofEpochMilli(instant.toEpochMilliseconds().toDouble()).atZone(jodaZone)
val theirLdt = with(zdt) {
LocalDateTime(
year(),
monthValue(),
dayOfMonth(),
hour(),
minute(),
second(),
nano().roundToInt()
)
}
// It seems that sometimes, js-joda interprets its data incorrectly by at most one second,
// and we don't want to replicate that.
// Example: America/Noronha at 1914-01-01T02:09:39.998Z:
// - Computed 1913-12-31T23:59:59.998 with offset -02:09:40
// - 1914-01-01T00:00:00.998-02:09:39[America/Noronha] is js-joda's interpretation
// The raw data representing the offset is `29.E`, which is `2 * 60 + 9 + (ord 'E' - 29) / 60`,
// and `ord 'E'` is 69, so the offset is -2:09:40.
// Thus, we allow a difference of 1 second.
assertTrue(
(ourLdt.toInstant(TimeZone.UTC) - theirLdt.toInstant(TimeZone.UTC)).absoluteValue <= 1.seconds,
"Failed for $id at $instant: computed $ourLdt with offset $offset, but $zdt is correct"
)
}
fun checkTransition(instant: Instant) {
checkAtInstant(instant - 2.milliseconds)
Expand Down
8 changes: 4 additions & 4 deletions core/commonKotlin/src/Instant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,12 @@ public actual fun Instant.periodUntil(other: Instant, timeZone: TimeZone): DateT
val otherLdt = other.toZonedDateTimeFailing(timeZone)

val months = thisLdt.until(otherLdt, DateTimeUnit.MONTH) // `until` on dates never fails
thisLdt = thisLdt.plus(months.toLong(), DateTimeUnit.MONTH) // won't throw: thisLdt + months <= otherLdt, which is known to be valid
val days = thisLdt.until(otherLdt, DateTimeUnit.DAY).toInt() // `until` on dates never fails
thisLdt = thisLdt.plus(days.toLong(), DateTimeUnit.DAY) // won't throw: thisLdt + days <= otherLdt
thisLdt = thisLdt.plus(months, DateTimeUnit.MONTH) // won't throw: thisLdt + months <= otherLdt, which is known to be valid
val days = thisLdt.until(otherLdt, DateTimeUnit.DAY) // `until` on dates never fails
thisLdt = thisLdt.plus(days, DateTimeUnit.DAY) // won't throw: thisLdt + days <= otherLdt
val nanoseconds = thisLdt.until(otherLdt, DateTimeUnit.NANOSECOND) // |otherLdt - thisLdt| < 24h

return buildDateTimePeriod(months, days, nanoseconds)
return buildDateTimePeriod(months, days.toInt(), nanoseconds)
}

public actual fun Instant.until(other: Instant, unit: DateTimeUnit, timeZone: TimeZone): Long =
Expand Down
1 change: 0 additions & 1 deletion core/commonKotlin/src/internal/Platform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ package kotlinx.datetime.internal
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone

// RegionTimeZone(systemTzdb.rulesForId(zoneId), zoneId)
internal expect fun timeZoneById(zoneId: String): TimeZone

internal expect fun getAvailableZoneIds(): Set<String>
Expand Down
21 changes: 0 additions & 21 deletions js-without-timezones/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,12 @@ plugins {
id("org.jetbrains.kotlinx.kover")
}

val mainJavaToolchainVersion: String by project

java {
toolchain { languageVersion.set(JavaLanguageVersion.of(mainJavaToolchainVersion)) }
}

kotlin {

js {
nodejs {
}
compilations.all {
kotlinOptions {
sourceMap = true
moduleKind = "umd"
metaInfo = true
}
}
}


wasmJs {
nodejs {
}
Expand All @@ -42,12 +27,6 @@ kotlin {
resources.srcDir("$targetName/${suffix?.let { it + "Resources" } ?: "resources"}")
}

targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget> {
compilations["test"].kotlinOptions {
freeCompilerArgs += listOf("-trw")
}
}

sourceSets {
commonMain {
dependencies {
Expand Down
14 changes: 7 additions & 7 deletions license/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,19 @@ may apply:
- Origin: implementation of date/time calculations is based on ThreeTen backport project.
- License: BSD 3-Clause ([license/thirdparty/threetenbp_license.txt][threetenbp])

- Path: `core/nativeMain/src`
- Path: `core/commonKotlin/src`
- Origin: implementation of date/time entities is based on ThreeTen backport project.
- License: BSD 3-Clause ([license/thirdparty/threetenbp_license.txt][threetenbp])

- Path: `core/nativeTest/src`
- Path: `core/commonKotlin/src`
- Origin: Derived from tests of ThreeTen backport project
- License: BSD 3-Clause ([license/thirdparty/threetenbp_license.txt][threetenbp])

- Path: `core/commonTest/src`
- Origin: Some tests are derived from tests of ThreeTen backport project
- License: BSD 3-Clause ([license/thirdparty/threetenbp_license.txt][threetenbp])

- Path: `thirdparty/date`
- Origin: https://github.com/HowardHinnant/date library
- License: MIT ([license/thirdparty/cppdate_license.txt](thirdparty/cppdate_license.txt))

- Path: `core/nativeMain/cinterop/public/windows_zones.hpp`
- Path: `core/windows/src/internal/WindowsZoneNames.kt`
- Origin: time zone name mappings for Windows are generated from
https://raw.githubusercontent.com/unicode-org/cldr/master/common/supplemental/windowsZones.xml
- License: Unicode ([license/thirdparty/unicode_license.txt](thirdparty/unicode_license.txt))
Expand All @@ -31,4 +27,8 @@ may apply:
- Origin: implementation is based on the bionic project.
- License: BSD ([license/thirdparty/bionic_license.txt](thirdparty/bionic_license.txt))

- Path: `core/commonJs/src`
- Origin: implementation accesses the internals of js-joda.
- License: BSD ([license/thirdparty/js-joda_license.txt](thirdparty/js-joda_license.txt))

[threetenbp]: thirdparty/threetenbp_license.txt
34 changes: 34 additions & 0 deletions license/thirdparty/js-joda_license.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
BSD License

For js-joda software

Copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper

All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of js-joda nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

0 comments on commit e1a2c61

Please sign in to comment.