What is the most efficient way to obtain an NSDate object that represents midnight of the current day?
10 Answers
New API in iOS 8
iOS 8 includes a new method on NSCalendar called startOfDayForDate, which is really easy to use:
let startOfToday = NSCalendar.currentCalendar().startOfDayForDate(NSDate())
Apple's description:
This API returns the first moment date of a given date. Pass in [NSDate date], for example, if you want the start of "today". If there were two midnights, it returns the first. If there was none, it returns the first moment that did exist.
Update, regarding time zones:
Since startOfDayForDate is a method on NSCalendar, it uses the NSCalendar's time zone. So if I wanted to see what time it was in New York, when today began in Los Angeles, I could do this:
let losAngelesCalendar = NSCalendar.currentCalendar().copy() as! NSCalendar
losAngelesCalendar.timeZone = NSTimeZone(name: "America/Los_Angeles")!
let dateTodayBeganInLosAngeles = losAngelesCalendar.startOfDayForDate(NSDate())
dateTodayBeganInLosAngeles.timeIntervalSince1970
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .MediumStyle
dateFormatter.timeStyle = .ShortStyle
dateFormatter.timeZone = NSTimeZone(name: "America/New_York")!
let timeInNewYorkWhenTodayBeganInLosAngeles = dateFormatter.stringFromDate(dateTodayBeganInLosAngeles)
print(timeInNewYorkWhenTodayBeganInLosAngeles) // prints "Jul 29, 2015, 3:00 AM"

- 8,310
- 3
- 49
- 52
-
Does this handle timezones? – ftvs Jul 29 '15 at 07:32
-
1@ftvs, yes, since startOfDayForDate() is a method on NSCalendar, you can set the timeZone on your NSCalendar instance. The example code I included above uses NSCalender.currentCalendar() which uses the user's current time zone. You can create your own NSCalendar and set the time zone to different values. – Richard Venable Jul 29 '15 at 15:57
-
2@ftvs, I updated my answer with more info about time zones. – Richard Venable Jul 29 '15 at 16:11
-
4This should be the accepted answer, especially for iOS 8 and later. – ndmeiri Aug 18 '15 at 19:18
-
1@ndmeri why should this be the accepted answer? It is an answer for using Swift yes but does not provide an answer for Objective C. – wuf810 Nov 24 '15 at 17:11
-
@wuf810, the startOfDayForDate: method is also available in ObjC. – Richard Venable Nov 30 '15 at 19:11
-
Renamed to `public func startOfDay(for date: Date) -> Date` in newer versions of swift – David Dec 07 '20 at 20:45
Try this:
NSDate *const date = NSDate.date;
NSCalendar *const calendar = NSCalendar.currentCalendar;
NSCalendarUnit const preservedComponents = (NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay);
NSDateComponents *const components = [calendar components:preservedComponents fromDate:date];
NSDate *const normalizedDate = [calendar dateFromComponents:components];

