@@ -43,6 +43,99 @@ private struct GregorianCalendarTests {
4343 _ = d. julianDay
4444 }
4545
46+ // MARK: Leap month
47+ @Test func calendarUnitLeapMonth_gregorianCalendar( ) {
48+ // Test leap month with a calendar that does not observe leap month
49+
50+ // Gregorian: 2023-03-22.
51+ let date1 = Date ( timeIntervalSinceReferenceDate: 701161200 )
52+ // Gregorian: 2023-03-02.
53+ let date2 = Date ( timeIntervalSinceReferenceDate: 699433200 )
54+
55+ var calendar = Calendar ( identifier: . gregorian)
56+ calendar. timeZone = . gmt
57+
58+ let minRange = calendar. minimumRange ( of: . isLeapMonth)
59+ #expect( minRange? . lowerBound == 0 )
60+ #expect( minRange? . count == 1 )
61+
62+ let maxRange = calendar. maximumRange ( of: . isLeapMonth)
63+ #expect( maxRange? . lowerBound == 0 )
64+ #expect( maxRange? . count == 1 )
65+
66+ let leapMonthRange = calendar. range ( of: . isLeapMonth, in: . year, for: date1)
67+ #expect( leapMonthRange == nil )
68+
69+ let dateIntervial = calendar. dateInterval ( of: . isLeapMonth, for: date1)
70+ #expect( dateIntervial == nil )
71+
72+ // Invalid ordinality flag
73+ let ordinal = calendar. ordinality ( of: . isLeapMonth, in: . year, for: date1)
74+ #expect( ordinal == nil )
75+
76+ // Invalid ordinality flag
77+ let ordinal2 = calendar. ordinality ( of: . day, in: . isLeapMonth, for: date1)
78+ #expect( ordinal2 == nil )
79+
80+ let extractedComponents = calendar. dateComponents ( [ . year, . month] , from: date1)
81+ #expect( extractedComponents. isLeapMonth == false )
82+ #expect( extractedComponents. month == 3 )
83+
84+ let isLeap = calendar. component ( . isLeapMonth, from: date1)
85+ #expect( isLeap == 0 )
86+
87+ let extractedLeapMonthComponents_onlyLeapMonth = calendar. dateComponents ( [ . isLeapMonth] , from: date1)
88+ #expect( extractedLeapMonthComponents_onlyLeapMonth. isLeapMonth == false )
89+
90+ let extractedLeapMonthComponents = calendar. dateComponents ( [ . isLeapMonth, . month] , from: date1)
91+ #expect( extractedLeapMonthComponents. isLeapMonth == false )
92+ #expect( extractedLeapMonthComponents. month == 3 )
93+
94+ let isEqualMonth = calendar. isDate ( date1, equalTo: date2, toGranularity: . month)
95+ #expect( isEqualMonth) // Both are in month 3
96+
97+ let isEqualLeapMonth = calendar. isDate ( date1, equalTo: date2, toGranularity: . isLeapMonth)
98+ #expect( isEqualLeapMonth) // Both are not in leap month
99+
100+ // Invalid granularity flag. Return what we return for other invalid `Calendar.Component` inputs
101+ let result = calendar. compare ( date1, to: date2, toGranularity: . month)
102+ #expect( result == . orderedSame)
103+
104+ // Invalid granularity flag. Return what we return for other invalid `Calendar.Component` inputs
105+ let onlyLeapMonthComparisonResult = calendar. compare ( date1, to: date2, toGranularity: . isLeapMonth)
106+ #expect( onlyLeapMonthComparisonResult == . orderedSame)
107+
108+ let nextLeapMonthDate = calendar. nextDate ( after: date1, matching: DateComponents ( isLeapMonth: true ) , matchingPolicy: . strict)
109+ #expect( nextLeapMonthDate == nil ) // There is not a date in Gregorian that is a leap month
110+
111+ #if FIXED_SINGLE_LEAPMONTH
112+ let nextNonLeapMonthDate = calendar. nextDate ( after: date1, matching: DateComponents ( isLeapMonth: false ) , matchingPolicy: . strict)
113+ #expect( nextNonLeapMonthDate == date1) // date1 matches the condition already
114+ #endif
115+
116+ var settingLeapMonthComponents = calendar. dateComponents ( [ . year, . month, . day] , from: date1)
117+ settingLeapMonthComponents. isLeapMonth = true
118+ let settingLeapMonthDate = calendar. date ( from: settingLeapMonthComponents)
119+ #expect( settingLeapMonthDate == nil ) // There is not a date in Gregorian that is a leap month
120+
121+ var settingNonLeapMonthComponents = calendar. dateComponents ( [ . year, . month, . day, . hour, . minute, . second] , from: date1)
122+ settingNonLeapMonthComponents. isLeapMonth = false
123+ let settingNonLeapMonthDate = calendar. date ( from: settingNonLeapMonthComponents)
124+ #expect( settingNonLeapMonthDate == date1) // date1 matches the condition already
125+
126+ let diffComponents = calendar. dateComponents ( [ . month, . day, . isLeapMonth] , from: date1, to: date2)
127+ #expect( diffComponents. month == 0 )
128+ #expect( diffComponents. isLeapMonth == nil )
129+ #expect( diffComponents. day == - 20 )
130+
131+ let addedDate = calendar. date ( byAdding: . isLeapMonth, value: 1 , to: date1)
132+ #expect( addedDate == nil )
133+
134+ // Invalid argument; cannot add a boolean component with an integer value
135+ let addedDate_notLeap = calendar. date ( byAdding: . isLeapMonth, value: 0 , to: date1)
136+ #expect( addedDate_notLeap == nil )
137+ }
138+
46139 // MARK: Date from components
47140
48141 @Test func testDateFromComponents( ) {
0 commit comments