I'm trying to get a standard gregorian calendar. To my understanding
let cal = Calendar(identifier: .gregorian)
should give me a standard gregorian calendar.
However cal.component(.weekday, from: Date()) is returning wrong values.
cal.weekdaySymbols returns ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], so it seems that the week is starting on Sundays. However according to international standards, namely ISO 8601:2004 which is cited in the documentation of NSCalendar to which the Calendar object bridges, a week should start with Monday as day 1 and end with Sunday as day 7.
From ISO 8601:2004
calendar day of the week is represented by one decimal digit. Monday shall be identified as calendar day [1] of any calendar week, and subsequent calendar days of the same calendar week shall be numbered in ascending sequence to Sunday (calendar day [7]).
This was causing some serious confusion when communicating with an API that is parsing dates according to the ISO standard. Took me a while to pin the problem to the calendar object ...
Am I missing something? Is this not the correct way to get a standard calendar?
Thanks for your help.
The order of the symbols returned by weekdaySymbols always remains the same and starts with sunday, regardless at which day the week really starts.
At which day the week starts is determined by the Calendar property firstWeekday. For Calendar(identifier: .gregorian) this returns 1 and for Calendar(identifier: .iso8601) it's 2. It doesn't seem to be documented what that means, but it seems that this value maps to the weekday symbols.
Without knowing more details I guess you could 'just' use Calendar(identifier: .iso8601) and be fine.
Thanks!
It seems I've missed the .iso8601 option in Calendar.Identifier
But just out of curiosity: If .iso8601 is the standardized Gregorian calendar, what is .gregorian? Does anybody know where the behaviour of .gregorian is specified?
Edit:
I just checked checked your solution. This still doesn't work. firstWeekday indeed returns 2 now. But the .weekday component for today (which is Sunday) still returns 1, which, despite the identifier, is definitely not ISO8601 compliant.
let cal = Calendar(identifier: .iso8601)
let date = ISO8601DateFormatter().date(from: "2018-11-11T12:00:00+0000")
print(cal.firstWeekday) // prints 2
print(cal.component(.weekday, from: date!)) // prints 1
Is this a bug in the Core Library or am I holding it wrong?
This gives me the correct index for weekdaySymbols but not the integer representation for day of week according to the standard. Isn't this kind of confusing? One would expect that, when using a calendar called .iso8601, one would get values as described in ISO8601, right?
And when using a julian, buddhist or islamic calendar would you still expect ISO8601 behaviour? The calendar API is generic over many (cough actually just a few) different calendar systems.
And in addition the API is much, much older than that ISO standard. ;)
No. I would expect a behavior that is appropriate for the respective calendar. That's exactly the problem here. The weekday component does not behave in a generic way. It just always returns the day of week for a Gregorian calendar with an en_US locale. You're right about the documentation. I think it should reflect that weekday is not the day of the week here, but just an index for the weekdaySymbol property.
At least for me the word weekday suggests that the function returns the (ordinal) day of the week. Which is actually true for a Gregorian calendar with an en_US locale, but not true for a lot of other configurations. Since a lot of developers are working with that exact setup this is easily overlooked and might actually result in some problems when the software is deployed in other contexts That's exactly what happened to me.
Thanks! That's good to know.
Anyways, I think I found a generic way to get the actual day of week for any given calendar and locale: cal.ordinality(of: .weekday, in: .weekOfYear, for: date)!
Confusingly, that is not the same as cal.component(.weekdayOrdinal, from: date) which is something else entirely.
Thanks for your help figuring this out. When appropriate, it's probably safer to work with the ISO8601DateFormatter, which seems more straightforward, than with the Calendar(identifier: .iso8601) API
I have a web app wich shows a calendar. Each day div has ID = ( GregorianCalendar ) day .getTimeInMillis(); so that I can easily get the div of following days if I need to do some js modifications to them by adding or subtracting 86400000, the amount of milliseconds in a day
All works smoothly apart from a few dates. Between 27th and 28th of January 2023, for example. Turns out that the number of milliseconds between the two is not the expected 86400 but 82800.
That difference is 3600 seconds, i.e. exactly one hour, which presumably reflects a Daylight Saving's transition in the appropriate timezone. This is part of why calendar manipulation is a tricky problem — all sorts of relationships between units that are usually true turn out to have unexpected exceptions. It's important to use a well-designed calendar API that lets you do the high-level manipulations that you actually want, which in this case seems to be adding and subtracting whole days, rather than assuming that you can equivalently scale up the math on a smaller unit.