Skip to content

Commit 43ecec2

Browse files
authored
Merge pull request #1725 from Kaiede/dateFormatterFix
Reapply "Fix DateFormatter TimeZone Setter"
2 parents cc7d217 + e826252 commit 43ecec2

File tree

5 files changed

+91
-13
lines changed

5 files changed

+91
-13
lines changed

CoreFoundation/NumberDate.subproj/CFTimeZone.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -790,7 +790,7 @@ static CFTimeZoneRef __CFTimeZoneCreateSystem(void) {
790790
size_t zoneInfoDirLen = CFStringGetLength(__tzZoneInfo);
791791
if (strncmp(linkbuf, tzZoneInfo, zoneInfoDirLen) == 0) {
792792
name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)linkbuf + zoneInfoDirLen,
793-
strlen(linkbuf) - zoneInfoDirLen + 1, kCFStringEncodingUTF8, false);
793+
strlen(linkbuf) - zoneInfoDirLen, kCFStringEncodingUTF8, false);
794794
} else {
795795
name = CFStringCreateWithBytes(kCFAllocatorSystemDefault, (uint8_t *)linkbuf, strlen(linkbuf), kCFStringEncodingUTF8, false);
796796
}

Foundation/DateFormatter.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,13 @@ open class DateFormatter : Formatter {
176176
/*@NSCopying*/ internal var _timeZone: TimeZone? { willSet { _reset() } }
177177
open var timeZone: TimeZone! {
178178
get {
179-
guard let timeZone = _timeZone else {
179+
guard let tz = _timeZone else {
180180
return (CFDateFormatterCopyProperty(_cfObject, kCFDateFormatterTimeZone) as! NSTimeZone)._swiftObject
181181
}
182-
return timeZone
182+
return tz
183183
}
184184
set {
185-
_timeZone = timeZone
185+
_timeZone = newValue
186186
}
187187
}
188188

TestFoundation/TestCalendar.swift

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,16 @@ class TestNSDateComponents: XCTestCase {
345345
// 2286-11-20 17:46:41
346346
let date4 = Date(timeIntervalSince1970: 10_000_000_001)
347347

348-
let diff1 = Calendar.current.dateComponents([.month, .year, .day], from: date1, to: date2)
348+
// The date components below assume UTC/GMT time zone.
349+
guard let timeZone = TimeZone(abbreviation: "UTC") else {
350+
XCTFail("Unable to create UTC TimeZone for Test")
351+
return
352+
}
353+
354+
var calendar = Calendar.current
355+
calendar.timeZone = timeZone
356+
357+
let diff1 = calendar.dateComponents([.month, .year, .day], from: date1, to: date2)
349358
XCTAssertEqual(diff1.year, 1)
350359
XCTAssertEqual(diff1.month, 5)
351360
XCTAssertEqual(diff1.isLeapMonth, false)
@@ -364,35 +373,35 @@ class TestNSDateComponents: XCTestCase {
364373
XCTAssertNil(diff1.calendar)
365374
XCTAssertNil(diff1.timeZone)
366375

367-
let diff2 = Calendar.current.dateComponents([.weekOfMonth], from: date2, to: date1)
376+
let diff2 = calendar.dateComponents([.weekOfMonth], from: date2, to: date1)
368377
XCTAssertEqual(diff2.weekOfMonth, -76)
369378
XCTAssertEqual(diff2.isLeapMonth, false)
370379

371-
let diff3 = Calendar.current.dateComponents([.weekday], from: date2, to: date1)
380+
let diff3 = calendar.dateComponents([.weekday], from: date2, to: date1)
372381
XCTAssertEqual(diff3.weekday, -536)
373382
XCTAssertEqual(diff3.isLeapMonth, false)
374383

375-
let diff4 = Calendar.current.dateComponents([.weekday, .weekOfMonth], from: date1, to: date2)
384+
let diff4 = calendar.dateComponents([.weekday, .weekOfMonth], from: date1, to: date2)
376385
XCTAssertEqual(diff4.weekday, 4)
377386
XCTAssertEqual(diff4.weekOfMonth, 76)
378387
XCTAssertEqual(diff4.isLeapMonth, false)
379388

380-
let diff5 = Calendar.current.dateComponents([.weekday, .weekOfYear], from: date1, to: date2)
389+
let diff5 = calendar.dateComponents([.weekday, .weekOfYear], from: date1, to: date2)
381390
XCTAssertEqual(diff5.weekday, 4)
382391
XCTAssertEqual(diff5.weekOfYear, 76)
383392
XCTAssertEqual(diff5.isLeapMonth, false)
384393

385-
let diff6 = Calendar.current.dateComponents([.month, .weekOfMonth], from: date1, to: date2)
394+
let diff6 = calendar.dateComponents([.month, .weekOfMonth], from: date1, to: date2)
386395
XCTAssertEqual(diff6.month, 17)
387396
XCTAssertEqual(diff6.weekOfMonth, 2)
388397
XCTAssertEqual(diff6.isLeapMonth, false)
389398

390-
let diff7 = Calendar.current.dateComponents([.weekOfYear, .weekOfMonth], from: date2, to: date1)
399+
let diff7 = calendar.dateComponents([.weekOfYear, .weekOfMonth], from: date2, to: date1)
391400
XCTAssertEqual(diff7.weekOfYear, -76)
392401
XCTAssertEqual(diff7.weekOfMonth, 0)
393402
XCTAssertEqual(diff7.isLeapMonth, false)
394403

395-
let diff8 = Calendar.current.dateComponents([.era, .quarter, .year, .month, .day, .hour, .minute, .second, .nanosecond, .calendar, .timeZone], from: date2, to: date3)
404+
let diff8 = calendar.dateComponents([.era, .quarter, .year, .month, .day, .hour, .minute, .second, .nanosecond, .calendar, .timeZone], from: date2, to: date3)
396405
XCTAssertEqual(diff8.era, 0)
397406
XCTAssertEqual(diff8.year, 315)
398407
XCTAssertEqual(diff8.quarter, 0)
@@ -406,7 +415,7 @@ class TestNSDateComponents: XCTestCase {
406415
XCTAssertNil(diff8.calendar)
407416
XCTAssertNil(diff8.timeZone)
408417

409-
let diff9 = Calendar.current.dateComponents([.era, .quarter, .year, .month, .day, .hour, .minute, .second, .nanosecond, .calendar, .timeZone], from: date4, to: date3)
418+
let diff9 = calendar.dateComponents([.era, .quarter, .year, .month, .day, .hour, .minute, .second, .nanosecond, .calendar, .timeZone], from: date4, to: date3)
410419
XCTAssertEqual(diff9.era, 0)
411420
XCTAssertEqual(diff9.year, 0)
412421
XCTAssertEqual(diff9.quarter, 0)

TestFoundation/TestDateFormatter.swift

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class TestDateFormatter: XCTestCase {
2424
("test_dateFormatString", test_dateFormatString),
2525
("test_setLocaleToNil", test_setLocaleToNil),
2626
("test_setTimeZoneToNil", test_setTimeZoneToNil),
27+
("test_setTimeZone", test_setTimeZone),
28+
("test_expectedTimeZone", test_expectedTimeZone),
2729
]
2830
}
2931

@@ -356,4 +358,55 @@ class TestDateFormatter: XCTestCase {
356358
// Time zone should go back to the system one.
357359
XCTAssertEqual(f.timeZone, NSTimeZone.system)
358360
}
361+
362+
func test_setTimeZone() {
363+
// Test two different time zones. Should ensure that if one
364+
// happens to be TimeZone.current, we still get a valid test.
365+
let newYork = TimeZone(identifier: "America/New_York")!
366+
let losAngeles = TimeZone(identifier: "America/Los_Angeles")!
367+
368+
XCTAssertNotEqual(newYork, losAngeles)
369+
370+
// Case 1: New York
371+
let f = DateFormatter()
372+
f.timeZone = newYork
373+
XCTAssertEqual(f.timeZone, newYork)
374+
375+
// Case 2: Los Angeles
376+
f.timeZone = losAngeles
377+
XCTAssertEqual(f.timeZone, losAngeles)
378+
}
379+
380+
func test_expectedTimeZone() {
381+
let newYork = TimeZone(identifier: "America/New_York")!
382+
let losAngeles = TimeZone(identifier: "America/Los_Angeles")!
383+
384+
XCTAssertNotEqual(newYork, losAngeles)
385+
386+
let now = Date()
387+
388+
let f = DateFormatter()
389+
f.dateFormat = "z"
390+
f.locale = Locale(identifier: "en_US_POSIX")
391+
392+
// Case 1: TimeZone.current
393+
// This case can catch some issues that cause TimeZone.current to be
394+
// treated like GMT, but it doesn't work if TimeZone.current is GMT.
395+
// If you do find an issue like this caused by this first case,
396+
// it would benefit from a more specific test that fails when
397+
// TimeZone.current is GMT as well.
398+
// (ex. TestTimeZone.test_systemTimeZoneName)
399+
400+
// Disabled because of: https://bugs.swift.org/browse/SR-8994
401+
// f.timeZone = TimeZone.current
402+
// XCTAssertEqual(f.string(from: now), TimeZone.current.abbreviation())
403+
404+
// Case 2: New York
405+
f.timeZone = newYork
406+
XCTAssertEqual(f.string(from: now), newYork.abbreviation())
407+
408+
// Case 3: Los Angeles
409+
f.timeZone = losAngeles
410+
XCTAssertEqual(f.string(from: now), losAngeles.abbreviation())
411+
}
359412
}

TestFoundation/TestTimeZone.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88
//
99

10+
import CoreFoundation
11+
1012
class TestTimeZone: XCTestCase {
1113

1214
static var allTests: [(String, (TestTimeZone) -> () throws -> Void)] {
@@ -29,6 +31,7 @@ class TestTimeZone: XCTestCase {
2931

3032
("test_customMirror", test_tz_customMirror),
3133
("test_knownTimeZones", test_knownTimeZones),
34+
("test_systemTimeZoneName", test_systemTimeZoneName),
3235
]
3336
}
3437

@@ -198,4 +201,17 @@ class TestTimeZone: XCTestCase {
198201
XCTAssertNotNil(TimeZone(identifier: tz), "Cant instantiate valid timeZone: \(tz)")
199202
}
200203
}
204+
205+
func test_systemTimeZoneName() {
206+
// Ensure that the system time zone creates names the same way as creating them with an identifier.
207+
// If it isn't the same, bugs in DateFormat can result, but in this specific case, the bad length
208+
// is only visible to CoreFoundation APIs, and the Swift versions hide it, making it hard to detect.
209+
let timeZone = CFTimeZoneCopySystem()
210+
let timeZoneName = CFTimeZoneGetName(timeZone)
211+
212+
let createdTimeZone = TimeZone(identifier: TimeZone.current.identifier)!
213+
214+
XCTAssertEqual(CFStringGetLength(timeZoneName), TimeZone.current.identifier.count)
215+
XCTAssertEqual(CFStringGetLength(timeZoneName), createdTimeZone.identifier.count)
216+
}
201217
}

0 commit comments

Comments
 (0)