Skip to content

Commit ad8551b

Browse files
authored
Common: add Instant.fromEpochSeconds(Long, Int) (#46)
This overload allows to pass an Int as the number of nanoseconds.
1 parent 5dcb6e2 commit ad8551b

File tree

7 files changed

+99
-32
lines changed

7 files changed

+99
-32
lines changed

core/commonMain/src/Instant.kt

+8
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ public expect class Instant : Comparable<Instant> {
111111
*/
112112
fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long = 0): Instant
113113

114+
/**
115+
* Returns an [Instant] that is the [epochSeconds] number of seconds from the epoch instant `1970-01-01T00:00:00Z`
116+
* and the [nanosecondAdjustment] number of nanoseconds from the whole second.
117+
*
118+
* The return value is clamped to the platform-specific boundaries for [Instant] if the result exceeds them.
119+
*/
120+
fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant
121+
114122
/**
115123
* Parses a string that represents an instant in ISO-8601 format including date and time components and
116124
* the mandatory `Z` designator of the UTC+0 time zone and returns the parsed [Instant] value.

core/commonTest/src/InstantTest.kt

+18-6
Original file line numberDiff line numberDiff line change
@@ -264,22 +264,34 @@ class InstantTest {
264264
@Test
265265
fun nanosecondAdjustment() {
266266
for (i in -2..2L) {
267-
for (j in 0..9L) {
267+
for (j in 0..9) {
268268
val t: Instant = Instant.fromEpochSeconds(i, j)
269+
val t2: Instant = Instant.fromEpochSeconds(i, j.toLong())
269270
assertEquals(i, t.epochSeconds)
270-
assertEquals(j, t.nanosecondsOfSecond.toLong())
271+
assertEquals(j, t.nanosecondsOfSecond)
272+
assertEquals(t, t2)
271273
}
272-
for (j in -10..-1L) {
274+
for (j in -10..-1) {
273275
val t: Instant = Instant.fromEpochSeconds(i, j)
276+
val t2: Instant = Instant.fromEpochSeconds(i, j.toLong())
274277
assertEquals(i - 1, t.epochSeconds)
275-
assertEquals(j + 1000000000, t.nanosecondsOfSecond.toLong())
278+
assertEquals(j + 1000000000, t.nanosecondsOfSecond)
279+
assertEquals(t, t2)
276280
}
277-
for (j in 999_999_990..999_999_999L) {
281+
for (j in 999_999_990..999_999_999) {
278282
val t: Instant = Instant.fromEpochSeconds(i, j)
283+
val t2: Instant = Instant.fromEpochSeconds(i, j.toLong())
279284
assertEquals(i, t.epochSeconds)
280-
assertEquals(j, t.nanosecondsOfSecond.toLong())
285+
assertEquals(j, t.nanosecondsOfSecond)
286+
assertEquals(t, t2)
281287
}
282288
}
289+
val t = Instant.fromEpochSeconds(0, Int.MAX_VALUE)
290+
assertEquals((Int.MAX_VALUE / 1_000_000_000).toLong(), t.epochSeconds)
291+
assertEquals(Int.MAX_VALUE % 1_000_000_000, t.nanosecondsOfSecond)
292+
val t2 = Instant.fromEpochSeconds(0, Long.MAX_VALUE)
293+
assertEquals(Long.MAX_VALUE / 1_000_000_000, t2.epochSeconds)
294+
assertEquals((Long.MAX_VALUE % 1_000_000_000).toInt(), t2.nanosecondsOfSecond)
283295
}
284296

285297
/* Based on the ThreeTenBp project.

core/jsMain/src/Instant.kt

+12
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ public actual class Instant internal constructor(internal val value: jtInstant)
8080
}
8181

8282
actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Long): Instant = try {
83+
/* Performing normalization here because otherwise this fails:
84+
assertEquals((Long.MAX_VALUE % 1_000_000_000).toInt(),
85+
Instant.fromEpochSeconds(0, Long.MAX_VALUE).nanosecondsOfSecond) */
86+
val secs = safeAdd(epochSeconds, floorDiv(nanosecondAdjustment, NANOS_PER_ONE.toLong()))
87+
val nos = floorMod(nanosecondAdjustment, NANOS_PER_ONE.toLong()).toInt()
88+
Instant(jtInstant.ofEpochSecond(secs, nos))
89+
} catch (e: Throwable) {
90+
if (!e.isJodaDateTimeException() && e !is ArithmeticException) throw e
91+
if (epochSeconds > 0) MAX else MIN
92+
}
93+
94+
actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant = try {
8395
Instant(jtInstant.ofEpochSecond(epochSeconds, nanosecondAdjustment))
8496
} catch (e: Throwable) {
8597
if (!e.isJodaDateTimeException()) throw e

core/jsMain/src/mathJs.kt

+31
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,34 @@ internal actual fun safeMultiply(a: Int, b: Int): Int {
5858
if (result > Int.MAX_VALUE || result < Int.MIN_VALUE) throw ArithmeticException("Multiplication overflows Int range: $a * $b.")
5959
return result.toInt()
6060
}
61+
62+
/**
63+
* Returns the floor division.
64+
*
65+
* This returns `0` for `floorDiv(0, 4)`.
66+
* This returns `-1` for `floorDiv(-1, 4)`.
67+
* This returns `-1` for `floorDiv(-2, 4)`.
68+
* This returns `-1` for `floorDiv(-3, 4)`.
69+
* This returns `-1` for `floorDiv(-4, 4)`.
70+
* This returns `-2` for `floorDiv(-5, 4)`.
71+
*
72+
* @param a the dividend
73+
* @param b the divisor
74+
* @return the floor division
75+
*/
76+
internal fun floorDiv(a: Long, b: Long): Long = if (a >= 0) a / b else (a + 1) / b - 1
77+
78+
/**
79+
* Returns the floor modulus.
80+
*
81+
* This returns `0` for `floorMod(0, 4)`.
82+
* This returns `1` for `floorMod(-1, 4)`.
83+
* This returns `2` for `floorMod(-2, 4)`.
84+
* This returns `3` for `floorMod(-3, 4)`.
85+
* This returns `0` for `floorMod(-4, 4)`.
86+
*
87+
* @param a the dividend
88+
* @param b the divisor
89+
* @return the floor modulus (positive)
90+
*/
91+
internal fun floorMod(a: Long, b: Long): Long = (a % b + b) % b

core/jvmMain/src/Instant.kt

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ public actual class Instant internal constructor(internal val value: jtInstant)
7272
if (epochSeconds > 0) MAX else MIN
7373
}
7474

