Skip to content

Commit 4c95608

Browse files
authored
Merge pull request swiftlang#1658 from spevans/pr_sr_7011
2 parents 24e43de + 8e25995 commit 4c95608

File tree

3 files changed

+142
-16
lines changed

3 files changed

+142
-16
lines changed

CoreFoundation/Locale.subproj/CFCalendar.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -951,9 +951,23 @@ Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTim
951951
int direction = (startingAT <= resultAT) ? 1 : -1;
952952
char ch = *componentDesc;
953953
while (ch) {
954+
if (count < 1) {
955+
// Output vector has no more free entries
956+
return false;
957+
}
958+
954959
UCalendarDateFields field = __CFCalendarGetICUFieldCodeFromChar(ch);
955-
const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14};
956-
int multiple = direction * (1 << multiple_table[flsl(__CFCalendarGetCalendarUnitFromChar(ch)) - 1]);
960+
const int multiple_table[] = {0, 0, 16, 19, 24, 26, 24, 28, 14, 14, 14, 0, 0, 0, 0};
961+
int unit = __CFCalendarGetCalendarUnitFromChar(ch);
962+
if (unit < 0) {
963+
// Not a valid calendar unit character
964+
return false;
965+
}
966+
int idx = flsl(unit) - 1;
967+
if (idx < 0 || idx >= (sizeof(multiple_table) / sizeof(int))) {
968+
return false;
969+
}
970+
int multiple = direction * (1 << multiple_table[idx]);
957971
Boolean divide = false, alwaysDivide = false;
958972
int result = 0;
959973
while ((direction > 0 && curr < goal) || (direction < 0 && goal < curr)) {
@@ -982,6 +996,7 @@ Boolean _CFCalendarGetComponentDifferenceV(CFCalendarRef calendar, CFAbsoluteTim
982996
}
983997
*(*vector) = result;
984998
vector++;
999+
count--;
9851000
componentDesc++;
9861001
ch = *componentDesc;
9871002
}

Foundation/NSCalendar.swift

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -457,11 +457,11 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding {
457457
} else {
458458
// _convert(comps.week, type: "^", vector: &vector, compDesc: &compDesc)
459459
}
460+
_convert(comps.month, type: "M", vector: &vector, compDesc: &compDesc)
460461
_convert(comps.weekOfMonth, type: "W", vector: &vector, compDesc: &compDesc)
461462
_convert(comps.yearForWeekOfYear, type: "Y", vector: &vector, compDesc: &compDesc)
462463
_convert(comps.weekday, type: "E", vector: &vector, compDesc: &compDesc)
463464
_convert(comps.weekdayOrdinal, type: "F", vector: &vector, compDesc: &compDesc)
464-
_convert(comps.month, type: "M", vector: &vector, compDesc: &compDesc)
465465
_convert(comps.isLeapMonth, type: "l", vector: &vector, compDesc: &compDesc)
466466
_convert(comps.day, type: "d", vector: &vector, compDesc: &compDesc)
467467
_convert(comps.hour, type: "H", vector: &vector, compDesc: &compDesc)
@@ -497,19 +497,21 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding {
497497
}
498498
}
499499

