Writing software that has to deal with dates and times is, generally, a traumatic horror. There are so many complications - time zones, daylight savings, leap seconds, different calendar systems, calendrical changes... the list just goes on and on and on. Between Falsehoods programmers believe about time and its sequel post, it gets ugly and it gets ugly fast.
One of the fun cases is the "week year," and around this time of year, there are usually a handful of posts on social media warning developers to check for the use of "YYYY"
(the year of the current week) where they likely intended to use "yyyy"
(the year of the current date) instead.
Essentially, the ISO standard for dates includes a separate calendar for accounting and business purposes where every week is 7 days and every year is 52 or 53 full weeks. The new week always starts on a Monday, and there are a few rules as to whether a year is a "long" year, i.e. whether the year has 53 weeks or not. This calendar only approximately aligns with the Gregorian calendar - there is no guarantee that the 31st of December or the 1st of January in this system has the same year number as that date in the Gregorian calendar.
And just to confuse people, unlike other calendar that have their own year counter (e.g. in the Hebrew calendar it is the year 5781), the ISO week year calendar and the Gregorian calendar use the same year counter, except during the week that the Gregorian year changes.
Luckily for me, in my day job I primarily use .NET which only has "yyyy"
, but its still easy to get mixed up in other areas, such as swapping "MM"
(the current month) for "mm"
(the current minute).
This year the whole issue first caught my attention when I ordered something from Apple on the 25th of December 2020, and the shipping information stated that it would be delivered on the 30th of December 2021.
I really didn't expect that it would take Apple 12 months to deliver a simple accessory, so it was most likely a date/time bug, and probably related to the common and insidious "YYYY"
since that tends to crop up around this time of year.
However, if we check the definition of the ISO week system, we would learn that the last week of the year contains the last Thursday of the Gregorian year. This year, that date is New Year's Eve, the 31st of December 2020... which means that the last week of the ISO week year for 2020 is the 28th of December through to the 3rd of January 2021.
Those 7 days make up week 53 of 2020... if you thought when the clock strikes midnight on the 1st of January that 2020 is now over, ISO 8601 would like to have a word with you.
So that means that 30 December 2020 should still belong to the week-year of 2020, and that the bug is somewhat reversed this year - instead of people's code showing 2021 in late December, it would show 2020 in early January - on the 1st of January, for example, it would say "1st January 2020".
Let's try this out in a Swift Playround:
That's... not what I expected from the ISO standard.
And to be totally honest, I'm not sure what is happening here.
If we call calendar.component(.yearForWeekOfYear, from: now)
, we get back 2020
, which is what I expect. But if we format using YYYY
, we get 2021
.
There's a big warning on Apple's documentation that suggests that this is dependent on the current Locale:
The values ofweekOfYear
andyearForWeekOfYear
depend on the calendar they are used with. For example, the ISO 8601 calendar starts a week on Monday, and uses the first Thursday to determine the first week of the year. However, the Gregorian calendar as used in North America starts the week on Sunday, and uses the year’s first Saturday to determine the first week. - Apple Developer
However, I saw the .yearForWeekOfYear
-versus-YYYY
discrepancy regardless of locale (I tested every locale in macOS 11) or relevant calendar (I tested both .iso8601
and .gregorian
)
So the Swift runtime knows that the current week in a week-of-year system is 53, and that the current year is still 2020, but YYYY
neverless results in 2021.
I guess that's just one more reason to avoid YYYY
- not only is it not what you'd want, but it can also result in completely unexpected output even if you do want the week-of-year output.
(As a bonus, in some locales, yyyy
results in non-Latin numbers, i.e. not 0-9, whereas YYYY
always uses Latin numbers. Again, I don't know why YYYY
does this.)
As a general rule, developers in the Apple world shouldn't be using dateFormat
anyway, but should be letting the runtime generate the correct format based on a standard style, or template string.
Otherwise, yyyy
is usually what you want, and YYYY
is simply a wasp's nest of bugs waiting to happen.
Thanks to Paul Goracke, Luke Redpath, and @_karan for pointing me to some of this information.