Skip to content

Commit afb8e91

Browse files
committed
Native (iOS): fix crash when getting the current time
Solves #52
1 parent f9f804f commit afb8e91

File tree

8 files changed

+51
-12
lines changed

8 files changed

+51
-12
lines changed

core/commonTest/src/LocalDateTimeTest.kt

-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ class LocalDateTimeTest {
7272
}
7373
}
7474

75-
7675
@OptIn(ExperimentalTime::class)
7776
@Test
7877
fun tomorrow() {

core/nativeMain/cinterop/cpp/apple.mm

+10
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,16 @@ static TZID id_by_name(NSString *zone_name)
8181

8282
extern "C" {
8383

84+
bool current_time(int64_t *sec, int32_t *nano)
85+
{
86+
double current = NSDate.date.timeIntervalSince1970;
87+
double dsec;
88+
double dnano = modf(current, &dsec);
89+
*sec = (int64_t)dsec;
90+
*nano = (int32_t)(dnano * 1000000000);
91+
return true;
92+
}
93+
8494
char * get_system_timezone(TZID *tzid)
8595
{
8696
/* The framework has its own cache of the system timezone. Calls to

core/nativeMain/cinterop/cpp/cdate.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,18 @@ static TZID id_by_zone(const tzdb& db, const time_zone* tz)
8181

8282
extern "C" {
8383

84+
bool current_time(int64_t *sec, int32_t *nano)
85+
{
86+
timespec tm;
87+
int error = clock_gettime(CLOCK_REALTIME, &tm);
88+
if (error) {
89+
return false;
90+
}
91+
*sec = tm.tv_sec;
92+
*nano = tm.tv_nsec;
93+
return true;
94+
}
95+
8496
char * get_system_timezone(TZID * id)
8597
{
8698
try {

core/nativeMain/cinterop/cpp/windows.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,18 @@ static int offset_at_systime(DYNAMIC_TIME_ZONE_INFORMATION& dtzi,
281281

282282
extern "C" {
283283

284+
bool current_time(int64_t *sec, int32_t *nano)
285+
{
286+
timespec tm;
287+
int error = clock_gettime(CLOCK_REALTIME, &tm);
288+
if (error) {
289+
return false;
290+
}
291+
*sec = tm.tv_sec;
292+
*nano = tm.tv_nsec;
293+
return true;
294+
}
295+
284296
char * get_system_timezone(TZID* id)
285297
{
286298
DYNAMIC_TIME_ZONE_INFORMATION dtzi{};

core/nativeMain/cinterop/public/cdate.h

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#pragma once
77
#include <stdint.h>
88
#include <stddef.h>
9+
#include <stdbool.h>
910

1011
typedef size_t TZID;
1112
const TZID TZID_INVALID = SIZE_MAX;
@@ -15,6 +16,9 @@ enum GAP_HANDLING {
1516
GAP_HANDLING_NEXT_CORRECT,
1617
};
1718

19+
// Returns true if successful.
20+
bool current_time(int64_t *sec, int32_t *nano);
21+
1822
/* Returns a string that must be freed by the caller, or null.
1923
If something is returned, `id` has the id of the timezone. */
2024
char * get_system_timezone(TZID* id);

core/nativeMain/cinterop_actuals/TimeZoneNative.kt

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ internal actual fun getCurrentSystemDefaultTimeZone(): TimeZone = memScoped {
1616
TimeZone(tzid.value, kotlinString)
1717
}
1818

19+
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 =
20+
kotlinx.datetime.internal.current_time(sec, nano)
21+
1922
internal actual fun available_zone_ids(): kotlinx.cinterop.CPointer<kotlinx.cinterop.CPointerVar<kotlinx.cinterop.ByteVar>>? =
2023
kotlinx.datetime.internal.available_zone_ids()
2124

core/nativeMain/src/Instant.kt

+6-8
Original file line numberDiff line numberDiff line change
@@ -209,15 +209,13 @@ public actual class Instant internal constructor(actual val epochSeconds: Long,
209209

210210
@Deprecated("Use Clock.System.now() instead", ReplaceWith("Clock.System.now()", "kotlinx.datetime.Clock"), level = DeprecationLevel.ERROR)
211211
actual fun now(): Instant = memScoped {
212-
val timespecBuf = alloc<timespec>()
213-
val error = clock_gettime(CLOCK_REALTIME, timespecBuf.ptr)
214-
assert(error == 0)
215-
// according to https://en.cppreference.com/w/c/chrono/timespec,
216-
// tv_nsec in [0; 10^9), so no need to call [ofEpochSecond].
217-
val seconds = timespecBuf.tv_sec.convert<Long>()
218-
val nanosec = timespecBuf.tv_nsec.toInt()
212+
val seconds = alloc<LongVar>()
213+
val nanoseconds = alloc<IntVar>()
214+
val result = current_time(seconds.ptr, nanoseconds.ptr)
215+
require(result)
219216
try {
220-
Instant(seconds, nanosec)
217+
require(nanoseconds.value >= 0 && nanoseconds.value < NANOS_PER_ONE)
218+
Instant(seconds.value, nanoseconds.value)
221219
} catch (e: IllegalArgumentException) {
222220
throw IllegalStateException("The readings from the system clock are not representable as an Instant")
223221
}

core/nativeMain/src/TimeZone.kt

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ internal expect fun offset_at_datetime(zone: kotlinx.datetime.TZID /* = kotlin.U
2222
internal expect fun at_start_of_day(zone: kotlinx.datetime.TZID /* = kotlin.ULong */, epoch_sec: platform.posix.int64_t /* = kotlin.Long */): kotlin.Long
2323
internal expect fun offset_at_instant(zone: kotlinx.datetime.TZID /* = kotlin.ULong */, epoch_sec: platform.posix.int64_t /* = kotlin.Long */): kotlin.Int
2424
internal expect fun timezone_by_name(zone_name: kotlin.String?): kotlinx.datetime.TZID /* = kotlin.ULong */
25+
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
2526

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

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

9394
internal open fun offsetAtImpl(instant: Instant): ZoneOffset {
9495
val offset = offset_at_instant(tzid, instant.epochSeconds)
95-
if (offset == INT_MAX) {
96+
if (offset == Int.MAX_VALUE) {
9697
throw RuntimeException("Unable to acquire the offset at instant $instant for zone $this")
9798
}
9899
return ZoneOffset.ofSeconds(offset)
@@ -113,9 +114,9 @@ public actual open class TimeZone internal constructor(private val tzid: TZID, a
113114
internal open fun LocalDateTime.atZone(preferred: ZoneOffset? = null): ZonedDateTime = memScoped {
114115
val epochSeconds = toEpochSecond(ZoneOffset.UTC)
115116
val offset = alloc<IntVar>()
116-
offset.value = preferred?.totalSeconds ?: INT_MAX
117+
offset.value = preferred?.totalSeconds ?: Int.MAX_VALUE
117118
val transitionDuration = offset_at_datetime(tzid, epochSeconds, offset.ptr)
118-
if (offset.value == INT_MAX) {
119+
if (offset.value == Int.MAX_VALUE) {
119120
throw RuntimeException("Unable to acquire the offset at ${this@atZone} for zone ${this@TimeZone}")
120121
}
121122
val dateTime = try {

0 commit comments

Comments
 (0)