500-
private func _setup(_ unitFlags: Unit) -> [Int8] {
500+
private func _setup(_ unitFlags: Unit, addIsLeapMonth: Bool = true) -> [Int8] {
501501
var compDesc = [Int8]()
502502
_setup(unitFlags, field: .era, type: "G", compDesc: &compDesc)
503503
_setup(unitFlags, field: .year, type: "y", compDesc: &compDesc)
504504
_setup(unitFlags, field: .quarter, type: "Q", compDesc: &compDesc)
505-
_setup(unitFlags, field: .month, type: "M", compDesc: &compDesc)
506-
_setup(unitFlags, field: .month, type: "l", compDesc: &compDesc)
507-
_setup(unitFlags, field: .day, type: "d", compDesc: &compDesc)
508505
_setup(unitFlags, field: .weekOfYear, type: "w", compDesc: &compDesc)
506+
_setup(unitFlags, field: .month, type: "M", compDesc: &compDesc)
507+
if addIsLeapMonth {
508+
_setup(unitFlags, field: .month, type: "l", compDesc: &compDesc)
509+
}
509510
_setup(unitFlags, field: .weekOfMonth, type: "W", compDesc: &compDesc)
510511
_setup(unitFlags, field: .yearForWeekOfYear, type: "Y", compDesc: &compDesc)
511512
_setup(unitFlags, field: .weekday, type: "E", compDesc: &compDesc)
512513
_setup(unitFlags, field: .weekdayOrdinal, type: "F", compDesc: &compDesc)
514+
_setup(unitFlags, field: .day, type: "d", compDesc: &compDesc)
513515
_setup(unitFlags, field: .hour, type: "H", compDesc: &compDesc)
514516
_setup(unitFlags, field: .minute, type: "m", compDesc: &compDesc)
515517
_setup(unitFlags, field: .second, type: "s", compDesc: &compDesc)
@@ -520,27 +522,27 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding {
520522

521523
private func _setComp(_ unitFlags: Unit, field: Unit, vector: [Int32], compIndex: inout Int, setter: (Int32) -> Void) {
522524
if unitFlags.contains(field) {
523-
if vector[compIndex] != -1 {
524-
setter(vector[compIndex])
525-
}
525+
setter(vector[compIndex])
526526
compIndex += 1
527527
}
528528
}
529529

530-
private func _components(_ unitFlags: Unit, vector: [Int32]) -> DateComponents {
530+
private func _components(_ unitFlags: Unit, vector: [Int32], addIsLeapMonth: Bool = true) -> DateComponents {
531531
var compIdx = 0
532532
var comps = DateComponents()
533533
_setComp(unitFlags, field: .era, vector: vector, compIndex: &compIdx) { comps.era = Int($0) }
534534
_setComp(unitFlags, field: .year, vector: vector, compIndex: &compIdx) { comps.year = Int($0) }
535535
_setComp(unitFlags, field: .quarter, vector: vector, compIndex: &compIdx) { comps.quarter = Int($0) }
536-
_setComp(unitFlags, field: .month, vector: vector, compIndex: &compIdx) { comps.month = Int($0) }
537-
_setComp(unitFlags, field: .month, vector: vector, compIndex: &compIdx) { comps.isLeapMonth = $0 != 0 }
538-
_setComp(unitFlags, field: .day, vector: vector, compIndex: &compIdx) { comps.day = Int($0) }
539536
_setComp(unitFlags, field: .weekOfYear, vector: vector, compIndex: &compIdx) { comps.weekOfYear = Int($0) }
537+
_setComp(unitFlags, field: .month, vector: vector, compIndex: &compIdx) { comps.month = Int($0) }
538+
if addIsLeapMonth {
539+
_setComp(unitFlags, field: .month, vector: vector, compIndex: &compIdx) { comps.isLeapMonth = $0 != 0 }
540+
}
540541
_setComp(unitFlags, field: .weekOfMonth, vector: vector, compIndex: &compIdx) { comps.weekOfMonth = Int($0) }
541542
_setComp(unitFlags, field: .yearForWeekOfYear, vector: vector, compIndex: &compIdx) { comps.yearForWeekOfYear = Int($0) }
542543
_setComp(unitFlags, field: .weekday, vector: vector, compIndex: &compIdx) { comps.weekday = Int($0) }
543544
_setComp(unitFlags, field: .weekdayOrdinal, vector: vector, compIndex: &compIdx) { comps.weekdayOrdinal = Int($0) }
545+
_setComp(unitFlags, field: .day, vector: vector, compIndex: &compIdx) { comps.day = Int($0) }
544546
_setComp(unitFlags, field: .hour, vector: vector, compIndex: &compIdx) { comps.hour = Int($0) }
545547
_setComp(unitFlags, field: .minute, vector: vector, compIndex: &compIdx) { comps.minute = Int($0) }
546548
_setComp(unitFlags, field: .second, vector: vector, compIndex: &compIdx) { comps.second = Int($0) }
@@ -599,7 +601,14 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding {
599601
}
600602

601603
open func components(_ unitFlags: Unit, from startingDate: Date, to resultDate: Date, options opts: Options = []) -> DateComponents {
602-
let compDesc = _setup(unitFlags)
604+
let validUnitFlags: NSCalendar.Unit = [
605+
.era, .year, .month, .day, .hour, .minute, .second, .weekOfYear, .weekOfMonth, .yearForWeekOfYear, .weekday, .weekdayOrdinal ]
606+
607+
let invalidUnitFlags: NSCalendar.Unit = [ .quarter, .nanosecond, .timeZone, .calendar]
608+
609+
// Mask off the unsupported fields
610+
let newUnitFlags = Unit(rawValue: unitFlags.rawValue & validUnitFlags.rawValue)
611+
let compDesc = _setup(newUnitFlags, addIsLeapMonth: false)
603612
var ints = [Int32](repeating: 0, count: 20)
604613
let res = ints.withUnsafeMutableBufferPointer { (intArrayBuffer: inout UnsafeMutableBufferPointer<Int32>) -> Bool in
605614
var vector: [UnsafeMutablePointer<Int32>] = (0..<20).map { idx in
@@ -612,7 +621,19 @@ open class NSCalendar : NSObject, NSCopying, NSSecureCoding {
612621
}
613622
}
614623
if res {
615-
return _components(unitFlags, vector: ints)
624+
let emptyUnitFlags = Unit(rawValue: unitFlags.rawValue & invalidUnitFlags.rawValue)
625+
var components = _components(newUnitFlags, vector: ints, addIsLeapMonth: false)
626+
627+
// nanosecond and quarter always get set to zero if requested in the output
628+
if emptyUnitFlags.contains(.nanosecond) {
629+
components.nanosecond = 0
630+
}
631+
if emptyUnitFlags.contains(.quarter) {
632+
components.quarter = 0
633+
}
634+
// isLeapMonth is always set
635+
components.isLeapMonth = false
636+
return components
616637
}
617638
fatalError()
618639
}

TestFoundation/TestCalendar.swift

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ class TestNSDateComponents: XCTestCase {
199199
static var allTests: [(String, (TestNSDateComponents) -> () throws -> Void)] {
200200
return [
201201
("test_copyNSDateComponents", test_copyNSDateComponents),
202+
("test_dateDifferenceComponents", test_dateDifferenceComponents),
202203
]
203204
}
204205

@@ -223,4 +224,93 @@ class TestNSDateComponents: XCTestCase {
223224
XCTAssertEqual(components.hour, 12)
224225
XCTAssertEqual(copy.hour, 14)
225226
}
227+
228+
func test_dateDifferenceComponents() {
229+
// 1970-01-01 00:00:00
230+
let date1 = Date(timeIntervalSince1970: 0)
231+
232+
// 1971-06-21 00:00:00
233+
let date2 = Date(timeIntervalSince1970: 46310400)
234+
235+
// 2286-11-20 17:46:40
236+
let date3 = Date(timeIntervalSince1970: 10_000_000_000)
237+
238+
// 2286-11-20 17:46:41
239+
let date4 = Date(timeIntervalSince1970: 10_000_000_001)
240+
241+
let diff1 = Calendar.current.dateComponents([.month, .year, .day], from: date1, to: date2)
242+
XCTAssertEqual(diff1.year, 1)
243+
XCTAssertEqual(diff1.month, 5)
244+
XCTAssertEqual(diff1.isLeapMonth, false)
245+
XCTAssertEqual(diff1.day, 20)
246+
XCTAssertNil(diff1.era)
247+
XCTAssertNil(diff1.yearForWeekOfYear)
248+
XCTAssertNil(diff1.quarter)
249+
XCTAssertNil(diff1.weekOfYear)
250+
XCTAssertNil(diff1.weekOfMonth)
251+
XCTAssertNil(diff1.weekdayOrdinal)
252+
XCTAssertNil(diff1.weekday)
253+
XCTAssertNil(diff1.hour)
254+
XCTAssertNil(diff1.minute)
255+
XCTAssertNil(diff1.second)
256+
XCTAssertNil(diff1.nanosecond)
257+
XCTAssertNil(diff1.calendar)
258+
XCTAssertNil(diff1.timeZone)
259+
260+
let diff2 = Calendar.current.dateComponents([.weekOfMonth], from: date2, to: date1)
261+
XCTAssertEqual(diff2.weekOfMonth, -76)
262+
XCTAssertEqual(diff2.isLeapMonth, false)
263+
264+
let diff3 = Calendar.current.dateComponents([.weekday], from: date2, to: date1)
265+
XCTAssertEqual(diff3.weekday, -536)
266+
XCTAssertEqual(diff3.isLeapMonth, false)
267+
268+
let diff4 = Calendar.current.dateComponents([.weekday, .weekOfMonth], from: date1, to: date2)
269+
XCTAssertEqual(diff4.weekday, 4)
270+
XCTAssertEqual(diff4.weekOfMonth, 76)
271+
XCTAssertEqual(diff4.isLeapMonth, false)
272+
273+
let diff5 = Calendar.current.dateComponents([.weekday, .weekOfYear], from: date1, to: date2)
274+
XCTAssertEqual(diff5.weekday, 4)
275+
XCTAssertEqual(diff5.weekOfYear, 76)
276+
XCTAssertEqual(diff5.isLeapMonth, false)
277+
278+
let diff6 = Calendar.current.dateComponents([.month, .weekOfMonth], from: date1, to: date2)
279+
XCTAssertEqual(diff6.month, 17)
280+
XCTAssertEqual(diff6.weekOfMonth, 2)
281+
XCTAssertEqual(diff6.isLeapMonth, false)
282+
283+
let diff7 = Calendar.current.dateComponents([.weekOfYear, .weekOfMonth], from: date2, to: date1)
284+
XCTAssertEqual(diff7.weekOfYear, -76)
285+
XCTAssertEqual(diff7.weekOfMonth, 0)
286+
XCTAssertEqual(diff7.isLeapMonth, false)
287+
288+
let diff8 = Calendar.current.dateComponents([.era, .quarter, .year, .month, .day, .hour, .minute, .second, .nanosecond, .calendar, .timeZone], from: date2, to: date3)
289+
XCTAssertEqual(diff8.era, 0)
290+
XCTAssertEqual(diff8.year, 315)
291+
XCTAssertEqual(diff8.quarter, 0)
292+
XCTAssertEqual(diff8.month, 4)
293+
XCTAssertEqual(diff8.day, 30)
294+
XCTAssertEqual(diff8.hour, 17)
295+
XCTAssertEqual(diff8.minute, 46)
296+
XCTAssertEqual(diff8.second, 40)
297+
XCTAssertEqual(diff8.nanosecond, 0)
298+
XCTAssertEqual(diff8.isLeapMonth, false)
299+
XCTAssertNil(diff8.calendar)
300+
XCTAssertNil(diff8.timeZone)
301+
302+
let diff9 = Calendar.current.dateComponents([.era, .quarter, .year, .month, .day, .hour, .minute, .second, .nanosecond, .calendar, .timeZone], from: date4, to: date3)
303+
XCTAssertEqual(diff9.era, 0)
304+
XCTAssertEqual(diff9.year, 0)
305+
XCTAssertEqual(diff9.quarter, 0)
306+
XCTAssertEqual(diff9.month, 0)
307+
XCTAssertEqual(diff9.day, 0)
308+
XCTAssertEqual(diff9.hour, 0)
309+
XCTAssertEqual(diff9.minute, 0)
310+
XCTAssertEqual(diff9.second, -1)
311+
XCTAssertEqual(diff9.nanosecond, 0)
312+
XCTAssertEqual(diff9.isLeapMonth, false)
313+
XCTAssertNil(diff9.calendar)
314+
XCTAssertNil(diff9.timeZone)
315+
}
226316
}

0 commit comments

Comments
 (0)