forked from swiftlang/swift-foundation
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit cd3d294
authored
WIP Gregorian Calendar in FoundationEssentials (swiftlang#303)
* WIP Gregorian Calendar in FoundationEssentials
Implement `dateComponents(from date:)`. The implementation largely follows that of ICU in calendar.cpp and gregocal.cpp. Julian day calculation algorithm is referenced [The Explanatory Supplement to the Astronomical Almanac](https://aa.usno.navy.mil/publications/exp_supp).
First, calculate the Julian day number from a given `Date`. Then, convert the Julian day number to Gregorian calendar date fields[^1]. The remaining part is the time within the day.
There are a few things to look out for
- Julian day starts at noon, while `Date` uses midnight as the reference time, so we need to adjust it acccordingly at every conversion. Note that ICU uses Modified Julian Day number throughout their codebase, which starts at midnight, so
- Dates close to Gregorian calendar adoption, which is also referred as "cutover" or "transition" date interchangeably, needs to be handled with care. Date before the adoption date were represented in Julian Calendar and Gregorian Calendar after the adoption date. The common Gregorian Calendar adoption date is Friday, 15 October 1582. It's also customizable in the API.
- The week number of a month and a year depends on `firstWeekday` and `minimumDaysInFirstWeek` values. The week starts at `firstWeekday`. Week one must contain `minimumDaysInFirstWeek` number of days.
[^1]: A nice readable version of the Julain - Gregorian converting algorithm: https://en.wikipedia.org/wiki/Julian_day#Julian_day_number_calculation.
* Disable a test that's not available until we implement the function in `GregorianCalendar`.
* Add a precondition for first weekday setter instead of silently dropping the value
* Gregorian Calendar part 2: Implement `date(from components:)`.
The implementation largely follows that of ICU. The logic follows this pattern generally
- Calculate the Julian day number from a given year, month, day of month number
- Add time field values if they are set
- Adjust for timezone offset
The complexity lies in the following situations
(1) The passed-in date components lacks essential fields to determine the Julian day number
(2) The date components has week-related fields set, but no year, month, day of month fields set
(3) The date components has both week-related fields and month, day of month fields set
(4) The day is near Gregorian calendar transition date
For (1), simply assume the start of the year/month/day if the field is missing. The value would be either 0 or 1, depending on if it's 0 or 1 indexed.
For (2), calculate the julian day of the start of the month. Advance by multiples of the given week number.
For (3), we need to determine which fields take over precedence of others. ICU tracks the order of when the fields are modified; newly modified ones always take precedence over past ones. We have not been using this mechanism at all in existing Calendar_ICU's implementation. Instead we've been setting the fields arbitrarily. Therefore, here we simplify ICU's heuristics by assuming the timestamps of modified fields are all equal.
For (4), recalculate the date using Julian calendar if the found date is before Gregorian transition date.1 parent 55dad24 commit cd3d294Copy full SHA for cd3d294
File tree
Expand file treeCollapse file tree
3 files changed
+858
-11
lines changedOpen diff view settings
Filter options
- Sources
- FoundationEssentials/Calendar
- TestSupport
- Tests/FoundationInternationalizationTests
Expand file treeCollapse file tree
3 files changed
+858
-11
lines changedOpen diff view settings
0 commit comments