Skip to content

Commit

Permalink
Remove kotlinx.datetime from JS target (#316)
Browse files Browse the repository at this point in the history
Add JS actual implementation
  • Loading branch information
wkal-pubnub authored Nov 28, 2024
1 parent 986142b commit e117611
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 22 deletions.
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ logback = "1.2.11"
okhttp = "4.12.0"
retrofit2 = "2.9.0"
nexus = "2.0.0"
kotlin = "2.0.20"
kotlin = "2.0.21"
vanniktech = "0.29.0"
ktlint = "12.1.0"
dokka = "1.9.20"
kotlinx_datetime = "0.6.0"
kotlinx_datetime = "0.6.1"
kotlinx_coroutines = "1.8.1"

[libraries]
Expand Down
5 changes: 0 additions & 5 deletions kotlin-js-store/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@
# yarn lockfile v1


"@js-joda/[email protected]":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273"
integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg==

"@tootallnate/quickjs-emscripten@^0.23.0":
version "0.23.0"
resolved "https://registry.yarnpkg.com/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz#db4ecfd499a9765ab24002c3b696d02e6d32a12c"
Expand Down
18 changes: 18 additions & 0 deletions pubnub-kotlin/pubnub-kotlin-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import com.pubnub.gradle.enableAnyIosTarget
import com.pubnub.gradle.enableJsTarget

plugins {
Expand All @@ -17,7 +18,15 @@ kotlin {
}
}

val nonJs = create("nonJs") {
dependsOn(commonMain)
dependencies {
api(libs.kotlinx.datetime)
}
}

val jvmMain by getting {
dependsOn(nonJs)
dependencies {
api(libs.retrofit2)
api(libs.okhttp)
Expand All @@ -28,6 +37,15 @@ kotlin {
}
}

if (enableAnyIosTarget) {
val appleMain by getting {
dependsOn(nonJs)
dependencies {
api(libs.kotlinx.datetime)
}
}
}

if (enableJsTarget) {
val jsMain by getting {
dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.pubnub.api.utils

import kotlinx.datetime.Instant

/**
* Utility object for converting PubNub timetokens to various date-time representations and vice versa.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.pubnub.api.utils

import kotlin.time.Duration

expect class Instant : Comparable<Instant> {
val epochSeconds: Long
val nanosecondsOfSecond: Int

fun toEpochMilliseconds(): Long

operator fun plus(duration: Duration): Instant

operator fun minus(duration: Duration): Instant

operator fun minus(other: Instant): Duration

override operator fun compareTo(other: Instant): Int

companion object {
fun fromEpochMilliseconds(epochMilliseconds: Long): Instant

fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant
}
}

expect interface Clock {
fun now(): Instant

object System : Clock {
override fun now(): Instant
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.pubnub.api.util

import com.pubnub.api.utils.Instant
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.Duration.Companion.seconds

class InstantTest {
@Test
fun plusDuration() {
val nowMillis = 1732793616984
val now = Instant.fromEpochMilliseconds(nowMillis)
val later = now + 1500.milliseconds

assertEquals(nowMillis + 1500, later.toEpochMilliseconds())

val laterWithNanos = now + 2000.nanoseconds
assertEquals(now.epochSeconds, laterWithNanos.epochSeconds)
assertEquals(now.nanosecondsOfSecond + 2000, laterWithNanos.nanosecondsOfSecond)

val laterWithSecondsAndNanos = now + (1.seconds + 2000.nanoseconds)
assertEquals(now.epochSeconds + 1, laterWithSecondsAndNanos.epochSeconds)
assertEquals(now.nanosecondsOfSecond + 2000, laterWithSecondsAndNanos.nanosecondsOfSecond)
}

@Test
fun minusDuration() {
val nowMillis = 1732793616984
val now = Instant.fromEpochMilliseconds(nowMillis)
val later = now - 1500.milliseconds

assertEquals(nowMillis - 1500, later.toEpochMilliseconds())

val laterWithNanos = now - 2000.nanoseconds
assertEquals(now.epochSeconds, laterWithNanos.epochSeconds)
assertEquals(now.nanosecondsOfSecond - 2000, laterWithNanos.nanosecondsOfSecond)

val laterWithSecondsAndNanos = now - (1.seconds + 2000.nanoseconds)
assertEquals(now.epochSeconds - 1, laterWithSecondsAndNanos.epochSeconds)
assertEquals(now.nanosecondsOfSecond - 2000, laterWithSecondsAndNanos.nanosecondsOfSecond)
}

@Test
fun minusInstant() {
val nowMillis = 1732793616984
val laterMillis = 1732793616984 + 1500
val now = Instant.fromEpochMilliseconds(nowMillis)
val later = Instant.fromEpochMilliseconds(laterMillis)

assertEquals(1500.milliseconds, later - now)
assertEquals(-1500.milliseconds, now - later)
}

@Test
fun compareTo() {
val nowMillis = 1732793616984
val laterMillis = 1732793616984 + 1500
val now = Instant.fromEpochMilliseconds(nowMillis)
val later = Instant.fromEpochMilliseconds(laterMillis)

assertTrue(now < later)
assertTrue(later > now)

assertTrue(now + 100.nanoseconds > now)
assertTrue(now - 100.nanoseconds < now)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.pubnub.api.util

import com.pubnub.api.utils.Instant
import com.pubnub.api.utils.TimetokenUtil
import kotlinx.datetime.Instant
import kotlinx.datetime.TimeZone
import kotlinx.datetime.toLocalDateTime
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
Expand All @@ -19,9 +17,6 @@ class TimetokenUtilsTest {
val timetokenResult = TimetokenUtil.instantToTimetoken(instant)

// then
val localDateTimeInUTC = instant.toLocalDateTime(TimeZone.UTC)
assertEquals("2024-09-30", localDateTimeInUTC.date.toString())
assertEquals("11:24:20.623211800", localDateTimeInUTC.time.toString())
assertEquals(timetoken, timetokenResult)
}

Expand All @@ -35,9 +30,7 @@ class TimetokenUtilsTest {

// then
val instant: Instant = TimetokenUtil.timetokenToInstant(timetoken)
val localDateTimeInUTC = instant.toLocalDateTime(TimeZone.UTC)
assertEquals("2024-10-02", localDateTimeInUTC.date.toString())
assertEquals("11:02:15.316", localDateTimeInUTC.time.toString())
assertEquals(unixTime, instant.toEpochMilliseconds())
}

@Test
Expand All @@ -59,8 +52,6 @@ class TimetokenUtilsTest {

// then
val instant = Instant.fromEpochMilliseconds(unixTime)
val toLocalDateTime = instant.toLocalDateTime(TimeZone.UTC)
assertEquals("2024-09-30", toLocalDateTime.date.toString())
assertEquals("11:24:20.623", toLocalDateTime.time.toString())
assertEquals(timetoken / 10000 * 10000, TimetokenUtil.instantToTimetoken(instant))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ package com.pubnub.test.integration
import com.pubnub.test.BaseIntegrationTest
import com.pubnub.test.await
import com.pubnub.test.randomString
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withContext
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.seconds

class MessageCountsTest : BaseIntegrationTest() {
private val channel = randomString()
Expand Down Expand Up @@ -35,6 +39,9 @@ class MessageCountsTest : BaseIntegrationTest() {
).await().timetoken
}

withContext(Dispatchers.Default) {
delay(2.seconds)
}
val counts = pubnub.messageCounts(
listOf(channel, otherChannel),
listOf(timetokens.first() - 1, timetokensOther.first() - 1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.pubnub.api.utils

import kotlin.js.Date
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.Duration.Companion.seconds

actual class Instant(
actual val epochSeconds: Long,
actual val nanosecondsOfSecond: Int = 0,
) : Comparable<Instant> {
actual fun toEpochMilliseconds(): Long {
return epochSeconds.seconds.inWholeMilliseconds + nanosecondsOfSecond.nanoseconds.inWholeMilliseconds
}

actual operator fun plus(duration: Duration): Instant {
val durationWholeSecondsOnly = duration.inWholeSeconds.seconds
val durationNanosOnly = duration - durationWholeSecondsOnly
val sum = add(epochSeconds to nanosecondsOfSecond, duration.inWholeSeconds to durationNanosOnly.inWholeNanoseconds.toInt())
return Instant(sum.first, sum.second)
}

actual operator fun minus(duration: Duration): Instant {
return plus(-duration)
}

actual operator fun minus(other: Instant): Duration {
return epochSeconds.seconds + nanosecondsOfSecond.nanoseconds - other.epochSeconds.seconds - other.nanosecondsOfSecond.nanoseconds
}

actual override operator fun compareTo(other: Instant): Int {
return epochSeconds.compareTo(other.epochSeconds)
.takeIf { it != 0 }
?: nanosecondsOfSecond.compareTo(other.nanosecondsOfSecond)
}

private fun add(secondsAndNanos: SecondsAndNanos, secondsAndNanos2: SecondsAndNanos): Pair<Long, Int> {
val nanosSum = secondsAndNanos.nanos + secondsAndNanos2.nanos
val secondsFromNanos = nanosSum.inWholeSeconds.seconds

val secondsResult = secondsAndNanos.seconds + secondsAndNanos2.seconds + secondsFromNanos
val nanosResult = nanosSum - secondsFromNanos
return secondsResult.inWholeSeconds to nanosResult.inWholeNanoseconds.toInt()
}

actual companion object {
actual fun fromEpochMilliseconds(epochMilliseconds: Long): Instant {
val wholeSeconds = epochMilliseconds.milliseconds.inWholeSeconds
val nanos = (epochMilliseconds.milliseconds - wholeSeconds.seconds).inWholeNanoseconds
return Instant(wholeSeconds, nanos.toInt())
}

actual fun fromEpochSeconds(epochSeconds: Long, nanosecondAdjustment: Int): Instant {
return Instant(epochSeconds, nanosecondAdjustment)
}
}
}

actual interface Clock {
actual fun now(): Instant

actual object System : Clock {
actual override fun now(): Instant {
return Instant.fromEpochMilliseconds(Date.now().toLong())
}
}
}

typealias SecondsAndNanos = Pair<Long, Int>

val SecondsAndNanos.seconds get() = this.first.seconds
val SecondsAndNanos.nanos get() = this.second.nanoseconds
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.pubnub.api.utils

import kotlinx.datetime.Clock
import kotlinx.datetime.Instant

actual typealias Instant = Instant

actual interface Clock {
actual fun now(): com.pubnub.api.utils.Instant

actual object System : com.pubnub.api.utils.Clock {
actual override fun now(): com.pubnub.api.utils.Instant {
return Clock.System.now()
}
}
}
1 change: 0 additions & 1 deletion pubnub-kotlin/pubnub-kotlin-core-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ kotlin {
val commonMain by getting {
dependencies {
implementation(libs.kotlinx.atomicfu)
api(libs.kotlinx.datetime)
}
}

Expand Down

0 comments on commit e117611

Please sign in to comment.