How can I set an NSDate object to midnight?

Your statement

The problem with the above method is that you can only set one unit of
time …

is not correct. NSCalendarUnit conforms to the RawOptionSetType protocol which
inherits from BitwiseOperationsType. This means that the options can be bitwise
combined with & and |.

In Swift 2 (Xcode 7) this was changed again to be
an OptionSetType which offers a set-like interface, see
for example Error combining NSCalendarUnit with OR (pipe) in Swift 2.0.

Therefore the following compiles and works in iOS 7 and iOS 8:

let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!

// Swift 1.2:
let components = cal.components(.CalendarUnitDay | .CalendarUnitMonth | .CalendarUnitYear, fromDate: date)
// Swift 2:
let components = cal.components([.Day , .Month, .Year ], fromDate: date)

let newDate = cal.dateFromComponents(components)

(Note that I have omitted the type annotations for the variables, the Swift compiler
infers the type automatically from the expression on the right hand side of
the assignments.)

Determining the start of the given day (midnight) can also done
with the rangeOfUnit() method (iOS 7 and iOS 8):

let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
var newDate : NSDate?

// Swift 1.2:
cal.rangeOfUnit(.CalendarUnitDay, startDate: &newDate, interval: nil, forDate: date)
// Swift 2:
cal.rangeOfUnit(.Day, startDate: &newDate, interval: nil, forDate: date)

If your deployment target is iOS 8 then it is even simpler:

let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)

Update for Swift 3 (Xcode 8):

let date = Date()
let cal = Calendar(identifier: .gregorian)
let newDate = cal.startOfDay(for: date)

Leave a Comment