Skip to content

Commit 889c38f

Browse files
authored
Fix TimeZone.current lookup on Windows (#975)
1 parent 33a49e5 commit 889c38f

File tree

4 files changed

+41
-13
lines changed

4 files changed

+41
-13
lines changed

Sources/FoundationEssentials/TimeZone/TimeZone.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ extension TimeZone {
390390

391391
extension TimeZone {
392392
internal static func dataFromTZFile(_ name: String) -> Data {
393-
#if NO_TZFILE
393+
#if NO_TZFILE || os(Windows)
394394
return Data()
395395
#else
396396
let path = TZDIR + "/" + name

Sources/FoundationEssentials/TimeZone/TimeZone_Cache.swift

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ dynamic package func _timeZoneGMTClass() -> _TimeZoneProtocol.Type {
5050
}
5151
#endif
5252

53+
#if os(Windows)
54+
dynamic package func _timeZoneIdentifier(forWindowsIdentifier windowsIdentifier: String) -> String? {
55+
nil
56+
}
57+
#endif
58+
5359
/// Singleton which listens for notifications about preference changes for TimeZone and holds cached values for current, fixed time zones, etc.
5460
struct TimeZoneCache : Sendable, ~Copyable {
5561
// MARK: - State
@@ -114,18 +120,14 @@ struct TimeZoneCache : Sendable, ~Copyable {
114120
}
115121

116122
#if os(Windows)
117-
let hFile = TZDEFAULT.withCString(encodedAs: UTF16.self) {
118-
CreateFileW($0, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nil, OPEN_EXISTING, 0, nil)
119-
}
120-
defer { CloseHandle(hFile) }
121-
let dwSize = GetFinalPathNameByHandleW(hFile, nil, 0, VOLUME_NAME_DOS)
122-
let path = withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(dwSize)) {
123-
_ = GetFinalPathNameByHandleW(hFile, $0.baseAddress, dwSize, VOLUME_NAME_DOS)
124-
return String(decodingCString: $0.baseAddress!, as: UTF16.self)
125-
}
126-
if let rangeOfZoneInfo = path._range(of: "\(TZDIR)\\", anchored: false, backwards: false) {
127-
let name = path[rangeOfZoneInfo.upperBound...]
128-
if let result = fixed(String(name)) {
123+
var timeZoneInfo = TIME_ZONE_INFORMATION()
124+
if GetTimeZoneInformation(&timeZoneInfo) != TIME_ZONE_ID_INVALID {
125+
let windowsName = withUnsafePointer(to: &(timeZoneInfo.StandardName)) {
126+
$0.withMemoryRebound(to: WCHAR.self, capacity: 32) {
127+
String(decoding: UnsafeBufferPointer(start: $0, count: wcslen($0)), as: UTF16.self)
128+
}
129+
}
130+
if let identifier = _timeZoneIdentifier(forWindowsIdentifier: windowsName), let result = fixed(identifier) {
129131
return TimeZone(inner: result)
130132
}
131133
}

Sources/FoundationInternationalization/TimeZone/TimeZone_ICU.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ private func _timeZoneICUClass_localized() -> _TimeZoneProtocol.Type? {
3232
}
3333
#endif
3434

35+
#if os(Windows)
36+
@_dynamicReplacement(for: _timeZoneIdentifier(forWindowsIdentifier:))
37+
private func _timeZoneIdentifier_ICU(forWindowsIdentifier windowsIdentifier: String) -> String? {
38+
_TimeZoneICU.getSystemTimeZoneID(forWindowsIdentifier: windowsIdentifier)
39+
}
40+
#endif
41+
3542
internal final class _TimeZoneICU: _TimeZoneProtocol, Sendable {
3643
init?(secondsFromGMT: Int) {
3744
fatalError("Unexpected init")
@@ -309,6 +316,23 @@ internal final class _TimeZoneICU: _TimeZoneProtocol, Sendable {
309316
return result
310317
}
311318

319+
#if os(Windows)
320+
internal static func getSystemTimeZoneID(forWindowsIdentifier identifier: String) -> String? {
321+
let timeZoneIdentifier = Array(identifier.utf16)
322+
let result: String? = timeZoneIdentifier.withUnsafeBufferPointer { identifier in
323+
return _withResizingUCharBuffer { buffer, size, status in
324+
let len = ucal_getTimeZoneIDForWindowsID(identifier.baseAddress, Int32(identifier.count), nil, buffer, size, &status)
325+
if status.isSuccess {
326+
return len
327+
} else {
328+
return nil
329+
}
330+
}
331+
}
332+
return result
333+
}
334+
#endif
335+
312336
internal static func timeZoneNamesFromICU() -> [String] {
313337
let filteredTimeZoneNames = [
314338
"ACT",

Sources/_FoundationCShims/include/_CStdlib.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,15 @@
148148
#include <tzfile.h>
149149
#else
150150

151+
#if TARGET_OS_MAC || TARGET_OS_LINUX
151152
#ifndef TZDIR
152153
#define TZDIR "/usr/share/zoneinfo/" /* Time zone object file directory */
153154
#endif /* !defined TZDIR */
154155

155156
#ifndef TZDEFAULT
156157
#define TZDEFAULT "/etc/localtime"
157158
#endif /* !defined TZDEFAULT */
159+
#endif /* TARGET_OS_MAC || TARGET_OS_LINUX */
158160

159161
#endif
160162

0 commit comments

Comments
 (0)