Gregorian Calendar

Hello everybody,

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.

Greetings

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.

1 Like

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?

I undestand it this way.

Today it is Sunday. Sunday is the first element in the array returned by weekdaySymbols. That matches.

But the first day of the week in this calendar is Monday, which is the second element in the said array.

This is my interpretation. The API really needs proper documentation. The status quo is completely inaceptable…

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. ;)

The behavior of a Calendar also depends on its locale. You may want to use Locale(identifier: "en_US_POSIX").

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 :wink: 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 :smile:

2 Likes

Hi all,

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.

Is it a bug, or is there a way to correct it?

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.

4 Likes