Skip to content

Commit

Permalink
Use millisecond precision for NSDate conversion
Browse files Browse the repository at this point in the history
Fixes tests, since CBL only stores milliseconds, but kotlinx-datetime now supports higher precision NSDate conversion
  • Loading branch information
jeffdgr8 committed Feb 17, 2025
1 parent 1fec391 commit 399b594
Show file tree
Hide file tree
Showing 11 changed files with 87 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ package kotbase
import cocoapods.CouchbaseLite.CBLTLSIdentity
import kotbase.internal.DelegatedClass
import kotbase.ext.toByteArray
import kotbase.ext.toKotlinInstantMillis
import kotbase.ext.toNSData
import kotbase.ext.wrapCBLError
import kotlinx.datetime.Instant
import kotlinx.datetime.toKotlinInstant
import kotlinx.datetime.toNSDate
import platform.Security.SecCertificateRef
import platform.Security.SecIdentityRef
Expand All @@ -34,7 +34,7 @@ internal constructor(actual: CBLTLSIdentity) : DelegatedClass<CBLTLSIdentity>(ac
get() = (actual.certs as List<SecCertificateRef>).map { it.toByteArray() }

public actual val expiration: Instant
get() = actual.expiration.toKotlinInstant()
get() = actual.expiration.toKotlinInstantMillis()

public actual companion object {

Expand Down
4 changes: 2 additions & 2 deletions couchbase-lite/src/appleMain/kotlin/kotbase/Array.apple.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ package kotbase

import cocoapods.CouchbaseLite.CBLArray
import kotbase.ext.asNumber
import kotbase.ext.toKotlinInstantMillis
import kotbase.internal.DelegatedClass
import kotlinx.cinterop.convert
import kotlinx.datetime.Instant
import kotlinx.datetime.toKotlinInstant

public actual open class Array
internal constructor(actual: CBLArray) : DelegatedClass<CBLArray>(actual), Iterable<Any?> {
Expand Down Expand Up @@ -82,7 +82,7 @@ internal constructor(actual: CBLArray) : DelegatedClass<CBLArray>(actual), Itera

public actual fun getDate(index: Int): Instant? {
checkIndex(index)
return actual.dateAtIndex(index.convert())?.toKotlinInstant()
return actual.dateAtIndex(index.convert())?.toKotlinInstantMillis()
}

public actual open fun getArray(index: Int): Array? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ package kotbase

import cocoapods.CouchbaseLite.CBLCollection
import kotbase.ext.asDispatchQueue
import kotbase.ext.toKotlinInstantMillis
import kotbase.ext.wrapCBLError
import kotbase.internal.DelegatedClass
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.datetime.Instant
import kotlinx.datetime.toKotlinInstant
import kotlinx.datetime.toNSDate
import kotlin.coroutines.CoroutineContext
import kotlin.experimental.ExperimentalObjCRefinement
Expand Down Expand Up @@ -150,7 +150,7 @@ internal constructor(
public actual fun getDocumentExpiration(id: String): Instant? {
return wrapCBLError { error ->
actual.getDocumentExpirationWithID(id, error)
}?.toKotlinInstant()
}?.toKotlinInstantMillis()
}

public actual fun addChangeListener(listener: CollectionChangeListener): ListenerToken {
Expand Down
4 changes: 2 additions & 2 deletions couchbase-lite/src/appleMain/kotlin/kotbase/Database.apple.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package kotbase
import cocoapods.CouchbaseLite.*
import kotbase.internal.DelegatedClass
import kotbase.ext.asDispatchQueue
import kotbase.ext.toKotlinInstantMillis
import kotbase.ext.wrapCBLError
import kotlinx.atomicfu.locks.reentrantLock
import kotlinx.atomicfu.locks.withLock
Expand All @@ -26,7 +27,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.datetime.Instant
import kotlinx.datetime.toKotlinInstant
import kotlinx.datetime.toNSDate
import kotlin.coroutines.CoroutineContext
import kotlin.experimental.ExperimentalObjCRefinement
Expand Down Expand Up @@ -430,7 +430,7 @@ internal constructor(actual: CBLDatabase) : DelegatedClass<CBLDatabase>(actual),
@Throws(CouchbaseLiteException::class)
public actual fun getDocumentExpiration(id: String): Instant? {
return mustBeOpen {
actual.getDocumentExpirationWithID(id)?.toKotlinInstant()
actual.getDocumentExpirationWithID(id)?.toKotlinInstantMillis()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ package kotbase

import cocoapods.CouchbaseLite.CBLDictionary
import kotbase.ext.asNumber
import kotbase.ext.toKotlinInstantMillis
import kotbase.internal.DelegatedClass
import kotlinx.datetime.Instant
import kotlinx.datetime.toKotlinInstant

public actual open class Dictionary
internal constructor(actual: CBLDictionary) : DelegatedClass<CBLDictionary>(actual), Iterable<String> {
Expand Down Expand Up @@ -67,7 +67,7 @@ internal constructor(actual: CBLDictionary) : DelegatedClass<CBLDictionary>(actu
actual.blobForKey(key)?.asBlob()

public actual fun getDate(key: String): Instant? =
actual.dateForKey(key)?.toKotlinInstant()
actual.dateForKey(key)?.toKotlinInstantMillis()

public actual open fun getArray(key: String): Array? {
return getInternalCollection(key)
Expand Down
4 changes: 2 additions & 2 deletions couchbase-lite/src/appleMain/kotlin/kotbase/Document.apple.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ package kotbase
import cocoapods.CouchbaseLite.CBLDocument
import com.couchbase.lite.database
import kotbase.ext.asNumber
import kotbase.ext.toKotlinInstantMillis
import kotbase.internal.DelegatedClass
import kotlinx.datetime.Instant
import kotlinx.datetime.toKotlinInstant

public actual open class Document
internal constructor(
Expand Down Expand Up @@ -90,7 +90,7 @@ internal constructor(
actual.blobForKey(key)?.asBlob()

public actual fun getDate(key: String): Instant? =
actual.dateForKey(key)?.toKotlinInstant()
actual.dateForKey(key)?.toKotlinInstantMillis()

public actual open fun getArray(key: String): Array? {
return getInternalCollection(key)
Expand Down
6 changes: 3 additions & 3 deletions couchbase-lite/src/appleMain/kotlin/kotbase/Result.apple.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ package kotbase
import cocoapods.CouchbaseLite.CBLQueryResult
import kotbase.internal.DelegatedClass
import kotbase.ext.asNumber
import kotbase.ext.toKotlinInstantMillis
import kotlinx.cinterop.convert
import kotlinx.datetime.Instant
import kotlinx.datetime.toKotlinInstant

public actual class Result
internal constructor(actual: CBLQueryResult) : DelegatedClass<CBLQueryResult>(actual), Iterable<String> {
Expand Down Expand Up @@ -75,7 +75,7 @@ internal constructor(actual: CBLQueryResult) : DelegatedClass<CBLQueryResult>(ac

public actual fun getDate(index: Int): Instant? {
assertInBounds(index)
return actual.dateAtIndex(index.convert())?.toKotlinInstant()
return actual.dateAtIndex(index.convert())?.toKotlinInstantMillis()
}

public actual fun getArray(index: Int): Array? {
Expand Down Expand Up @@ -123,7 +123,7 @@ internal constructor(actual: CBLQueryResult) : DelegatedClass<CBLQueryResult>(ac
actual.blobForKey(key)?.asBlob()

public actual fun getDate(key: String): Instant? =
actual.dateForKey(key)?.toKotlinInstant()
actual.dateForKey(key)?.toKotlinInstantMillis()

public actual fun getArray(key: String): Array? =
actual.arrayForKey(key)?.asArray()
Expand Down
4 changes: 2 additions & 2 deletions couchbase-lite/src/appleMain/kotlin/kotbase/Utils.apple.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
package kotbase

import cocoapods.CouchbaseLite.*
import kotbase.ext.toKotlinInstantMillis
import kotbase.internal.DelegatedClass
import kotlinx.datetime.Instant
import kotlinx.datetime.toKotlinInstant
import kotlinx.datetime.toNSDate
import platform.Foundation.NSDate
import platform.Foundation.NSNull
Expand All @@ -30,7 +30,7 @@ internal fun Any.delegateIfNecessary(): Any? = when (this) {
is CBLArray -> asArray()
is CBLMutableDictionary -> asMutableDictionary()
is CBLDictionary -> asDictionary()
is NSDate -> toKotlinInstant()
is NSDate -> toKotlinInstantMillis()
is List<*> -> delegateIfNecessary()
is Map<*, *> -> delegateIfNecessary()
else -> this
Expand Down
28 changes: 28 additions & 0 deletions couchbase-lite/src/appleMain/kotlin/kotbase/ext/DateExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2025 Jeff Lockhart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotbase.ext

import kotlinx.datetime.Instant
import platform.Foundation.NSDate
import platform.Foundation.timeIntervalSince1970
import kotlin.math.roundToLong

internal fun NSDate.toKotlinInstantMillis(): Instant {
val secs = timeIntervalSince1970()
val fullSeconds = secs.toLong()
val millis = (secs - fullSeconds) * MILLIS_PER_ONE
return Instant.fromEpochMilliseconds(fullSeconds * MILLIS_PER_ONE + millis.roundToLong())
}
32 changes: 32 additions & 0 deletions couchbase-lite/src/appleTest/kotlin/kotbase/ext/DateExtTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2025 Jeff Lockhart
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package kotbase.ext

import kotlinx.datetime.Clock
import kotlinx.datetime.toNSDate
import kotlin.test.Test
import kotlin.test.assertEquals

class DateExtTest {

@Test
fun testToKotlinInstantMillis() {
val now = Clock.System.nowMillis()
val nsDate = now.toNSDate()
val result = nsDate.toKotlinInstantMillis()
assertEquals(now, result)
}
}
11 changes: 10 additions & 1 deletion couchbase-lite/src/commonMain/kotlin/kotbase/ext/DateExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
package kotbase.ext

import kotlinx.datetime.Instant
import kotlin.math.roundToLong

/**
* An ISO 8601 UTC string with milliseconds
* precision. The default [Instant.toString]
* can be seconds or nanoseconds precision.
*/
internal fun Instant.toStringMillis(): String {
return toString().let {
return roundToMillis().toString().let {
if (it.length == 20) {
it.dropLast(1) + ".000Z"
} else if (it.length > 24) {
Expand All @@ -33,3 +34,11 @@ internal fun Instant.toStringMillis(): String {
}
}
}

internal const val NANOS_PER_MILLI = 1_000_000
internal const val MILLIS_PER_ONE = 1_000

internal fun Instant.roundToMillis(): Instant {
val millis = nanosecondsOfSecond.toFloat() / NANOS_PER_MILLI
return Instant.fromEpochMilliseconds(epochSeconds * MILLIS_PER_ONE + millis.roundToLong())
}

0 comments on commit 399b594

Please sign in to comment.