Skip to content

Commit a435b41

Browse files
authored
Native (iOS): Fix availableZoneIds having non-available ids (#4)
There were actually two bugs: * First, contrary to the expectations, `timeZoneWithName` does not resolve timezone name abbreviations on its own. They should be checked manually. * Second, contrary to the expectations, `abbreviationDictionary` has abbreviations for timezones that are not even available! Both are fixed.
1 parent c3bd812 commit a435b41

File tree

4 files changed

+39
-23
lines changed

4 files changed

+39
-23
lines changed

core/commonTest/src/TimeZoneTest.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,13 @@ class TimeZoneTest {
3535
assertTrue("UTC" in allTzIds)
3636
}
3737

38+
@Test
39+
fun availableZonesAreAvailable() {
40+
for (zoneName in TimeZone.availableZoneIds) {
41+
TimeZone.of(zoneName)
42+
}
43+
}
44+
3845
@Test
3946
fun of() {
4047
val tzm = TimeZone.of("Europe/Moscow")

core/nativeMain/cinterop/cpp/apple.mm

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,21 @@
1212
#import <Foundation/NSDate.h>
1313
#import <Foundation/NSCalendar.h>
1414
#import <limits.h>
15+
#import <set>
16+
#import <string>
1517
#include "helper_macros.hpp"
1618

19+
static NSTimeZone * zone_by_name(NSString *zone_name)
20+
{
21+
auto abbreviations = NSTimeZone.abbreviationDictionary;
22+
auto true_name = [abbreviations valueForKey: zone_name];
23+
NSString *name = zone_name;
24+
if (true_name != nil) {
25+
name = true_name;
26+
}
27+
return [NSTimeZone timeZoneWithName: name];
28+
}
29+
1730
extern "C" {
1831
#include "cdate.h"
1932

@@ -40,52 +53,49 @@
4053

4154
char ** available_zone_ids()
4255
{
56+
std::set<std::string> ids;
4357
auto zones = NSTimeZone.knownTimeZoneNames;
44-
auto abbrevs = NSTimeZone.abbreviationDictionary.allKeys;
58+
for (NSString * zone in zones) {
59+
ids.insert(std::string([zone UTF8String]));
60+
}
61+
auto abbrevs = NSTimeZone.abbreviationDictionary;
62+
for (NSString * key in abbrevs) {
63+
if (ids.count(std::string([abbrevs[key] UTF8String]))) {
64+
ids.insert(std::string([key UTF8String]));
65+
}
66+
}
4567
char ** zones_copy = (char **)malloc(
46-
sizeof(char *) * (zones.count + abbrevs.count + 1));
68+
sizeof(char *) * (ids.size() + 1));
4769
if (zones_copy == nullptr) {
4870
return nullptr;
4971
}
50-
zones_copy[zones.count + abbrevs.count] = nullptr;
51-
unsigned long idx = 0;
52-
for (unsigned long i = 0; i < zones.count; ++i) {
53-
idx = i;
54-
CFIndex bufferSize = zones[i].length + 1;
55-
char * buffer = (char *)malloc(bufferSize);
56-
PUSH_BACK_OR_RETURN(zones_copy, idx, buffer);
57-
strncpy(buffer, zones[i].UTF8String, bufferSize);
58-
}
59-
for (unsigned long i = 0; i < abbrevs.count; ++i) {
60-
idx = zones.count + i;
61-
CFIndex bufferSize = abbrevs[i].length + 1;
62-
char * buffer = (char *)malloc(bufferSize);
63-
PUSH_BACK_OR_RETURN(zones_copy, idx, buffer);
64-
strncpy(buffer, abbrevs[i].UTF8String, bufferSize);
72+
zones_copy[ids.size()] = nullptr;
73+
unsigned long i = 0;
74+
for (auto it = ids.begin(); it != ids.end(); ++i, ++it) {
75+
PUSH_BACK_OR_RETURN(zones_copy, i, strdup(it->c_str()));
6576
}
6677
return zones_copy;
6778
}
6879

6980
int offset_at_instant(const char *zone_name, int64_t epoch_sec)
7081
{
7182
auto zone_name_nsstring = [NSString stringWithUTF8String: zone_name];
72-
auto zone = [NSTimeZone timeZoneWithName: zone_name_nsstring];
83+
auto zone = zone_by_name(zone_name_nsstring);
7384
auto date = [NSDate dateWithTimeIntervalSince1970: epoch_sec];
7485
return (int32_t)[zone secondsFromGMTForDate: date];
7586
}
7687

7788
bool is_known_timezone(const char *zone_name) {
7889
auto zone_name_nsstring = [NSString stringWithUTF8String: zone_name];
79-
auto zone = [NSTimeZone timeZoneWithName: zone_name_nsstring];
80-
return (zone != nil);
90+
return (zone_by_name(zone_name_nsstring) != nil);
8191
}
8292

8393
int offset_at_datetime(const char *zone_name, int64_t epoch_sec, int *offset) {
8494
*offset = INT_MAX;
8595
// timezone name
8696
auto zone_name_nsstring = [NSString stringWithUTF8String: zone_name];
8797
// timezone
88-
auto zone = [NSTimeZone timeZoneWithName: zone_name_nsstring];
98+
auto zone = zone_by_name(zone_name_nsstring);
8999
if (zone == nil) { return 0; }
90100
/* a date in an unspecified timezone, defined by the number of seconds since
91101
the start of the epoch in *that* unspecified timezone */

core/nativeMain/cinterop/cpp/windows.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ char * get_system_timezone()
281281
char ** available_zone_ids()
282282
{
283283
std::set<std::string> known_native_names, known_ids;
284-
known_ids.insert("UTC");
285284
DYNAMIC_TIME_ZONE_INFORMATION dtzi{};
286285
for (DWORD dwResult = 0, i = 0; dwResult != ERROR_NO_MORE_ITEMS; ++i) {
287286
dwResult = EnumDynamicTimeZoneInformation(i, &dtzi);

core/nativeMain/src/TimeZone.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public actual open class TimeZone internal constructor(actual val id: String) {
6262

6363
actual val availableZoneIds: Set<String>
6464
get() {
65-
val set = mutableSetOf<String>()
65+
val set = mutableSetOf<String>("UTC")
6666
val zones = available_zone_ids()
6767
?: throw RuntimeException("Failed to get the list of available timezones")
6868
var ptr = zones

0 commit comments

Comments
 (0)