Skip to content

Commit 1b741a9

Browse files
drodriguezspevans
authored andcommitted
Fix DateFormatter behaviour for nil locale and timeZone. (swiftlang#1681)
The locale and timeZone properties of DateFormatter seems to be "null resetteable" in macOS, but the Swift Foundation behaviour differs. Before this patch, setting locale to nil would segfault the program the next time the formatter is used, while timeZone will incorrectly return nil. After this patch a nil locale will simply reset to the current locale and a nil timeZone will reset to the system one. The tests check that the initial defaults are also correctly set. (cherry picked from commit 1965003)
1 parent 0c62cfc commit 1b741a9

File tree

2 files changed

+49
-3
lines changed

2 files changed

+49
-3
lines changed

Foundation/DateFormatter.swift

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ open class DateFormatter : Formatter {
9797

9898
internal func _setFormatterAttributes(_ formatter: CFDateFormatter) {
9999
_setFormatterAttribute(formatter, attributeName: kCFDateFormatterIsLenient, value: isLenient._cfObject)
100-
_setFormatterAttribute(formatter, attributeName: kCFDateFormatterTimeZone, value: timeZone?._cfObject)
100+
_setFormatterAttribute(formatter, attributeName: kCFDateFormatterTimeZone, value: _timeZone?._cfObject)
101101
if let ident = _calendar?.identifier {
102102
_setFormatterAttribute(formatter, attributeName: kCFDateFormatterCalendarName, value: Calendar._toNSCalendarIdentifier(ident).rawValue._cfObject)
103103
} else {
@@ -166,11 +166,31 @@ open class DateFormatter : Formatter {
166166
}
167167
}
168168

169-
/*@NSCopying*/ open var locale: Locale! = .current { willSet { _reset() } }
169+
/*@NSCopying*/ internal var _locale: Locale? { willSet { _reset() } }
170+
open var locale: Locale! {
171+
get {
172+
guard let locale = _locale else { return .current }
173+
return locale
174+
}
175+
set {
176+
_locale = newValue
177+
}
178+
}
170179

171180
open var generatesCalendarDates = false { willSet { _reset() } }
172181

173-
/*@NSCopying*/ open var timeZone: TimeZone! = NSTimeZone.system { willSet { _reset() } }
182+
/*@NSCopying*/ internal var _timeZone: TimeZone? { willSet { _reset() } }
183+
open var timeZone: TimeZone! {
184+
get {
185+
guard let timeZone = _timeZone else {
186+
return (CFDateFormatterCopyProperty(_cfObject, kCFDateFormatterTimeZone) as! NSTimeZone)._swiftObject
187+
}
188+
return timeZone
189+
}
190+
set {
191+
_timeZone = timeZone
192+
}
193+
}
174194

175195
/*@NSCopying*/ internal var _calendar: Calendar! { willSet { _reset() } }
176196
open var calendar: Calendar! {

TestFoundation/TestDateFormatter.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class TestDateFormatter: XCTestCase {
2222
("test_customDateFormat", test_customDateFormat),
2323
("test_setLocalizedDateFormatFromTemplate", test_setLocalizedDateFormatFromTemplate),
2424
("test_dateFormatString", test_dateFormatString),
25+
("test_setLocaleToNil", test_setLocaleToNil),
26+
("test_setTimeZoneToNil", test_setTimeZoneToNil),
2527
("test_dateFrom", test_dateFrom),
2628
]
2729
}
@@ -332,6 +334,30 @@ class TestDateFormatter: XCTestCase {
332334
}
333335
}
334336

337+
func test_setLocaleToNil() {
338+
let f = DateFormatter()
339+
// Locale should be the current one by default
340+
XCTAssertEqual(f.locale, .current)
341+
342+
f.locale = nil
343+
344+
// Locale should go back to current.
345+
XCTAssertEqual(f.locale, .current)
346+
347+
// A nil locale should not crash a subsequent operation
348+
let result: String? = f.string(from: Date())
349+
XCTAssertNotNil(result)
350+
}
351+
352+
func test_setTimeZoneToNil() {
353+
let f = DateFormatter()
354+
// Time zone should be the system one by default.
355+
XCTAssertEqual(f.timeZone, NSTimeZone.system)
356+
f.timeZone = nil
357+
// Time zone should go back to the system one.
358+
XCTAssertEqual(f.timeZone, NSTimeZone.system)
359+
}
360+
335361
func test_dateFrom() throws {
336362
let formatter = DateFormatter()
337363
formatter.timeZone = TimeZone(identifier: "UTC")

0 commit comments

Comments
 (0)