@@ -43,6 +43,99 @@ private struct GregorianCalendarTests {
43
43
_ = d. julianDay
44
44
}
45
45
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
+
46
139
// MARK: Date from components
47
140
48
141
@Test func testDateFromComponents( ) {
0 commit comments