Skip to content

Commit d3d8499

Browse files
authored
Fix TimeZone.current lookup on Windows (#975) (#978)
1 parent 1562333 commit d3d8499

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 {
5561
// MARK: - State
@@ -129,18 +135,14 @@ struct TimeZoneCache : Sendable {
129135
}
130136

131137
#if os(Windows)
132-
let hFile = TZDEFAULT.withCString(encodedAs: UTF16.self) {
133-
CreateFileW($0, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nil, OPEN_EXISTING, 0, nil)
134-
}
135-
defer { CloseHandle(hFile) }
136-
let dwSize = GetFinalPathNameByHandleW(hFile, nil, 0, VOLUME_NAME_DOS)
137-
let path = withUnsafeTemporaryAllocation(of: WCHAR.self, capacity: Int(dwSize)) {
138-
_ = GetFinalPathNameByHandleW(hFile, $0.baseAddress, dwSize, VOLUME_NAME_DOS)
139-
return String(decodingCString: $0.baseAddress!, as: UTF16.self)
140-
}
141-
if let rangeOfZoneInfo = path._range(of: "\(TZDIR)\\", anchored: false, backwards: false) {
142-
let name = path[rangeOfZoneInfo.upperBound...]
143-
if let result = fixed(String(name)) {
138+
var timeZoneInfo = TIME_ZONE_INFORMATION()
139+
if GetTimeZoneInformation(&timeZoneInfo) != TIME_ZONE_ID_INVALID {
140+
let windowsName = withUnsafePointer(to: &(timeZoneInfo.StandardName)) {
141+
$0.withMemoryRebound(to: WCHAR.self, capacity: 32) {
142+
String(decoding: UnsafeBufferPointer(start: $0, count: wcslen($0)), as: UTF16.self)
143+
}
144+
}
145+
if let identifier = _timeZoneIdentifier(forWindowsIdentifier: windowsName), let result = fixed(identifier) {
144146
return TimeZone(inner: result)
145147
}
146148
}

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
@@ -156,13 +156,15 @@
156156
#include <tzfile.h>
157157
#else
158158

159+
#if TARGET_OS_MAC || TARGET_OS_LINUX
159160
#ifndef TZDIR
160161
#define TZDIR "/usr/share/zoneinfo/" /* Time zone object file directory */
161162
#endif /* !defined TZDIR */
162163

163164
#ifndef TZDEFAULT
164165
#define TZDEFAULT "/etc/localtime"
165166
#endif /* !defined TZDEFAULT */
167+
#endif /* TARGET_OS_MAC || TARGET_OS_LINUX */
166168

167169
#endif
168170

0 commit comments

Comments
 (0)