From 8a4e47b2c28ea9dff042cd2da9da423d6644691d Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Wed, 1 Jun 2022 14:01:50 +0200 Subject: [PATCH 1/4] Implement LocalDate.fromEpochDays --- core/common/src/Instant.kt | 2 + core/common/src/LocalDate.kt | 18 ++++ core/common/src/math.kt | 15 ++- core/common/test/LocalDateTest.kt | 89 ++++++++++++++- core/js/src/LocalDate.kt | 9 ++ core/jvm/src/LocalDate.kt | 5 + core/native/src/Instant.kt | 2 +- core/native/src/LocalDate.kt | 39 ++----- core/native/src/LocalDateTime.kt | 2 +- core/native/src/Month.kt | 9 -- core/native/src/TimeZone.kt | 2 +- core/native/src/mathNative.kt | 8 +- core/native/test/ThreeTenBpLocalDateTest.kt | 113 +------------------- 13 files changed, 149 insertions(+), 164 deletions(-) diff --git a/core/common/src/Instant.kt b/core/common/src/Instant.kt index 53de5477e..908546641 100644 --- a/core/common/src/Instant.kt +++ b/core/common/src/Instant.kt @@ -126,6 +126,8 @@ public expect class Instant : Comparable { * Returns an [Instant] that is [epochMilliseconds] number of milliseconds from the epoch instant `1970-01-01T00:00:00Z`. * * The return value is clamped to the platform-specific boundaries for [Instant] if the result exceeds them. + * + * @see Instant.toEpochMilliseconds */ public fun fromEpochMilliseconds(epochMilliseconds: Long): Instant diff --git a/core/common/src/LocalDate.kt b/core/common/src/LocalDate.kt index 737a797d1..fc432d073 100644 --- a/core/common/src/LocalDate.kt +++ b/core/common/src/LocalDate.kt @@ -32,6 +32,15 @@ public expect class LocalDate : Comparable { */ public fun parse(isoString: String): LocalDate + /** + * Returns a [LocalDate] that is [epochDays] number of days from the epoch day `1970-01-01`. + * + * @throws IllegalArgumentException if the result exceeds the platform-specific boundaries of [LocalDate]. + * + * @see LocalDate.toEpochDays + */ + public fun fromEpochDays(epochDays: Int): LocalDate + internal val MIN: LocalDate internal val MAX: LocalDate } @@ -79,6 +88,15 @@ public expect class LocalDate : Comparable { /** Returns the day-of-year component of the date. */ public val dayOfYear: Int + /** + * Returns the number of days since the epoch day `1970-01-01`. + * + * If the result does not fit in [Int], returns [Int.MAX_VALUE] for a positive result or [Int.MIN_VALUE] for a negative result. + * + * @see LocalDate.fromEpochDays + */ + public fun toEpochDays(): Int + /** * Compares `this` date with the [other] date. * Returns zero if this date represent the same day as the other (i.e. equal to other), diff --git a/core/common/src/math.kt b/core/common/src/math.kt index b647f1d96..ad4d6a23b 100644 --- a/core/common/src/math.kt +++ b/core/common/src/math.kt @@ -200,4 +200,17 @@ internal fun multiplyAndAdd(d: Long, n: Long, r: Long): Long { mr -= n } return safeAdd(safeMultiply(md, n), mr) -} \ No newline at end of file +} + +// org.threeten.bp.chrono.IsoChronology#isLeapYear +internal fun isLeapYear(year: Int): Boolean { + val prolepticYear: Long = year.toLong() + return prolepticYear and 3 == 0L && (prolepticYear % 100 != 0L || prolepticYear % 400 == 0L) +} + +internal fun Int.monthLength(isLeapYear: Boolean): Int = + when (this) { + 2 -> if (isLeapYear) 29 else 28 + 4, 6, 9, 11 -> 30 + else -> 31 + } diff --git a/core/common/test/LocalDateTest.kt b/core/common/test/LocalDateTest.kt index f1bf49ecc..6c68dde60 100644 --- a/core/common/test/LocalDateTest.kt +++ b/core/common/test/LocalDateTest.kt @@ -36,8 +36,9 @@ class LocalDateTest { @Test fun parseIsoString() { - fun checkParsedComponents(value: String, year: Int, month: Int, day: Int, dayOfWeek: Int, dayOfYear: Int) { + fun checkParsedComponents(value: String, year: Int, month: Int, day: Int, dayOfWeek: Int? = null, dayOfYear: Int? = null) { checkComponents(LocalDate.parse(value), year, month, day, dayOfWeek, dayOfYear) + assertEquals(value, LocalDate(year, month, day).toString()) } checkParsedComponents("2019-10-01", 2019, 10, 1, 2, 274) checkParsedComponents("2016-02-29", 2016, 2, 29, 1, 60) @@ -49,6 +50,17 @@ class LocalDateTest { assertInvalidFormat { LocalDate.parse("2017-10--01") } // this date is currently larger than the largest representable one any of the platforms: assertInvalidFormat { LocalDate.parse("+1000000000-10-01") } + // threetenbp + checkParsedComponents("2008-07-05", 2008, 7, 5) + checkParsedComponents("2007-12-31", 2007, 12, 31) + checkParsedComponents("0999-12-31", 999, 12, 31) + checkParsedComponents("-0001-01-02", -1, 1, 2) + checkParsedComponents("9999-12-31", 9999, 12, 31) + checkParsedComponents("-9999-12-31", -9999, 12, 31) + checkParsedComponents("+10000-01-01", 10000, 1, 1) + checkParsedComponents("-10000-01-01", -10000, 1, 1) + checkParsedComponents("+123456-01-01", 123456, 1, 1) + checkParsedComponents("-123456-01-01", -123456, 1, 1) } @Test @@ -221,9 +233,61 @@ class LocalDateTest { assertEquals(Int.MIN_VALUE, LocalDate.MAX.until(LocalDate.MIN, DateTimeUnit.DAY)) } } -} - + @Test + fun fromEpochDays() { + /** This test uses [LocalDate.next] and [LocalDate.previous] and not [LocalDate.plus] because, on Native, + * [LocalDate.plus] is implemented via [LocalDate.toEpochDays]/[LocalDate.fromEpochDays], and so it's better to + * test those independently. */ + if (LocalDate.fromEpochDays(0).daysUntil(LocalDate.MIN) > Int.MIN_VALUE) { + assertEquals(LocalDate.MIN, LocalDate.fromEpochDays(LocalDate.MIN.toEpochDays())) + assertFailsWith { LocalDate.fromEpochDays(LocalDate.MIN.toEpochDays() - 1) } + assertFailsWith { LocalDate.fromEpochDays(Int.MIN_VALUE) } + } + if (LocalDate.fromEpochDays(0).daysUntil(LocalDate.MAX) < Int.MAX_VALUE) { + assertEquals(LocalDate.MAX, LocalDate.fromEpochDays(LocalDate.MAX.toEpochDays())) + assertFailsWith { LocalDate.fromEpochDays(LocalDate.MAX.toEpochDays() + 1) } + assertFailsWith { LocalDate.fromEpochDays(Int.MAX_VALUE) } + } + val eraBeginning = -678941 - 40587 + assertEquals(LocalDate(1970, 1, 1), LocalDate.fromEpochDays(0)) + assertEquals(LocalDate(0, 1, 1), LocalDate.fromEpochDays(eraBeginning)) + assertEquals(LocalDate(-1, 12, 31), LocalDate.fromEpochDays(eraBeginning - 1)) + var test = LocalDate(0, 1, 1) + for (i in eraBeginning..699999) { + assertEquals(test, LocalDate.fromEpochDays(i)) + test = test.plus(DateTimeUnit.DAY) + } + test = LocalDate(0, 1, 1) + for (i in eraBeginning downTo -2000000 + 1) { + assertEquals(test, LocalDate.fromEpochDays(i)) + test = test.minus(DateTimeUnit.DAY) + } + } + // threetenbp + @Test + fun toEpochDays() { + /** This test uses [LocalDate.next] and [LocalDate.previous] and not [LocalDate.plus] because, on Native, + * [LocalDate.plus] is implemented via [LocalDate.toEpochDays]/[LocalDate.fromEpochDays], and so it's better to + * test those independently. */ + val startOfEra = -678941 - 40587 + var date = LocalDate(0, 1, 1) + for (i in startOfEra..699999) { + assertEquals(i, date.toEpochDays()) + date = date.next + } + date = LocalDate(0, 1, 1) + for (i in startOfEra downTo -2000000 + 1) { + assertEquals(i, date.toEpochDays()) + date = date.previous + } + assertEquals(-40587, LocalDate(1858, 11, 17).toEpochDays()) + assertEquals(-678575 - 40587, LocalDate(1, 1, 1).toEpochDays()) + assertEquals(49987 - 40587, LocalDate(1995, 9, 27).toEpochDays()) + assertEquals(0, LocalDate(1970, 1, 1).toEpochDays()) + assertEquals(-678942 - 40587, LocalDate(-1, 12, 31).toEpochDays()) + } +} fun checkInvalidDate(constructor: (year: Int, month: Int, day: Int) -> LocalDate) { assertFailsWith { constructor(2007, 2, 29) } @@ -236,3 +300,22 @@ fun checkInvalidDate(constructor: (year: Int, month: Int, day: Int) -> LocalDate assertFailsWith { constructor(2007, 0, 1) } assertFailsWith { constructor(2007, 13, 1) } } + +private val LocalDate.next: LocalDate get() = + if (dayOfMonth != monthNumber.monthLength(isLeapYear(year))) { + LocalDate(year, monthNumber, dayOfMonth + 1) + } else if (monthNumber != 12) { + LocalDate(year, monthNumber + 1, 1) + } else { + LocalDate(year + 1, 1, 1) + } + +private val LocalDate.previous: LocalDate get() = + if (dayOfMonth != 1) { + LocalDate(year, monthNumber, dayOfMonth - 1) + } else if (monthNumber != 1) { + val newMonthNumber = monthNumber - 1 + LocalDate(year, newMonthNumber, newMonthNumber.monthLength(isLeapYear(year))) + } else { + LocalDate(year - 1, 12, 31) + } \ No newline at end of file diff --git a/core/js/src/LocalDate.kt b/core/js/src/LocalDate.kt index 1ef98ce78..d2dbb45d2 100644 --- a/core/js/src/LocalDate.kt +++ b/core/js/src/LocalDate.kt @@ -22,6 +22,13 @@ public actual class LocalDate internal constructor(internal val value: jtLocalDa internal actual val MIN: LocalDate = LocalDate(jtLocalDate.MIN) internal actual val MAX: LocalDate = LocalDate(jtLocalDate.MAX) + + public actual fun fromEpochDays(epochDays: Int): LocalDate = try { + LocalDate(jtLocalDate.ofEpochDay(epochDays)) + } catch (e: Throwable) { + if (e.isJodaDateTimeException()) throw IllegalArgumentException(e) + throw e + } } public actual constructor(year: Int, monthNumber: Int, dayOfMonth: Int) : @@ -49,6 +56,8 @@ public actual class LocalDate internal constructor(internal val value: jtLocalDa actual override fun toString(): String = value.toString() actual override fun compareTo(other: LocalDate): Int = this.value.compareTo(other.value).toInt() + + public actual fun toEpochDays(): Int = value.toEpochDay().toInt() } public actual fun LocalDate.plus(unit: DateTimeUnit.DateBased): LocalDate = plusNumber(1, unit) diff --git a/core/jvm/src/LocalDate.kt b/core/jvm/src/LocalDate.kt index f7f6603be..bea90e63b 100644 --- a/core/jvm/src/LocalDate.kt +++ b/core/jvm/src/LocalDate.kt @@ -23,6 +23,9 @@ public actual class LocalDate internal constructor(internal val value: jtLocalDa internal actual val MIN: LocalDate = LocalDate(jtLocalDate.MIN) internal actual val MAX: LocalDate = LocalDate(jtLocalDate.MAX) + + public actual fun fromEpochDays(epochDays: Int): LocalDate = + LocalDate(jtLocalDate.ofEpochDay(epochDays.toLong())) } public actual constructor(year: Int, monthNumber: Int, dayOfMonth: Int) : @@ -49,6 +52,8 @@ public actual class LocalDate internal constructor(internal val value: jtLocalDa actual override fun toString(): String = value.toString() actual override fun compareTo(other: LocalDate): Int = this.value.compareTo(other.value) + + public actual fun toEpochDays(): Int = value.toEpochDay().clampToInt() } public actual fun LocalDate.plus(unit: DateTimeUnit.DateBased): LocalDate = diff --git a/core/native/src/Instant.kt b/core/native/src/Instant.kt index 0c31a4870..5e9eee6df 100644 --- a/core/native/src/Instant.kt +++ b/core/native/src/Instant.kt @@ -105,7 +105,7 @@ private val instantParser: Parser } catch (e: ArithmeticException) { throw DateTimeFormatException(e) } - val epochDay = localDate.toEpochDay().toLong() + val epochDay = localDate.toEpochDays().toLong() val instantSecs = epochDay * 86400 - offset.totalSeconds + localTime.toSecondOfDay() + secDelta try { Instant(instantSecs, nano) diff --git a/core/native/src/LocalDate.kt b/core/native/src/LocalDate.kt index 0c571d988..ee257f8bc 100644 --- a/core/native/src/LocalDate.kt +++ b/core/native/src/LocalDate.kt @@ -42,8 +42,8 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu init { // org.threeten.bp.LocalDate#create require(isValidYear(year)) { "Invalid date: the year is out of range" } - require(monthNumber >= 1 && monthNumber <= 12) { "Invalid date: month must be a number between 1 and 12, got $monthNumber" } - require(dayOfMonth >= 1 && dayOfMonth <= 31) { "Invalid date: day of month must be a number between 1 and 31, got $dayOfMonth" } + require(monthNumber in 1..12) { "Invalid date: month must be a number between 1 and 12, got $monthNumber" } + require(dayOfMonth in 1..31) { "Invalid date: day of month must be a number between 1 and 31, got $dayOfMonth" } if (dayOfMonth > 28 && dayOfMonth > monthNumber.monthLength(isLeapYear(year))) { if (dayOfMonth == 29) { throw IllegalArgumentException("Invalid date 'February 29' as '$year' is not a leap year") @@ -60,16 +60,12 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu localDateParser.parse(isoString) // org.threeten.bp.LocalDate#toEpochDay - /** - * @throws IllegalArgumentException if the result exceeds the boundaries - */ - internal fun ofEpochDay(epochDay: Int): LocalDate { + public actual fun fromEpochDays(epochDays: Int): LocalDate { // LocalDate(-999999, 1, 1).toEpochDay(), LocalDate(999999, 12, 31).toEpochDay() - // Unidiomatic code due to https://github.com/Kotlin/kotlinx-datetime/issues/5 - require(epochDay >= MIN_EPOCH_DAY && epochDay <= MAX_EPOCH_DAY) { + require(epochDays in MIN_EPOCH_DAY..MAX_EPOCH_DAY) { "Invalid date: boundaries of LocalDate exceeded" } - var zeroDay = epochDay + DAYS_0000_TO_1970 + var zeroDay = epochDays + DAYS_0000_TO_1970 // find the march-based year zeroDay -= 60 // adjust to 0000-03-01 so leap day is at end of four year cycle @@ -106,7 +102,7 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu } // org.threeten.bp.LocalDate#toEpochDay - internal fun toEpochDay(): Int { + public actual fun toEpochDays(): Int { val y = year val m = monthNumber var total = 0 @@ -140,7 +136,7 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu // org.threeten.bp.LocalDate#getDayOfWeek public actual val dayOfWeek: DayOfWeek get() { - val dow0 = (toEpochDay() + 3).mod(7) + val dow0 = (toEpochDays() + 3).mod(7) return DayOfWeek(dow0 + 1) } @@ -170,15 +166,6 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu return LocalDate(year, month, newDay) } - // org.threeten.bp.LocalDate#plusYears - /** - * @throws IllegalArgumentException if the result exceeds the boundaries - * @throws ArithmeticException if arithmetic overflow occurs - */ - internal fun plusYears(yearsToAdd: Int): LocalDate = - if (yearsToAdd == 0) this - else resolvePreviousValid(safeAdd(year, yearsToAdd), monthNumber, dayOfMonth) - // org.threeten.bp.LocalDate#plusMonths /** * @throws IllegalArgumentException if the result exceeds the boundaries @@ -195,14 +182,6 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu return resolvePreviousValid(newYear, newMonth, dayOfMonth) } - // org.threeten.bp.LocalDate#plusWeeks - /** - * @throws IllegalArgumentException if the result exceeds the boundaries - * @throws ArithmeticException if arithmetic overflow occurs - */ - internal fun plusWeeks(value: Int): LocalDate = - plusDays(safeMultiply(value, 7)) - // org.threeten.bp.LocalDate#plusDays /** * @throws IllegalArgumentException if the result exceeds the boundaries @@ -210,7 +189,7 @@ public actual class LocalDate actual constructor(public actual val year: Int, pu */ internal fun plusDays(daysToAdd: Int): LocalDate = if (daysToAdd == 0) this - else ofEpochDay(safeAdd(toEpochDay(), daysToAdd)) + else fromEpochDays(safeAdd(toEpochDays(), daysToAdd)) override fun equals(other: Any?): Boolean = this === other || (other is LocalDate && compareTo(other) == 0) @@ -292,7 +271,7 @@ public actual fun LocalDate.until(other: LocalDate, unit: DateTimeUnit.DateBased // org.threeten.bp.LocalDate#daysUntil public actual fun LocalDate.daysUntil(other: LocalDate): Int = - other.toEpochDay() - this.toEpochDay() + other.toEpochDays() - this.toEpochDays() // org.threeten.bp.LocalDate#getProlepticMonth internal val LocalDate.prolepticMonth get() = (year * 12) + (monthNumber - 1) diff --git a/core/native/src/LocalDateTime.kt b/core/native/src/LocalDateTime.kt index a81502a66..106b2107f 100644 --- a/core/native/src/LocalDateTime.kt +++ b/core/native/src/LocalDateTime.kt @@ -71,7 +71,7 @@ public actual constructor(public actual val date: LocalDate, public actual val t // org.threeten.bp.chrono.ChronoLocalDateTime#toEpochSecond internal fun toEpochSecond(offset: UtcOffset): Long { - val epochDay = date.toEpochDay().toLong() + val epochDay = date.toEpochDays().toLong() var secs: Long = epochDay * 86400 + time.toSecondOfDay() secs -= offset.totalSeconds return secs diff --git a/core/native/src/Month.kt b/core/native/src/Month.kt index 2eb243e8b..fa5995cca 100644 --- a/core/native/src/Month.kt +++ b/core/native/src/Month.kt @@ -27,12 +27,3 @@ internal fun Month.firstDayOfYear(leapYear: Boolean): Int { Month.DECEMBER -> 335 + leap } } - -// From threetenbp -internal fun Int.monthLength(leapYear: Boolean): Int { - return when (this) { - 2 -> if (leapYear) 29 else 28 - 4, 6, 9, 11 -> 30 - else -> 31 - } -} diff --git a/core/native/src/TimeZone.kt b/core/native/src/TimeZone.kt index 97565f442..c0cf1ff12 100644 --- a/core/native/src/TimeZone.kt +++ b/core/native/src/TimeZone.kt @@ -145,7 +145,7 @@ internal fun Instant.toLocalDateTimeImpl(offset: UtcOffset): LocalDateTime { val localSecond: Long = epochSeconds + offset.totalSeconds // overflow caught later val localEpochDay = localSecond.floorDiv(SECONDS_PER_DAY.toLong()).toInt() val secsOfDay = localSecond.mod(SECONDS_PER_DAY.toLong()).toInt() - val date: LocalDate = LocalDate.ofEpochDay(localEpochDay) // may throw + val date: LocalDate = LocalDate.fromEpochDays(localEpochDay) // may throw val time: LocalTime = LocalTime.ofSecondOfDay(secsOfDay, nanosecondsOfSecond) return LocalDateTime(date, time) } diff --git a/core/native/src/mathNative.kt b/core/native/src/mathNative.kt index 64664faff..af21f97ff 100644 --- a/core/native/src/mathNative.kt +++ b/core/native/src/mathNative.kt @@ -117,10 +117,4 @@ internal fun zoneIdByOffset(totalSeconds: Int): String { } buf.toString() } -} - -// org.threeten.bp.chrono.IsoChronology#isLeapYear -internal fun isLeapYear(year: Int): Boolean { - val prolepticYear: Long = year.toLong() - return prolepticYear and 3 == 0L && (prolepticYear % 100 != 0L || prolepticYear % 400 == 0L) -} +} \ No newline at end of file diff --git a/core/native/test/ThreeTenBpLocalDateTest.kt b/core/native/test/ThreeTenBpLocalDateTest.kt index 2446f2c83..448d7b513 100644 --- a/core/native/test/ThreeTenBpLocalDateTest.kt +++ b/core/native/test/ThreeTenBpLocalDateTest.kt @@ -12,49 +12,6 @@ import kotlinx.datetime.* import kotlin.test.* class ThreeTenBpLocalDateTest { - @Test - fun ofEpochDay() { - val date_0000_01_01 = -678941 - 40587 - val minDate = LocalDate(YEAR_MIN, 1, 1) - assertEquals(minDate, LocalDate.ofEpochDay(minDate.toEpochDay())) - val maxDate = LocalDate(YEAR_MAX, 12, 31) - assertEquals(maxDate, LocalDate.ofEpochDay(maxDate.toEpochDay())) - assertEquals(LocalDate(1970, 1, 1), LocalDate.ofEpochDay(0)) - assertEquals(LocalDate(0, 1, 1), LocalDate.ofEpochDay(date_0000_01_01)) - assertEquals(LocalDate(-1, 12, 31), LocalDate.ofEpochDay(date_0000_01_01 - 1)) - var test = LocalDate(0, 1, 1) - for (i in date_0000_01_01..699999) { - assertEquals(test, LocalDate.ofEpochDay(i)) - test = next(test) - } - test = LocalDate(0, 1, 1) - for (i in date_0000_01_01 downTo -2000000 + 1) { - assertEquals(test, LocalDate.ofEpochDay(i)) - test = previous(test) - } - } - - @Test - fun toEpochDay() { - val date_0000_01_01 = -678941 - 40587 - - var test = LocalDate(0, 1, 1) - for (i in date_0000_01_01..699999) { - assertEquals(i, test.toEpochDay()) - test = next(test) - } - test = LocalDate(0, 1, 1) - for (i in date_0000_01_01 downTo -2000000 + 1) { - assertEquals(i, test.toEpochDay()) - test = previous(test) - } - - assertEquals(-40587, LocalDate(1858, 11, 17).toEpochDay()) - assertEquals(-678575 - 40587, LocalDate(1, 1, 1).toEpochDay()) - assertEquals(49987 - 40587, LocalDate(1995, 9, 27).toEpochDay()) - assertEquals(0, LocalDate(1970, 1, 1).toEpochDay()) - assertEquals(-678942 - 40587, LocalDate(-1, 12, 31).toEpochDay()) - } @Test fun dayOfWeek() { @@ -79,24 +36,15 @@ class ThreeTenBpLocalDateTest { Triple(2004, 1, 1), Triple(-1, 1, 2)) for ((y, m, d) in dates) { - val a: LocalDate = LocalDate(y, m, d) var total = 0 for (i in 1 until m) { total += i.monthLength(isLeapYear(y)) } val doy = total + d - assertEquals(a.dayOfYear, doy) + assertEquals(LocalDate(y, m, d).dayOfYear, doy) } } - @Test - fun plusYears() { - assertEquals(LocalDate(2008, 7, 15), LocalDate(2007, 7, 15).plusYears(1)) - assertEquals(LocalDate(2006, 7, 15), LocalDate(2007, 7, 15).plusYears(-1)) - assertEquals(LocalDate(2009, 2, 28), LocalDate(2008, 2, 29).plusYears(1)) - } - - @Test fun plusMonths() { val date = LocalDate(2007, 7, 15) @@ -109,72 +57,15 @@ class ThreeTenBpLocalDateTest { assertEquals(LocalDate(2007, 4, 30), LocalDate(2007, 3, 31).plusMonths(1)) } - @Test - fun plusWeeks() { - val date = LocalDate(2007, 7, 15) - assertEquals(LocalDate(2007, 7, 22), date.plusWeeks(1)) - assertEquals(LocalDate(2007, 9, 16), date.plusWeeks(9)) - assertEquals(date, LocalDate(2006, 7, 16).plusWeeks(52)) - assertEquals(LocalDate(2008, 7, 12), date.plusYears(-1).plusWeeks(104)) - assertEquals(LocalDate(2007, 7, 8), date.plusWeeks(-1)) - assertEquals(LocalDate(2006, 12, 31), date.plusWeeks(-28)) - assertEquals(LocalDate(2005, 7, 17), date.plusWeeks(-104)) - } - @Test fun plusDays() { val date = LocalDate(2007, 7, 15) assertEquals(LocalDate(2007, 7, 16), date.plusDays(1)) assertEquals(LocalDate(2007, 9, 15), date.plusDays(62)) assertEquals(date, LocalDate(2006, 7, 14).plusDays(366)) - assertEquals(LocalDate(2008, 7, 15), date.plusYears(-1).plusDays(365 + 366)) + assertEquals(LocalDate(2008, 7, 15), date.plusMonths(-12).plusDays(365 + 366)) assertEquals(LocalDate(2007, 7, 14), date.plusDays(-1)) assertEquals(LocalDate(2006, 12, 31), date.plusDays(-196)) assertEquals(LocalDate(2005, 7, 15), date.plusDays(-730)) } - - @Test - fun strings() { - val data = arrayOf( - Pair(LocalDate(2008, 7, 5), "2008-07-05"), - Pair(LocalDate(2007, 12, 31), "2007-12-31"), - Pair(LocalDate(999, 12, 31), "0999-12-31"), - Pair(LocalDate(-1, 1, 2), "-0001-01-02"), - Pair(LocalDate(9999, 12, 31), "9999-12-31"), - Pair(LocalDate(-9999, 12, 31), "-9999-12-31"), - Pair(LocalDate(10000, 1, 1), "+10000-01-01"), - Pair(LocalDate(-10000, 1, 1), "-10000-01-01"), - Pair(LocalDate(123456, 1, 1), "+123456-01-01"), - Pair(LocalDate(-123456, 1, 1), "-123456-01-01")) - for ((date, str) in data) { - assertEquals(date, LocalDate.parse(str)) - assertEquals(str, date.toString()) - } - } - - private fun next(localDate: LocalDate): LocalDate { - var date = localDate - val newDayOfMonth: Int = date.dayOfMonth + 1 - if (newDayOfMonth <= date.monthNumber.monthLength(isLeapYear(date.year))) { - return LocalDate(date.year, date.monthNumber, newDayOfMonth) - } - date = LocalDate(date.year, date.monthNumber, 1) - if (date.month === Month.DECEMBER) { - date = date.withYear(date.year + 1) - } - return LocalDate(date.year, date.monthNumber % 12 + 1, 1) - } - - private fun previous(localDate: LocalDate): LocalDate { - var date = localDate - val newDayOfMonth: Int = date.dayOfMonth - 1 - if (newDayOfMonth > 0) { - return LocalDate(date.year, date.monthNumber, newDayOfMonth) - } - date = LocalDate(date.year, (date.monthNumber + 10) % 12 + 1, date.dayOfMonth) - if (date.month === Month.DECEMBER) { - date = date.withYear(date.year - 1) - } - return LocalDate(date.year, date.monthNumber, date.monthNumber.monthLength(isLeapYear(date.year))) - } } From 8f0e25e3a9f83439c4e1ee7d6cf2f70c54b8035a Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Fri, 3 Jun 2022 11:10:20 +0200 Subject: [PATCH 2/4] Fixup --- core/common/test/LocalDateTest.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/common/test/LocalDateTest.kt b/core/common/test/LocalDateTest.kt index 6c68dde60..7239d35ba 100644 --- a/core/common/test/LocalDateTest.kt +++ b/core/common/test/LocalDateTest.kt @@ -255,12 +255,12 @@ class LocalDateTest { var test = LocalDate(0, 1, 1) for (i in eraBeginning..699999) { assertEquals(test, LocalDate.fromEpochDays(i)) - test = test.plus(DateTimeUnit.DAY) + test = test.next } test = LocalDate(0, 1, 1) for (i in eraBeginning downTo -2000000 + 1) { assertEquals(test, LocalDate.fromEpochDays(i)) - test = test.minus(DateTimeUnit.DAY) + test = test.previous } } From 82aff29a613f7f840e9344cee3cfbdd904a5c8fb Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Fri, 24 Jun 2022 10:15:39 +0200 Subject: [PATCH 3/4] Split math.kt into math.kt and dateCalculations.kt --- core/common/src/DateTimePeriod.kt | 1 + core/common/src/DateTimeUnit.kt | 1 + core/common/src/Instant.kt | 1 + core/common/src/internal/dateCalculations.kt | 41 +++++++++++++++ core/common/src/{ => internal}/math.kt | 37 +------------- core/common/test/InstantTest.kt | 1 + core/common/test/LocalDateTest.kt | 1 + core/common/test/LocalTimeTest.kt | 1 + core/common/test/MultiplyAndDivideTest.kt | 2 +- core/js/src/Instant.kt | 2 + core/js/src/LocalTime.kt | 1 + core/js/src/{ => internal}/mathJs.kt | 4 +- core/jvm/src/Instant.kt | 2 + core/jvm/src/LocalDate.kt | 3 ++ core/jvm/src/LocalTime.kt | 1 + core/jvm/src/{ => internal}/mathJvm.kt | 4 +- .../native/cinterop_actuals/TimeZoneNative.kt | 1 + core/native/src/Instant.kt | 1 + core/native/src/LocalDate.kt | 3 ++ core/native/src/LocalDateTime.kt | 1 + core/native/src/LocalTime.kt | 1 + core/native/src/TimeZone.kt | 1 + core/native/src/UtcOffset.kt | 1 + core/native/src/internal/dateCalculations.kt | 51 +++++++++++++++++++ core/native/src/{ => internal}/mathNative.kt | 50 ++---------------- core/native/test/ThreeTenBpLocalDateTest.kt | 1 + core/native/test/ThreeTenBpUtilTest.kt | 2 +- 27 files changed, 127 insertions(+), 89 deletions(-) create mode 100644 core/common/src/internal/dateCalculations.kt rename core/common/src/{ => internal}/math.kt (84%) rename core/js/src/{ => internal}/mathJs.kt (94%) rename core/jvm/src/{ => internal}/mathJvm.kt (82%) create mode 100644 core/native/src/internal/dateCalculations.kt rename core/native/src/{ => internal}/mathNative.kt (56%) diff --git a/core/common/src/DateTimePeriod.kt b/core/common/src/DateTimePeriod.kt index 139dfcd09..03623a1b7 100644 --- a/core/common/src/DateTimePeriod.kt +++ b/core/common/src/DateTimePeriod.kt @@ -5,6 +5,7 @@ package kotlinx.datetime +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.DatePeriodIso8601Serializer import kotlinx.datetime.serializers.DateTimePeriodIso8601Serializer import kotlin.math.* diff --git a/core/common/src/DateTimeUnit.kt b/core/common/src/DateTimeUnit.kt index 929edaa36..a219cc7ae 100644 --- a/core/common/src/DateTimeUnit.kt +++ b/core/common/src/DateTimeUnit.kt @@ -5,6 +5,7 @@ package kotlinx.datetime +import kotlinx.datetime.internal.safeMultiply import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable import kotlin.time.* diff --git a/core/common/src/Instant.kt b/core/common/src/Instant.kt index 908546641..bfe87cf6e 100644 --- a/core/common/src/Instant.kt +++ b/core/common/src/Instant.kt @@ -5,6 +5,7 @@ package kotlinx.datetime +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.InstantIso8601Serializer import kotlinx.serialization.Serializable import kotlin.time.* diff --git a/core/common/src/internal/dateCalculations.kt b/core/common/src/internal/dateCalculations.kt new file mode 100644 index 000000000..4db38e58c --- /dev/null +++ b/core/common/src/internal/dateCalculations.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2019-2022 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime.internal + +internal const val SECONDS_PER_HOUR = 60 * 60 + +internal const val SECONDS_PER_MINUTE = 60 + +internal const val MINUTES_PER_HOUR = 60 + +internal const val HOURS_PER_DAY = 24 + +internal const val SECONDS_PER_DAY: Int = SECONDS_PER_HOUR * HOURS_PER_DAY + +internal const val NANOS_PER_ONE = 1_000_000_000 +internal const val NANOS_PER_MILLI = 1_000_000 +internal const val MILLIS_PER_ONE = 1_000 + +internal const val NANOS_PER_DAY: Long = NANOS_PER_ONE * SECONDS_PER_DAY.toLong() + +internal const val NANOS_PER_MINUTE: Long = NANOS_PER_ONE * SECONDS_PER_MINUTE.toLong() + +internal const val NANOS_PER_HOUR = NANOS_PER_ONE * SECONDS_PER_HOUR.toLong() + +internal const val MILLIS_PER_DAY: Int = SECONDS_PER_DAY * MILLIS_PER_ONE + +// org.threeten.bp.chrono.IsoChronology#isLeapYear +internal fun isLeapYear(year: Int): Boolean { + val prolepticYear: Long = year.toLong() + return prolepticYear and 3 == 0L && (prolepticYear % 100 != 0L || prolepticYear % 400 == 0L) +} + +internal fun Int.monthLength(isLeapYear: Boolean): Int = + when (this) { + 2 -> if (isLeapYear) 29 else 28 + 4, 6, 9, 11 -> 30 + else -> 31 + } diff --git a/core/common/src/math.kt b/core/common/src/internal/math.kt similarity index 84% rename from core/common/src/math.kt rename to core/common/src/internal/math.kt index ad4d6a23b..87e614a19 100644 --- a/core/common/src/math.kt +++ b/core/common/src/internal/math.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -package kotlinx.datetime +package kotlinx.datetime.internal internal fun Long.clampToInt(): Int = when { @@ -12,28 +12,6 @@ internal fun Long.clampToInt(): Int = else -> toInt() } -internal const val SECONDS_PER_HOUR = 60 * 60 - -internal const val SECONDS_PER_MINUTE = 60 - -internal const val MINUTES_PER_HOUR = 60 - -internal const val HOURS_PER_DAY = 24 - -internal const val SECONDS_PER_DAY: Int = SECONDS_PER_HOUR * HOURS_PER_DAY - -internal const val NANOS_PER_ONE = 1_000_000_000 -internal const val NANOS_PER_MILLI = 1_000_000 -internal const val MILLIS_PER_ONE = 1_000 - -internal const val NANOS_PER_DAY: Long = NANOS_PER_ONE * SECONDS_PER_DAY.toLong() - -internal const val NANOS_PER_MINUTE: Long = NANOS_PER_ONE * SECONDS_PER_MINUTE.toLong() - -internal const val NANOS_PER_HOUR = NANOS_PER_ONE * SECONDS_PER_HOUR.toLong() - -internal const val MILLIS_PER_DAY: Int = SECONDS_PER_DAY * MILLIS_PER_ONE - internal expect fun safeMultiply(a: Long, b: Long): Long internal expect fun safeMultiply(a: Int, b: Int): Int internal expect fun safeAdd(a: Long, b: Long): Long @@ -201,16 +179,3 @@ internal fun multiplyAndAdd(d: Long, n: Long, r: Long): Long { } return safeAdd(safeMultiply(md, n), mr) } - -// org.threeten.bp.chrono.IsoChronology#isLeapYear -internal fun isLeapYear(year: Int): Boolean { - val prolepticYear: Long = year.toLong() - return prolepticYear and 3 == 0L && (prolepticYear % 100 != 0L || prolepticYear % 400 == 0L) -} - -internal fun Int.monthLength(isLeapYear: Boolean): Int = - when (this) { - 2 -> if (isLeapYear) 29 else 28 - 4, 6, 9, 11 -> 30 - else -> 31 - } diff --git a/core/common/test/InstantTest.kt b/core/common/test/InstantTest.kt index 435337a9e..4c9814a64 100644 --- a/core/common/test/InstantTest.kt +++ b/core/common/test/InstantTest.kt @@ -7,6 +7,7 @@ package kotlinx.datetime.test import kotlinx.datetime.* import kotlinx.datetime.Clock // currently, requires an explicit import due to a conflict with the deprecated Clock from kotlin.time +import kotlinx.datetime.internal.* import kotlin.random.* import kotlin.test.* import kotlin.time.* diff --git a/core/common/test/LocalDateTest.kt b/core/common/test/LocalDateTest.kt index 7239d35ba..846ece1b3 100644 --- a/core/common/test/LocalDateTest.kt +++ b/core/common/test/LocalDateTest.kt @@ -6,6 +6,7 @@ package kotlinx.datetime.test import kotlinx.datetime.* +import kotlinx.datetime.internal.* import kotlin.random.* import kotlin.test.* diff --git a/core/common/test/LocalTimeTest.kt b/core/common/test/LocalTimeTest.kt index 09d6735b5..09f0f5f6e 100644 --- a/core/common/test/LocalTimeTest.kt +++ b/core/common/test/LocalTimeTest.kt @@ -6,6 +6,7 @@ package kotlinx.datetime.test import kotlinx.datetime.* +import kotlinx.datetime.internal.* import kotlin.math.* import kotlin.random.* import kotlin.test.* diff --git a/core/common/test/MultiplyAndDivideTest.kt b/core/common/test/MultiplyAndDivideTest.kt index e0e63180e..b2336d2b7 100644 --- a/core/common/test/MultiplyAndDivideTest.kt +++ b/core/common/test/MultiplyAndDivideTest.kt @@ -6,7 +6,7 @@ package kotlinx.datetime.test import kotlin.random.* import kotlin.test.* -import kotlinx.datetime.* +import kotlinx.datetime.internal.* class MultiplyAndDivideTest { diff --git a/core/js/src/Instant.kt b/core/js/src/Instant.kt index 8f003fde4..35faaab9e 100644 --- a/core/js/src/Instant.kt +++ b/core/js/src/Instant.kt @@ -11,6 +11,8 @@ import kotlinx.datetime.internal.JSJoda.OffsetDateTime as jtOffsetDateTime import kotlinx.datetime.internal.JSJoda.Duration as jtDuration import kotlinx.datetime.internal.JSJoda.Clock as jtClock import kotlinx.datetime.internal.JSJoda.ChronoUnit +import kotlinx.datetime.internal.safeAdd +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.InstantIso8601Serializer import kotlinx.serialization.Serializable import kotlin.time.* diff --git a/core/js/src/LocalTime.kt b/core/js/src/LocalTime.kt index 6ce7d4512..df1f749cd 100644 --- a/core/js/src/LocalTime.kt +++ b/core/js/src/LocalTime.kt @@ -4,6 +4,7 @@ */ package kotlinx.datetime +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.LocalTimeIso8601Serializer import kotlinx.serialization.Serializable import kotlinx.datetime.internal.JSJoda.LocalTime as jtLocalTime diff --git a/core/js/src/mathJs.kt b/core/js/src/internal/mathJs.kt similarity index 94% rename from core/js/src/mathJs.kt rename to core/js/src/internal/mathJs.kt index 5a3f33e9c..91e09a1c8 100644 --- a/core/js/src/mathJs.kt +++ b/core/js/src/internal/mathJs.kt @@ -1,9 +1,9 @@ /* - * Copyright 2019-2020 JetBrains s.r.o. + * Copyright 2019-2022 JetBrains s.r.o. and contributors. * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -package kotlinx.datetime +package kotlinx.datetime.internal /** * Safely adds two long values. diff --git a/core/jvm/src/Instant.kt b/core/jvm/src/Instant.kt index 9545076a8..9584dbe53 100644 --- a/core/jvm/src/Instant.kt +++ b/core/jvm/src/Instant.kt @@ -6,6 +6,8 @@ package kotlinx.datetime +import kotlinx.datetime.internal.safeMultiply +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.InstantIso8601Serializer import kotlinx.serialization.Serializable import java.time.DateTimeException diff --git a/core/jvm/src/LocalDate.kt b/core/jvm/src/LocalDate.kt index bea90e63b..8089edf47 100644 --- a/core/jvm/src/LocalDate.kt +++ b/core/jvm/src/LocalDate.kt @@ -5,6 +5,9 @@ @file:JvmName("LocalDateJvmKt") package kotlinx.datetime +import kotlinx.datetime.internal.safeAdd +import kotlinx.datetime.internal.safeMultiply +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.LocalDateIso8601Serializer import kotlinx.serialization.Serializable import java.time.DateTimeException diff --git a/core/jvm/src/LocalTime.kt b/core/jvm/src/LocalTime.kt index ad598045e..335c5be7c 100644 --- a/core/jvm/src/LocalTime.kt +++ b/core/jvm/src/LocalTime.kt @@ -6,6 +6,7 @@ package kotlinx.datetime +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.LocalTimeIso8601Serializer import kotlinx.serialization.Serializable import java.time.DateTimeException diff --git a/core/jvm/src/mathJvm.kt b/core/jvm/src/internal/mathJvm.kt similarity index 82% rename from core/jvm/src/mathJvm.kt rename to core/jvm/src/internal/mathJvm.kt index 074bd7987..0e528b63d 100644 --- a/core/jvm/src/mathJvm.kt +++ b/core/jvm/src/internal/mathJvm.kt @@ -1,9 +1,9 @@ /* - * Copyright 2019-2020 JetBrains s.r.o. + * Copyright 2019-2022 JetBrains s.r.o. and contributors. * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ -package kotlinx.datetime +package kotlinx.datetime.internal internal actual fun safeMultiply(a: Long, b: Long): Long = Math.multiplyExact(a, b) internal actual fun safeMultiply(a: Int, b: Int): Int = Math.multiplyExact(a, b) diff --git a/core/native/cinterop_actuals/TimeZoneNative.kt b/core/native/cinterop_actuals/TimeZoneNative.kt index f41f1e7ca..ab040415a 100644 --- a/core/native/cinterop_actuals/TimeZoneNative.kt +++ b/core/native/cinterop_actuals/TimeZoneNative.kt @@ -6,6 +6,7 @@ package kotlinx.datetime import kotlinx.datetime.internal.* import kotlinx.cinterop.* +import kotlinx.datetime.internal.* import platform.posix.free internal actual class RegionTimeZone(private val tzid: TZID, actual override val id: String): TimeZone() { diff --git a/core/native/src/Instant.kt b/core/native/src/Instant.kt index 5e9eee6df..bada48cb4 100644 --- a/core/native/src/Instant.kt +++ b/core/native/src/Instant.kt @@ -8,6 +8,7 @@ package kotlinx.datetime +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.InstantIso8601Serializer import kotlinx.serialization.Serializable import kotlin.math.* diff --git a/core/native/src/LocalDate.kt b/core/native/src/LocalDate.kt index ee257f8bc..51230ab52 100644 --- a/core/native/src/LocalDate.kt +++ b/core/native/src/LocalDate.kt @@ -8,6 +8,9 @@ package kotlinx.datetime +import kotlinx.datetime.internal.* +import kotlinx.datetime.internal.safeAdd +import kotlinx.datetime.internal.safeMultiply import kotlinx.datetime.serializers.LocalDateIso8601Serializer import kotlinx.serialization.Serializable import kotlin.math.* diff --git a/core/native/src/LocalDateTime.kt b/core/native/src/LocalDateTime.kt index 106b2107f..54b23aff7 100644 --- a/core/native/src/LocalDateTime.kt +++ b/core/native/src/LocalDateTime.kt @@ -8,6 +8,7 @@ package kotlinx.datetime +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.LocalDateTimeIso8601Serializer import kotlinx.serialization.Serializable diff --git a/core/native/src/LocalTime.kt b/core/native/src/LocalTime.kt index 87ce49b9e..e12711333 100644 --- a/core/native/src/LocalTime.kt +++ b/core/native/src/LocalTime.kt @@ -8,6 +8,7 @@ package kotlinx.datetime +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.LocalTimeIso8601Serializer import kotlinx.serialization.Serializable diff --git a/core/native/src/TimeZone.kt b/core/native/src/TimeZone.kt index c0cf1ff12..681716d74 100644 --- a/core/native/src/TimeZone.kt +++ b/core/native/src/TimeZone.kt @@ -8,6 +8,7 @@ package kotlinx.datetime +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.* import kotlinx.serialization.Serializable diff --git a/core/native/src/UtcOffset.kt b/core/native/src/UtcOffset.kt index 19873dc99..7f904cd5a 100644 --- a/core/native/src/UtcOffset.kt +++ b/core/native/src/UtcOffset.kt @@ -5,6 +5,7 @@ package kotlinx.datetime +import kotlinx.datetime.internal.* import kotlinx.datetime.serializers.UtcOffsetSerializer import kotlinx.serialization.Serializable import kotlin.math.abs diff --git a/core/native/src/internal/dateCalculations.kt b/core/native/src/internal/dateCalculations.kt new file mode 100644 index 000000000..f185be183 --- /dev/null +++ b/core/native/src/internal/dateCalculations.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2019-2022 JetBrains s.r.o. and contributors. + * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. + */ + +package kotlinx.datetime.internal + +import kotlin.math.* + +/** + * All code below was taken from various places of https://github.com/ThreeTen/threetenbp with few changes + */ + +/** + * The number of days in a 400 year cycle. + */ +internal const val DAYS_PER_CYCLE = 146097 + +/** + * The number of days from year zero to year 1970. + * There are five 400 year cycles from year zero to 2000. + * There are 7 leap years from 1970 to 2000. + */ +internal const val DAYS_0000_TO_1970 = DAYS_PER_CYCLE * 5 - (30 * 365 + 7) + +// days in a 400 year cycle = 146097 +// days in a 10,000 year cycle = 146097 * 25 +// seconds per day = 86400 +internal const val SECONDS_PER_10000_YEARS = 146097L * 25L * 86400L + +internal const val SECONDS_0000_TO_1970 = (146097L * 5L - (30L * 365L + 7L)) * 86400L + +// org.threeten.bp.ZoneOffset#buildId +internal fun zoneIdByOffset(totalSeconds: Int): String { + return if (totalSeconds == 0) { + "Z" + } else { + val absTotalSeconds: Int = abs(totalSeconds) + val buf = StringBuilder() + val absHours: Int = absTotalSeconds / SECONDS_PER_HOUR + val absMinutes: Int = absTotalSeconds / SECONDS_PER_MINUTE % MINUTES_PER_HOUR + buf.append(if (totalSeconds < 0) "-" else "+") + .append(if (absHours < 10) "0" else "").append(absHours) + .append(if (absMinutes < 10) ":0" else ":").append(absMinutes) + val absSeconds: Int = absTotalSeconds % SECONDS_PER_MINUTE + if (absSeconds != 0) { + buf.append(if (absSeconds < 10) ":0" else ":").append(absSeconds) + } + buf.toString() + } +} diff --git a/core/native/src/mathNative.kt b/core/native/src/internal/mathNative.kt similarity index 56% rename from core/native/src/mathNative.kt rename to core/native/src/internal/mathNative.kt index af21f97ff..9fc4c7e0b 100644 --- a/core/native/src/mathNative.kt +++ b/core/native/src/internal/mathNative.kt @@ -1,36 +1,12 @@ /* - * Copyright 2019-2020 JetBrains s.r.o. + * Copyright 2019-2022 JetBrains s.r.o. and contributors. * Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file. */ + /* Based on the ThreeTenBp project. * Copyright (c) 2007-present, Stephen Colebourne & Michael Nascimento Santos */ -package kotlinx.datetime - -import kotlin.math.abs - -/** - * All code below was taken from various places of https://github.com/ThreeTen/threetenbp with few changes - */ - -/** - * The number of days in a 400 year cycle. - */ -internal const val DAYS_PER_CYCLE = 146097 - -/** - * The number of days from year zero to year 1970. - * There are five 400 year cycles from year zero to 2000. - * There are 7 leap years from 1970 to 2000. - */ -internal const val DAYS_0000_TO_1970 = DAYS_PER_CYCLE * 5 - (30 * 365 + 7) - -// days in a 400 year cycle = 146097 -// days in a 10,000 year cycle = 146097 * 25 -// seconds per day = 86400 -internal const val SECONDS_PER_10000_YEARS = 146097L * 25L * 86400L - -internal const val SECONDS_0000_TO_1970 = (146097L * 5L - (30L * 365L + 7L)) * 86400L +package kotlinx.datetime.internal /** * Safely adds two long values. @@ -97,24 +73,4 @@ internal actual fun safeMultiply(a: Int, b: Int): Int { throw ArithmeticException("Multiplication overflows an int: $a * $b") } return total.toInt() -} - -// org.threeten.bp.ZoneOffset#buildId -internal fun zoneIdByOffset(totalSeconds: Int): String { - return if (totalSeconds == 0) { - "Z" - } else { - val absTotalSeconds: Int = abs(totalSeconds) - val buf = StringBuilder() - val absHours: Int = absTotalSeconds / SECONDS_PER_HOUR - val absMinutes: Int = absTotalSeconds / SECONDS_PER_MINUTE % MINUTES_PER_HOUR - buf.append(if (totalSeconds < 0) "-" else "+") - .append(if (absHours < 10) "0" else "").append(absHours) - .append(if (absMinutes < 10) ":0" else ":").append(absMinutes) - val absSeconds: Int = absTotalSeconds % SECONDS_PER_MINUTE - if (absSeconds != 0) { - buf.append(if (absSeconds < 10) ":0" else ":").append(absSeconds) - } - buf.toString() - } } \ No newline at end of file diff --git a/core/native/test/ThreeTenBpLocalDateTest.kt b/core/native/test/ThreeTenBpLocalDateTest.kt index 448d7b513..8aed1862e 100644 --- a/core/native/test/ThreeTenBpLocalDateTest.kt +++ b/core/native/test/ThreeTenBpLocalDateTest.kt @@ -9,6 +9,7 @@ package kotlinx.datetime.test import kotlinx.datetime.* +import kotlinx.datetime.internal.* import kotlin.test.* class ThreeTenBpLocalDateTest { diff --git a/core/native/test/ThreeTenBpUtilTest.kt b/core/native/test/ThreeTenBpUtilTest.kt index ec2f67ac8..592bb270c 100644 --- a/core/native/test/ThreeTenBpUtilTest.kt +++ b/core/native/test/ThreeTenBpUtilTest.kt @@ -8,7 +8,7 @@ package kotlinx.datetime.test -import kotlinx.datetime.* +import kotlinx.datetime.internal.* import kotlin.test.* class ThreeTenBpUtilTest { From 7aa5c7dc4d25f3d920b0aa534bf7bfca52b406dd Mon Sep 17 00:00:00 2001 From: Dmitry Khalanskiy Date: Fri, 24 Jun 2022 12:17:02 +0200 Subject: [PATCH 4/4] Further increase timeout for JS tests --- core/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 33f0766d8..e1fea3da6 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -68,7 +68,7 @@ kotlin { nodejs { testTask { useMocha { - timeout = "5s" + timeout = "30s" } } }