skip to Main Content

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


  1. 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:

    To guarantee correct behavior you must make sure that the components used make sense for the calendar. Specifying “out of bounds” components—such as a day value of -6 or February 30th in the Gregorian calendar—produce 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:

    With lenient interpretation, a date such as "February 942, 1996" will be treated as being equivalent to the 941st day after February 1, 1996. With strict interpretation, such dates will cause an error when computing time from the time field values representing the dates.

    Foundation’s Calendar creates an icu::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.

    Login or Signup to reply.
  2. 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:

    private var date: Date? {
        let components = DateComponents(
            year: 2023,
            month: 2,
            day: 31,
            hour: 11,
            minute: 14
        )
        guard components.isValidDate else {return nil}
        return Calendar.current.date(from: components)
    }
    

    I feel like I shouldn’t need to, but I’ve not found a practical solution to avoid it yet.

    Login or Signup to reply.
  3. Maybe DateComponents is a direct bridge from NSDateComponents, and as stated in the oficial documentation here

    Important

    An NSDateComponents object is meaningless in itself; you need to know
    what calendar it is interpreted against, and you need to know whether
    the values are absolute values of the units, or quantities of the
    units.

    Also another importante note in the same page is:

    Important

    The Swift overlay to the Foundation framework provides the
    DateComponents structure, which bridges to the NSDateComponents class.
    For more information about value types, see Working with Cocoa
    Frameworks in Using Swift with Cocoa and Objective-C (Swift 4.1).

    The better approach is to use the Struct Date.ComponentsFormatStyle here using the property fields here to set the date components.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search