Skip to content

Commit

Permalink
Native (iOS): fix crash when getting the current time (#55)
Browse files Browse the repository at this point in the history
Solves #52
  • Loading branch information
dkhalanskyjb authored Sep 22, 2020
1 parent f9f804f commit 0bd3da5
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 12 deletions.
1 change: 0 additions & 1 deletion core/commonTest/src/LocalDateTimeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ class LocalDateTimeTest {
}
}


@OptIn(ExperimentalTime::class)
@Test
fun tomorrow() {
Expand Down
10 changes: 10 additions & 0 deletions core/nativeMain/cinterop/cpp/apple.mm
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ static TZID id_by_name(NSString *zone_name)

extern "C" {

bool current_time(int64_t *sec, int32_t *nano)
{
double current = NSDate.date.timeIntervalSince1970;
double dsec;
double dnano = modf(current, &dsec);
*sec = (int64_t)dsec;
*nano = (int32_t)(dnano * 1000000000);
return true;
}

char * get_system_timezone(TZID *tzid)
{
/* The framework has its own cache of the system timezone. Calls to
Expand Down
12 changes: 12 additions & 0 deletions core/nativeMain/cinterop/cpp/cdate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ static TZID id_by_zone(const tzdb& db, const time_zone* tz)

extern "C" {

bool current_time(int64_t *sec, int32_t *nano)
{
timespec tm;
int error = clock_gettime(CLOCK_REALTIME, &tm);
if (error) {
return false;
}
*sec = tm.tv_sec;
*nano = tm.tv_nsec;
return true;
}

char * get_system_timezone(TZID * id)
{
try {
Expand Down
12 changes: 12 additions & 0 deletions core/nativeMain/cinterop/cpp/windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,18 @@ static int offset_at_systime(DYNAMIC_TIME_ZONE_INFORMATION& dtzi,

extern "C" {

bool current_time(int64_t *sec, int32_t *nano)
{
timespec tm;
int error = clock_gettime(CLOCK_REALTIME, &tm);
if (error) {
return false;
}
*sec = tm.tv_sec;
*nano = tm.tv_nsec;
return true;
}

char * get_system_timezone(TZID* id)
{
DYNAMIC_TIME_ZONE_INFORMATION dtzi{};
Expand Down
4 changes: 4 additions & 0 deletions core/nativeMain/cinterop/public/cdate.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>

typedef size_t TZID;
const TZID TZID_INVALID = SIZE_MAX;
Expand All @@ -15,6 +16,9 @@ enum GAP_HANDLING {
GAP_HANDLING_NEXT_CORRECT,
};

// Returns true if successful.
bool current_time(int64_t *sec, int32_t *nano);

/* Returns a string that must be freed by the caller, or null.
If something is returned, `id` has the id of the timezone. */
char * get_system_timezone(TZID* id);
Expand Down
3 changes: 3 additions & 0 deletions core/nativeMain/cinterop_actuals/TimeZoneNative.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ internal actual fun getCurrentSystemDefaultTimeZone(): TimeZone = memScoped {
TimeZone(tzid.value, kotlinString)
}

internal actual fun current_time(sec: kotlinx.cinterop.CValuesRef<platform.posix.int64_tVar /* = kotlinx.cinterop.LongVarOf<kotlin.Long> */>?, nano: kotlinx.cinterop.CValuesRef<platform.posix.int32_tVar>?): kotlin.Boolean =
kotlinx.datetime.internal.current_time(sec, nano)

internal actual fun available_zone_ids(): kotlinx.cinterop.CPointer<kotlinx.cinterop.CPointerVar<kotlinx.cinterop.ByteVar>>? =
kotlinx.datetime.internal.available_zone_ids()

Expand Down
14 changes: 6 additions & 8 deletions core/nativeMain/src/Instant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,13 @@ public actual class Instant internal constructor(actual val epochSeconds: Long,

@Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR)
actual fun now(): Instant = memScoped {
val timespecBuf = alloc<timespec>()
val error = clock_gettime(CLOCK_REALTIME, timespecBuf.ptr)
assert(error == 0)
// according to https://en.cppreference.com/w/c/chrono/timespec,
// tv_nsec in [0; 10^9), so no need to call [ofEpochSecond].
val seconds = timespecBuf.tv_sec.convert<Long>()
val nanosec = timespecBuf.tv_nsec.toInt()
val seconds = alloc<LongVar>()
val nanoseconds = alloc<IntVar>()
val result = current_time(seconds.ptr, nanoseconds.ptr)
try {
Instant(seconds, nanosec)
require(result)
require(nanoseconds.value >= 0 && nanoseconds.value < NANOS_PER_ONE)
Instant(seconds.value, nanoseconds.value)
} catch (e: IllegalArgumentException) {
throw IllegalStateException("The readings from the system clock are not representable as an Instant")
}
Expand Down
7 changes: 4 additions & 3 deletions core/nativeMain/src/TimeZone.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal expect fun offset_at_datetime(zone: kotlinx.datetime.TZID /* = kotlin.U
internal expect fun at_start_of_day(zone: kotlinx.datetime.TZID /* = kotlin.ULong */, epoch_sec: platform.posix.int64_t /* = kotlin.Long */): kotlin.Long
internal expect fun offset_at_instant(zone: kotlinx.datetime.TZID /* = kotlin.ULong */, epoch_sec: platform.posix.int64_t /* = kotlin.Long */): kotlin.Int
internal expect fun timezone_by_name(zone_name: kotlin.String?): kotlinx.datetime.TZID /* = kotlin.ULong */
internal expect fun current_time(sec: kotlinx.cinterop.CValuesRef<platform.posix.int64_tVar /* = kotlinx.cinterop.LongVarOf<kotlin.Long> */>?, nano: kotlinx.cinterop.CValuesRef<platform.posix.int32_tVar>?): kotlin.Boolean

public actual open class TimeZone internal constructor(private val tzid: TZID, actual val id: String) {

Expand Down Expand Up @@ -92,7 +93,7 @@ public actual open class TimeZone internal constructor(private val tzid: TZID, a

internal open fun offsetAtImpl(instant: Instant): ZoneOffset {
val offset = offset_at_instant(tzid, instant.epochSeconds)
if (offset == INT_MAX) {
if (offset == Int.MAX_VALUE) {
throw RuntimeException("Unable to acquire the offset at instant $instant for zone $this")
}
return ZoneOffset.ofSeconds(offset)
Expand All @@ -113,9 +114,9 @@ public actual open class TimeZone internal constructor(private val tzid: TZID, a
internal open fun LocalDateTime.atZone(preferred: ZoneOffset? = null): ZonedDateTime = memScoped {
val epochSeconds = toEpochSecond(ZoneOffset.UTC)
val offset = alloc<IntVar>()
offset.value = preferred?.totalSeconds ?: INT_MAX
offset.value = preferred?.totalSeconds ?: Int.MAX_VALUE
val transitionDuration = offset_at_datetime(tzid, epochSeconds, offset.ptr)
if (offset.value == INT_MAX) {
if (offset.value == Int.MAX_VALUE) {
throw RuntimeException("Unable to acquire the offset at ${this@atZone} for zone ${this@TimeZone}")
}
val dateTime = try {
Expand Down

0 comments on commit 0bd3da5

Please sign in to comment.