This is how I generate date:
private var date: Date? {
let components = DateComponents(
year: 2023,
month: 2,
day: 31,
hour: 11,
minute: 14
)
return Calendar.current.date(from: components)
}
What is the output?
3 Mar 2023, 11:14
What to do to have nil returned when day components are out of expected number of days for selected month? Is there any parameter for this? Or should I calculate it manually?
3
Answers
This question of “why” is difficult to answer. Apple didn’t document why. The old Date and Time Programming Guide does in fact say that what you’re doing is undefined behavior:
We could guess that it’s undefined behavior, rather than explicitly producing an error, because that simplifies the interface (it doesn’t need a way to return an error) and simplifies the implementation (no need to check for out-of-range values).
Digging deeper, it’s helpful to know that Foundation’s
Calendar
implementation, like all of Foundation’s locale support, is based on the International Components for Unicode library (ICU). You can find the relevant Foundation source code here. The ICU source code is here.It turns out that the underlying ICU object, a C++
icu::Calendar
, has a “lenient” flag, which is true by default, and which produces the behavior you’re seeing:Foundation’s
Calendar
creates anicu::Calendar
and never changes its lenient flag, so you get lenient behavior.Why is lenient the default? I don’t know if that’s documented anywhere.
This has struck me as strange before, and while I’m sure there is a logic behind it, rather than just basic maths, I’ve never worked out what it is. My workaround is:
I feel like I shouldn’t need to, but I’ve not found a practical solution to avoid it yet.
Maybe DateComponents is a direct bridge from NSDateComponents, and as stated in the oficial documentation here
Also another importante note in the same page is:
The better approach is to use the Struct Date.ComponentsFormatStyle here using the property fields here to set the date components.