75+
actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant =
76+
fromEpochSeconds(epochSeconds, nanosecondAdjustment.toLong())
77+
7578
actual val DISTANT_PAST: Instant = Instant(jtInstant.ofEpochSecond(DISTANT_PAST_SECONDS, 999_999_999))
7679
actual val DISTANT_FUTURE: Instant = Instant(jtInstant.ofEpochSecond(DISTANT_FUTURE_SECONDS, 0))
7780

core/nativeMain/src/Instant.kt

+3
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ public actual class Instant internal constructor(actual val epochSeconds: Long,
250250
if (epochSeconds > 0) MAX else MIN
251251
}
252252

253+
actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant =
254+
fromEpochSeconds(epochSeconds, nanosecondAdjustment.toLong())
255+
253256
actual fun parse(isoString: String): Instant =
254257
instantParser.parse(isoString)
255258

core/nativeMain/src/mathNative.kt

+24-26
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,13 @@ internal actual fun safeMultiply(a: Int, b: Int): Int {
136136

137137
/**
138138
* Returns the floor division.
139-
* <p>
140-
* This returns {@code 0} for {@code floorDiv(0, 4)}.<br />
141-
* This returns {@code -1} for {@code floorDiv(-1, 4)}.<br />
142-
* This returns {@code -1} for {@code floorDiv(-2, 4)}.<br />
143-
* This returns {@code -1} for {@code floorDiv(-3, 4)}.<br />
144-
* This returns {@code -1} for {@code floorDiv(-4, 4)}.<br />
145-
* This returns {@code -2} for {@code floorDiv(-5, 4)}.<br />
139+
*
140+
* This returns `0` for `floorDiv(0, 4)`.
141+
* This returns `-1` for `floorDiv(-1, 4)`.
142+
* This returns `-1` for `floorDiv(-2, 4)`.
143+
* This returns `-1` for `floorDiv(-3, 4)`.
144+
* This returns `-1` for `floorDiv(-4, 4)`.
145+
* This returns `-2` for `floorDiv(-5, 4)`.
146146
*
147147
* @param a the dividend
148148
* @param b the divisor
@@ -152,13 +152,13 @@ internal fun floorDiv(a: Long, b: Long): Long = if (a >= 0) a / b else (a + 1) /
152152

153153
/**
154154
* Returns the floor division.
155-
* <p>
156-
* This returns {@code 0} for {@code floorDiv(0, 4)}.<br />
157-
* This returns {@code -1} for {@code floorDiv(-1, 4)}.<br />
158-
* This returns {@code -1} for {@code floorDiv(-2, 4)}.<br />
159-
* This returns {@code -1} for {@code floorDiv(-3, 4)}.<br />
160-
* This returns {@code -1} for {@code floorDiv(-4, 4)}.<br />
161-
* This returns {@code -2} for {@code floorDiv(-5, 4)}.<br />
155+
*
156+
* This returns `0` for `floorDiv(0, 4)`.
157+
* This returns `-1` for `floorDiv(-1, 4)`.
158+
* This returns `-1` for `floorDiv(-2, 4)`.
159+
* This returns `-1` for `floorDiv(-3, 4)`.
160+
* This returns `-1` for `floorDiv(-4, 4)`.
161+
* This returns `-2` for `floorDiv(-5, 4)`.
162162
*
163163
* @param a the dividend
164164
* @param b the divisor
@@ -169,12 +169,11 @@ internal fun floorDiv(a: Int, b: Int): Int = if (a >= 0) a / b else (a + 1) / b
169169
/**
170170
* Returns the floor modulus.
171171
*
172-
*
173-
* This returns `0` for `floorMod(0, 4)`.<br></br>
174-
* This returns `1` for `floorMod(-1, 4)`.<br></br>
175-
* This returns `2` for `floorMod(-2, 4)`.<br></br>
176-
* This returns `3` for `floorMod(-3, 4)`.<br></br>
177-
* This returns `0` for `floorMod(-4, 4)`.<br></br>
172+
* This returns `0` for `floorMod(0, 4)`.
173+
* This returns `1` for `floorMod(-1, 4)`.
174+
* This returns `2` for `floorMod(-2, 4)`.
175+
* This returns `3` for `floorMod(-3, 4)`.
176+
* This returns `0` for `floorMod(-4, 4)`.
178177
*
179178
* @param a the dividend
180179
* @param b the divisor
@@ -185,12 +184,11 @@ internal fun floorMod(a: Long, b: Long): Long = (a % b + b) % b
185184
/**
186185
* Returns the floor modulus.
187186
*
188-
*
189-
* This returns `0` for `floorMod(0, 4)`.<br></br>
190-
* This returns `1` for `floorMod(-1, 4)`.<br></br>
191-
* This returns `2` for `floorMod(-2, 4)`.<br></br>
192-
* This returns `3` for `floorMod(-3, 4)`.<br></br>
193-
* This returns `0` for `floorMod(-4, 4)`.<br></br>
187+
* This returns `0` for `floorMod(0, 4)`.
188+
* This returns `1` for `floorMod(-1, 4)`.
189+
* This returns `2` for `floorMod(-2, 4)`.
190+
* This returns `3` for `floorMod(-3, 4)`.
191+
* This returns `0` for `floorMod(-4, 4)`.
194192
*
195193
* @param a the dividend
196194
* @param b the divisor

0 commit comments

Comments
 (0)