Skip to content

Commit d72532e

Browse files
authored
Use _CalendarGregorian for ISO8601 (#1149)
1 parent 7e083cd commit d72532e

File tree

3 files changed

+52
-12
lines changed

3 files changed

+52
-12
lines changed

Sources/FoundationEssentials/Calendar/Calendar_Cache.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ dynamic package func _calendarICUClass() -> _CalendarProtocol.Type? {
2525
}
2626
#endif
2727

28-
func _calendarClass(identifier: Calendar.Identifier, useGregorian: Bool) -> _CalendarProtocol.Type? {
29-
if useGregorian && identifier == .gregorian {
28+
func _calendarClass(identifier: Calendar.Identifier) -> _CalendarProtocol.Type? {
29+
if identifier == .gregorian || identifier == .iso8601 {
3030
return _CalendarGregorian.self
3131
} else {
3232
return _calendarICUClass()
@@ -54,7 +54,7 @@ struct CalendarCache : Sendable, ~Copyable {
5454

5555
let id = Locale.current._calendarIdentifier
5656
// If we cannot create the right kind of class, we fail immediately here
57-
let calendarClass = _calendarClass(identifier: id, useGregorian: true)!
57+
let calendarClass = _calendarClass(identifier: id)!
5858
let calendar = calendarClass.init(identifier: id, timeZone: nil, locale: Locale.current, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil)
5959

6060
return _current.withLock {
@@ -90,7 +90,7 @@ struct CalendarCache : Sendable, ~Copyable {
9090
}
9191

9292
// If we cannot create the right kind of class, we fail immediately here
93-
let calendarClass = _calendarClass(identifier: id, useGregorian: true)!
93+
let calendarClass = _calendarClass(identifier: id)!
9494
let new = calendarClass.init(identifier: id, timeZone: nil, locale: nil, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil)
9595

9696
return _fixed.withLock {
@@ -106,7 +106,7 @@ struct CalendarCache : Sendable, ~Copyable {
106106
func fixed(identifier: Calendar.Identifier, locale: Locale?, timeZone: TimeZone?, firstWeekday: Int?, minimumDaysInFirstWeek: Int?, gregorianStartDate: Date?) -> any _CalendarProtocol {
107107
// Note: Only the ObjC NSCalendar initWithCoder supports gregorian start date values. For Swift it is always nil.
108108
// If we cannot create the right kind of class, we fail immediately here
109-
let calendarClass = _calendarClass(identifier: identifier, useGregorian: true)!
109+
let calendarClass = _calendarClass(identifier: identifier)!
110110
return calendarClass.init(identifier: identifier, timeZone: timeZone, locale: locale, firstWeekday: firstWeekday, minimumDaysInFirstWeek: minimumDaysInFirstWeek, gregorianStartDate: gregorianStartDate)
111111
}
112112

Sources/FoundationEssentials/Calendar/Calendar_Gregorian.swift

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,27 @@ internal final class _CalendarGregorian: _CalendarProtocol, @unchecked Sendable
191191

192192
let inf_ti : TimeInterval = 4398046511104.0
193193

194-
// FIXME: Support other Gregorian-calendar family such as ISO8601
195-
// Only respects Gregorian identifier
196194
init(identifier: Calendar.Identifier, timeZone: TimeZone?, locale: Locale?, firstWeekday: Int?, minimumDaysInFirstWeek: Int?, gregorianStartDate: Date?) {
197195

198-
self.timeZone = timeZone ?? TimeZone.default
196+
// ISO8601 has different default values for time zone, locale, firstWeekday, and minimumDaysInFirstWeek
197+
let defaultTimeZone: TimeZone
198+
let defaultLocale: Locale?
199+
let defaultFirstWeekday: Int?
200+
let defaultMinimumDaysInFirstWeek: Int?
201+
202+
if identifier == .iso8601 {
203+
defaultTimeZone = .gmt
204+
defaultLocale = Locale.unlocalized
205+
defaultFirstWeekday = 2
206+
defaultMinimumDaysInFirstWeek = 4
207+
} else {
208+
defaultTimeZone = .default
209+
defaultLocale = nil
210+
defaultFirstWeekday = nil
211+
defaultMinimumDaysInFirstWeek = nil
212+
}
213+
214+
self.timeZone = timeZone ?? defaultTimeZone
199215
if let gregorianStartDate {
200216
self.gregorianStartDate = gregorianStartDate
201217
do {
@@ -212,10 +228,12 @@ internal final class _CalendarGregorian: _CalendarProtocol, @unchecked Sendable
212228
self.gregorianStartDate = Date(timeIntervalSince1970: -12219292800) // 1582-10-15T00:00:00Z
213229
}
214230

215-
self.locale = locale
231+
self.locale = locale ?? defaultLocale
216232

217233
if let firstWeekday, (firstWeekday >= 1 && firstWeekday <= 7) {
218234
_firstWeekday = firstWeekday
235+
} else if let defaultFirstWeekday {
236+
_firstWeekday = defaultFirstWeekday
219237
}
220238

221239
if var minimumDaysInFirstWeek {
@@ -225,12 +243,14 @@ internal final class _CalendarGregorian: _CalendarProtocol, @unchecked Sendable
225243
minimumDaysInFirstWeek = 7
226244
}
227245
_minimumDaysInFirstWeek = minimumDaysInFirstWeek
246+
} else if let defaultMinimumDaysInFirstWeek {
247+
_minimumDaysInFirstWeek = defaultMinimumDaysInFirstWeek
228248
}
249+
250+
self.identifier = identifier
229251
}
230252

231-
var identifier: Calendar.Identifier {
232-
.gregorian
233-
}
253+
let identifier: Calendar.Identifier
234254

235255
var locale: Locale?
236256

Tests/FoundationEssentialsTests/GregorianCalendarTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3876,5 +3876,25 @@ final class GregorianCalendarTests : XCTestCase {
38763876
test(.minute, expected: -120)
38773877
test(.second, expected: -7200)
38783878
}
3879+
3880+
// MARK: ISO8601
3881+
3882+
func test_iso8601Gregorian() {
3883+
var calendar1 = Calendar(identifier: .iso8601)
3884+
let calendar2 = Calendar(identifier: .iso8601)
3885+
XCTAssertEqual(calendar1, calendar2)
3886+
3887+
XCTAssertEqual(calendar1.firstWeekday, 2)
3888+
XCTAssertEqual(calendar1.minimumDaysInFirstWeek, 4)
3889+
XCTAssertEqual(calendar1.timeZone, .gmt)
3890+
XCTAssertEqual(calendar1.locale, .unlocalized)
3891+
3892+
// Verify that the properties are still mutable
3893+
let tz = TimeZone(secondsFromGMT: -3600)!
3894+
calendar1.timeZone = tz
3895+
XCTAssertNotEqual(calendar1, calendar2)
3896+
3897+
XCTAssertEqual(calendar1.timeZone, tz)
3898+
}
38793899
}
38803900

0 commit comments

Comments
 (0)