@@ -54,6 +54,58 @@ final class CalendarRecurrenceRuleTests: XCTestCase {
5454 XCTAssertEqual ( results, expectedResults)
5555 }
5656
57+ func testExpandToLeapMonths( ) {
58+ var lunarCalendar = Calendar ( identifier: . chinese)
59+ lunarCalendar. timeZone = . gmt
60+ let locale = Locale ( identifier: " zh-TW " )
61+
62+ let start = Date ( timeIntervalSince1970: 1729641600.0 ) // 2024-10-23T00:00:00-0000
63+
64+ var rule = Calendar . RecurrenceRule ( calendar: lunarCalendar, frequency: . yearly)
65+ rule. months = [ Calendar . RecurrenceRule. Month ( 6 , isLeap: true ) ]
66+ rule. daysOfTheMonth = [ 1 ]
67+ var sequence = rule. recurrences ( of: start) . makeIterator ( )
68+
69+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1753401600.0 ) ) // 2025-07-25T00:00:00-0000 (Sixth leap month)
70+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1786579200.0 ) ) // 2026-08-13T00:00:00-0000 (Seventh month)
71+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1817164800.0 ) ) // 2027-08-02T00:00:00-0000 (Seventh month)
72+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1850342400.0 ) ) // 2028-08-20T00:00:00-0000 (Seventh month)
73+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1881014400.0 ) ) // 2029-08-10T00:00:00-0000 (Seventh month)
74+ }
75+
76+ func testStartFromLeapMonth( ) {
77+ var lunarCalendar = Calendar ( identifier: . chinese)
78+ lunarCalendar. timeZone = . gmt
79+
80+ // Find recurrences of an event that happens on a leap month
81+ let start = Date ( timeIntervalSince1970: 1753401600.0 ) // 2025-07-25T00:00:00-0000 (Leap month)
82+
83+ // A non-strict recurrence would match the month where the leap month would have been
84+ let rule = Calendar . RecurrenceRule ( calendar: lunarCalendar, frequency: . yearly, matchingPolicy: . nextTimePreservingSmallerComponents)
85+ var sequence = rule. recurrences ( of: start) . makeIterator ( )
86+
87+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1753401600.0 ) ) // 2025-07-25T00:00:00-0000 (Sixth leap month)
88+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1786579200.0 ) ) // 2026-08-13T00:00:00-0000 (Seventh month)
89+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1817164800.0 ) ) // 2027-08-02T00:00:00-0000 (Seventh month)
90+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1850342400.0 ) ) // 2028-08-20T00:00:00-0000 (Seventh month)
91+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1881014400.0 ) ) // 2029-08-10T00:00:00-0000 (Seventh month)
92+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1911600000.0 ) ) // 2030-07-30T00:00:00-0000 (Seventh month)
93+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1944777600.0 ) ) // 2031-08-18T00:00:00-0000 (Seventh month)
94+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 1975363200.0 ) ) // 2032-08-06T00:00:00-0000 (Seventh month)
95+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 2005948800.0 ) ) // 2033-07-26T00:00:00-0000 (Seventh month)
96+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 2039126400.0 ) ) // 2034-08-14T00:00:00-0000 (Seventh month)
97+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 2069798400.0 ) ) // 2035-08-04T00:00:00-0000 (Seventh month)
98+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 2100384000.0 ) ) // 2036-07-23T00:00:00-0000 (Sixth leap month)
99+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 2133561600.0 ) ) // 2037-08-11T00:00:00-0000 (Seventh month)
100+ XCTAssertEqual ( sequence. next ( ) , Date ( timeIntervalSince1970: 2164233600.0 ) ) // 2038-08-01T00:00:00-0000 (Seventh month)
101+
102+ // A strict recurrence only matches in years with leap months
103+ let strictRule = Calendar . RecurrenceRule ( calendar: lunarCalendar, frequency: . yearly, matchingPolicy: . strict)
104+ var strictSequence = strictRule. recurrences ( of: start) . makeIterator ( )
105+ XCTAssertEqual ( strictSequence. next ( ) , Date ( timeIntervalSince1970: 1753401600.0 ) ) // 2025-07-25T00:00:00-0000 (Sixth leap month)
106+ XCTAssertEqual ( strictSequence. next ( ) , Date ( timeIntervalSince1970: 2100384000.0 ) ) // 2036-07-23T00:00:00-0000 (Sixth leap month)
107+ }
108+
57109 func testDaylightSavingsRepeatedTimePolicyFirst( ) {
58110 let start = Date ( timeIntervalSince1970: 1730535600.0 ) // 2024-11-02T01:20:00-0700
59111 var rule = Calendar . RecurrenceRule ( calendar: gregorian, frequency: . daily)
@@ -68,9 +120,9 @@ final class CalendarRecurrenceRuleTests: XCTestCase {
68120 Date ( timeIntervalSince1970: 1730712000.0 ) , // 2024-11-04T01:20:00-0800
69121 ]
70122 XCTAssertEqual ( results, expectedResults)
71- }
72-
73- func testDaylightSavingsRepeatedTimePolicyLast( ) {
123+ }
124+
125+ func testDaylightSavingsRepeatedTimePolicyLast( ) {
74126 let start = Date ( timeIntervalSince1970: 1730535600.0 ) // 2024-11-02T01:20:00-0700
75127 var rule = Calendar . RecurrenceRule ( calendar: gregorian, frequency: . daily)
76128 rule. repeatedTimePolicy = . last
@@ -84,5 +136,5 @@ final class CalendarRecurrenceRuleTests: XCTestCase {
84136 Date ( timeIntervalSince1970: 1730712000.0 ) , // 2024-11-04T01:20:00-0800
85137 ]
86138 XCTAssertEqual ( results, expectedResults)
87- }
139+ }
88140}
0 commit comments