- 10,768
- 8
- 48
- 83
-
Doens't work for me. For my current date `2013-08-22 10:05:45 +0000` is returns `2013-08-21 22:00:00 +0000`. Then i have used `NSDateComponents` and have send `setHour`, `setMinute` and `setHour` with 0 and still no good. – Martin Berger Aug 22 '13 at 10:14
-
1The date string you posted here was apparently logged in the simulator. This automatically adapts to your timezone, the actual date stored is at midnight though. – Christian Schnorr Aug 22 '13 at 10:49
-
1
-
-
I get correct time reduced by two hours. That's what i noticed. But i have set time zone with `[calendar setTimeZone:[NSTimeZone systemTimeZone]];`. – Martin Berger Aug 22 '13 at 12:11
-
2That was my point. `NSLog(@"%@", date);` is sending it a `-description` message which is basically the same as `NSLog`ging it into the console. If you actually ask the date for its `NSHourCalendarUnit` you will see that it is a midnight date. – Christian Schnorr Aug 22 '13 at 12:39
-
Solution: [SO link](http://stackoverflow.com/questions/7325837/nscalendar-datefromcomponents-gmt-timezone-instead-of-systemtimezone) – Martin Berger Aug 22 '13 at 12:59
-
4
-
3Looks like since iOS 8, the components are now NSCalendarUnitYear, NSCalendarUnitMonth, and NSCalendarUnitDay in case anyone gets a deprecation warning. – Shalmezad May 18 '15 at 17:19
NSCalendar *cal = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
[cal setTimeZone:[NSTimeZone systemTimeZone]];
NSDateComponents * comp = [cal components:( NSYearCalendarUnit| NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit) fromDate:[NSDate date]];
[comp setMinute:0];
[comp setHour:0];
[comp setSecond:0];
NSDate *startOfToday = [cal dateFromComponents:comp];
If you mean midnight as 23:59 then set component's hour as 23 and minutes as 59.

- 1,304
- 8
- 27

- 8,740
- 10
- 59
- 80
Swift:
let cal = NSCalendar.currentCalendar()
//tip:NSCalendarUnit can be omitted, but with the presence of it, you can take advantage of Xcode's auto-completion
var comps = cal.components(NSCalendarUnit.YearCalendarUnit | .MonthCalendarUnit | .DayCalendarUnit | .HourCalendarUnit | .MinuteCalendarUnit | .SecondCalendarUnit, fromDate: NSDate())
comps.hour = 0
comps.minute = 0
comps.second = 0
let midnightOfToday = cal.dateFromComponents(comps)!
Swift 2.2:
let cal = NSCalendar.currentCalendar()
let comps = cal.components([.Year, .Month, .Day, .Hour, .Minute, .Second], fromDate: NSDate())
comps.hour = 0
comps.minute = 0
comps.second = 0
let midnightOfToday = cal.dateFromComponents(comps)!
Objective-C:
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *comps = [cal components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute) fromDate:[NSDate date]];
[comps setHour:0];
[comps setMinute:0];
[comps setSecond:0];
NSDate *midnightOfToday = [cal dateFromComponents:comps];

- 18,750
- 9
- 86
- 81

- 5,168
- 1
- 36
- 32
You could use the following method to get the midnight value for an NSDate.
- (NSDate *)dateAtBeginningOfDayForDate:(NSDate *)inputDate
{
// Use the user's current calendar and time zone
NSCalendar *calendar = [NSCalendar currentCalendar];
NSTimeZone *timeZone = [NSTimeZone systemTimeZone];
[calendar setTimeZone:timeZone];
// Selectively convert the date components (year, month, day) of the input date
NSDateComponents *dateComps = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:inputDate];
// Set the time components manually
[dateComps setHour:0];
[dateComps setMinute:0];
[dateComps setSecond:0];
// Convert back
NSDate *beginningOfDay = [calendar dateFromComponents:dateComps];
return beginningOfDay;
}
Thkis is taken from here website.

- 5,183
- 2
- 24
- 31
From iOS8, you can use startDayForDate.
So, in order to get the start of today (Objective -C):
NSDate * midnight;
midnight = [[NSCalendar currentCalendar] startOfDayForDate: [NSDate date]];

- 2,407
- 4
- 29
- 35
NSDate *now = [NSDate date];
NSDate *beginningOfToday = nil;
[[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit startDate:&beginningOfToday interval:NULL forDate:now];

- 10,295
- 11
- 80
- 129
try this:
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *now = [NSDate date];
NSDateComponents *components = [gregorian components:NSDayCalendarUnit | NSMonthCalendarUnit | NSYearCalendarUnit fromDate:now];
NSDateFormatter* df = [[NSDateFormatter alloc]init];
[df setDateFormat:@"MM/dd/yyyy HH:mm:ss.SSS"];
NSLog(@"%@",[df stringFromDate:[gregorian dateFromComponents:components]]);

- 1,114
- 13
- 23
let calendar = NSCalendar.currentCalendar()
let unitFlags: NSCalendarUnit = [.Year, .Month, .Day, .Minute, .Second]
let components = calendar.components(unitFlags , fromDate: NSDate())
components.hour = 0
components.minute = 0
components.second = 0
//Gives Midnight time of today
let midnightOfToday = calendar.dateFromComponents(components)!

- 2,581
- 2
- 32
- 34

- 23
- 6
You could use NSCalendar's dateBySettingHour:minute:second:ofDate:options:
So it would be as easy as doing:
NSCalendar *calendar = [NSCalendar currentCalendar];
calendar.timeZone = [NSTimeZone timeZoneWithName:@"UTC"];
NSDate *midnight = [calendar dateBySettingHour:0 minute:0 second:0 ofDate:[NSDate date] options:0];

- 555
- 1
- 6
- 17