Skip to content

Commit 5f9777d

Browse files
DraganBesevicDraganBesevic
andauthored
Expanded calendar support (#1138)
* Expanded calendar support * Updated proposal number --------- Co-authored-by: DraganBesevic <[email protected]>
1 parent b673ae7 commit 5f9777d

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed

NNNN-Hindu-calendar-support-OOS.md

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# Expanded calendar support
2+
3+
* Proposal: SF-017
4+
* Authors: [Dragan Besevic]([email protected])
5+
* Review Manager: Tina Liu
6+
* Status: **Accepted**
7+
8+
9+
## Revision history
10+
11+
* **v1** Initial version
12+
13+
## Introduction
14+
15+
16+
17+
This proposal is for adding expanded support for several calendars used in South Asia, lunisolar and solar calendars used in regions in India, as well as Thai, Vietnamese and Korean calendars based on the Chinese lunisolar calendar. Swift-foundation-icu repository will support these too, so users of Foundation on any platform will get these new calendars.
18+
19+
Some of these calendars are astronomical calendars, which means they are using true positions of sun (solar) or both sun and moon. For that reason, when specific set of events happen, the dates in these calendars can have both leap months and leap days.
20+
21+
Foundation will add support for these new identifiers, and all Foundation Calendar API that takes an identifier will be extended to support them.
22+
23+
## Proposed solution and example
24+
25+
Foundation will add constants to support the new calendar types.
26+
27+
Some calendars added by this proposal have a unique feature of "leap days", where two consecutive days can have the same numeric value. This new feature will have impact on APIs that search/match dates.
28+
29+
This property will be called isAdhikaDay, to indicate it is used in Hindu calendars which are the only calendars that has this feature. More details about the naming is provided in the section `Alternatives considered` at the end of the document.
30+
31+
## Detailed design
32+
33+
34+
Foundation will add new string constants for new calendar identifiers for NSCalendarIdentifier.
35+
36+
37+
**Calendar.swift**
38+
39+
```swift
40+
// ...
41+
extension Calendar.Identifier {
42+
// Bangla solar calendar
43+
@available(FoundationPreview 6.2, *)
44+
case bangla
45+
46+
// Gujarati lunisolar calendar
47+
@available(FoundationPreview 6.2, *)
48+
case gujarati
49+
50+
// Kannada lunisolar calendar
51+
@available(FoundationPreview 6.2, *)
52+
case kannada
53+
54+
// Malayalam solar calendar
55+
@available(FoundationPreview 6.2, *)
56+
case malayalam
57+
58+
// Marathi lunisolar calendar
59+
@available(FoundationPreview 6.2, *)
60+
case marathi
61+
62+
// Odia solar calendar
63+
@available(FoundationPreview 6.2, *)
64+
case odia
65+
66+
// Tamil solar calendar
67+
@available(FoundationPreview 6.2, *)
68+
case tamil
69+
70+
// Telugu lunisolar calendar
71+
@available(FoundationPreview 6.2, *)
72+
case telugu
73+
74+
// Vikram lunisolar calendar
75+
@available(FoundationPreview 6.2, *)
76+
case vikram
77+
78+
// Thai lunisolar calendar
79+
@available(FoundationPreview 6.2, *)
80+
case thai
81+
82+
// Vietnamese lunisolar calendar
83+
@available(FoundationPreview 6.2, *)
84+
case vietnamese
85+
86+
// Korean lunisolar calendar
87+
@available(FoundationPreview 6.2, *)
88+
case korean
89+
}
90+
```
91+
92+
Following are the code changes required to support new calendar property isAdhikaDay
93+
94+
95+
**Calendar.swift**
96+
97+
```swift
98+
// ...
99+
public enum Component : Sendable {
100+
// ...
101+
@available(FoundationPreview 6.2, *)
102+
case isAdhikaDay
103+
// ....
104+
}
105+
```
106+
107+
## Impact on existing code
108+
109+
Thai, Vietnamese and Korean calendars have no special considerations.
110+
111+
Calendars used in India are introducing the new field for the leap day. Clients using existing calendars won't be affected by this change. For new clients that use these calendars, we will handle the newly added leap day in the following places:
112+
113+
**First**, it will be set to false when a new `DateComponents` object is created.
114+
115+
```swift
116+
var components = DateComponents()
117+
```
118+
119+
**Second**, the comparison for calendar dates will account for this field. Two dates differs if they don't have the same value for isAdhikaDay
120+
121+
For example, the clients may currently use following check
122+
123+
```swift
124+
cal.compare(d1, d2)
125+
```
126+
127+
For Hindu calendars, this would only compare equal if they're both Adhika day or if they are both not. For non-Hindu calendars, isAdhikaDay property will be ignored.
128+
129+
**Third**, the calendar date arithmetic will be updated to correctly calculate the dates regarding `adhikaDay`. When working with Hindu calendars, as the leap days can occur at any position, looking for next day would have to involve recalculation.
130+
131+
As a general rule, behavior of `isAdhikaDay` will replicate `isLeapMonth` in APIs doing matching and searching.
132+
133+
For example, let's explain what is the expected behavior in this API that enumerate the next date
134+
135+
```swift
136+
public func nextDate(after date: Date, matching components: DateComponents, matchingPolicy: MatchingPolicy, repeatedTimePolicy: RepeatedTimePolicy = .first, direction: SearchDirection = .forward) -> Date? {
137+
var result: Date?
138+
enumerateDates(startingAfter: date, matching: components, matchingPolicy: matchingPolicy, repeatedTimePolicy: repeatedTimePolicy, direction: direction) { date, exactMatch, stop in
139+
result = date
140+
stop = true
141+
}
142+
return result
143+
}
144+
```
145+
146+
Clients using this function to enumerate the next date after `date`, matching the given date components, based on the `MatchingPolicy`, would get the following behavior depending on whether `date` is a leap day or not:
147+
148+
* If start is on a leap day
149+
150+
* `strict`: If components.isAdhikaDay is true, this gives you the next date that is also a leap day that shares the same day, month, year number (i.e. all those specified with the comps argument) as start. If only a subset of `DateComponents` is specified, for example only the month, this gives you the next date which is a leap day with same month. If no arguments are given for `DateComponents`, this gives the next date that is also a leap day. If components.isAdhikaDay is false, this function gives you the next date that matches the day/month/year number but one that is not a leap day.
151+
152+
* If start is not on a leap day
153+
154+
* `strict`: If components.isAdhikaDay is true, this gives you the next date that is a leap day that shares the same day, month, year number (i.e. all those specified with the comps argument) as start but a leap day. If components.isAdhikaDay is false, this function gives you the next date that matches the day/month/year number that is not a leap day.
155+
156+
For the `direction` and `repeatedTimePolicy`:
157+
158+
* `backward`: This flag does not affect how leap day search is handled, but merely changes the search direction so that it finds the match before start rather than after start.
159+
160+
* `first`: If there are two or more matching dates, and all their components are the same, including isAdhikaDay, the function returns the first date.
161+
162+
* `last`: Similar to `first`, but the function returns the last match.
163+
164+
165+
Following APIs for searching/matching/adding would follow the same logic
166+
167+
```swift
168+
public func dates(byMatching components: DateComponents,
169+
startingAt start: Date,
170+
in range: Range<Date>? = nil,
171+
matchingPolicy: MatchingPolicy = .nextTime,
172+
repeatedTimePolicy: RepeatedTimePolicy = .first,
173+
direction: SearchDirection = .forward) -> some (Sequence<Date> & Sendable)
174+
public func dates(byAdding components: DateComponents,
175+
startingAt start: Date,
176+
in range: Range<Date>? = nil,
177+
wrappingComponents: Bool = false) -> some (Sequence<Date> & Sendable)
178+
```
179+
180+
## Alternatives considered
181+
182+
As mentioned before, the leap day is a unique feature of some of new calendars. It appears when a certain astronomical position of Sun and Moon happens which means it can appear on any date. That differs from Gregorian leap day of February 29th, which happens every four years (approximately) and it is always on the same day.
183+
184+
This property will be called isAdhikaDay to clearly indicate it is related to Hindu lunisolar calendars. The alternative was to call it isLeapDay, but that may lead to confusion with Gregorian calendar
185+
186+
Another point of discussion was whether to use Bengali vs Bangla and Oriya vs Odia. There has been an effort to stop using the older colloquial names Bengali and Oriya and to switch to the now-accepted names Bangla and Odia. While most of the attention has gone to the language names, the same naming should also be used for the calendar names, despite them receiving less attention.

0 commit comments

Comments
 (0)