-(NSDate *)beginningOfDay:(NSDate *)date
{
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
return [cal dateFromComponents:components];
}
-(NSDate *)endOfDay:(NSDate *)date
{
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
[components setHour:23];
[components setMinute:59];
[components setSecond:59];
return [cal dateFromComponents:components];
}
When I call : [self endOfDay:[NSDate date]]; I get the first of the month ... Why is that? I use this two methods because I need an interval that is from the first second of the first date (beginningOfDay:date1) to the last second of the second date (endOfDay:Date2) ...
You are missing NSDayCalendarUnit
in
NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
Start Of Day / End Of Day — Swift 4
// Extension
extension Date {
var startOfDay: Date {
return Calendar.current.startOfDay(for: self)
}
var endOfDay: Date {
var components = DateComponents()
components.day = 1
components.second = -1
return Calendar.current.date(byAdding: components, to: startOfDay)!
}
var startOfMonth: Date {
let components = Calendar.current.dateComponents([.year, .month], from: startOfDay)
return Calendar.current.date(from: components)!
}
var endOfMonth: Date {
var components = DateComponents()
components.month = 1
components.second = -1
return Calendar.current.date(byAdding: components, to: startOfMonth)!
}
}
// End of day = Start of tomorrow minus 1 second
// End of month = Start of next month minus 1 second
Swift 4 Simple and more precise answer.
Start time: 00:00:00
End time: 23:59:59.5
let date = Date() // current date or replace with a specific date
let calendar = Calendar.current
let startTime = calendar.startOfDay(for: date)
let endTime = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: date)
In iOS 8+ this is really convenient; you can do:
let startOfDay = NSCalendar.currentCalendar().startOfDayForDate(date)
To get the end of day then just use the NSCalendar methods for 23 hours, 59 mins, 59 seconds, depending on how you define end of day.
// Swift 2.0
let components = NSDateComponents()
components.hour = 23
components.minute = 59
components.second = 59
let endOfDay = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: startOfDay, options: NSCalendarOptions(rawValue: 0))
Apple iOS NSCalendar Documentation. (See Section: Calendrical Calculations)
My Swift extensions for NSDate:
Swift 1.2
extension NSDate {
func beginningOfDay() -> NSDate {
var calendar = NSCalendar.currentCalendar()
var components = calendar.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: self)
return calendar.dateFromComponents(components)!
}
func endOfDay() -> NSDate {
var components = NSDateComponents()
components.day = 1
var date = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: self.beginningOfDay(), options: .allZeros)!
date = date.dateByAddingTimeInterval(-1)!
return date
}
}
Swift 2.0
extension NSDate {
func beginningOfDay() -> NSDate {
let calendar = NSCalendar.currentCalendar()
let components = calendar.components([.Year, .Month, .Day], fromDate: self)
return calendar.dateFromComponents(components)!
}
func endOfDay() -> NSDate {
let components = NSDateComponents()
components.day = 1
var date = NSCalendar.currentCalendar().dateByAddingComponents(components, toDate: self.beginningOfDay(), options: [])!
date = date.dateByAddingTimeInterval(-1)
return date
}
}
Swift 4.2 - XCode 10 with Date
class instead of NSDate
and Calender
instead of NSCalender
extension Date {
var startOfDay : Date {
let calendar = Calendar.current
let unitFlags = Set<Calendar.Component>([.year, .month, .day])
let components = calendar.dateComponents(unitFlags, from: self)
return calendar.date(from: components)!
}
var endOfDay : Date {
var components = DateComponents()
components.day = 1
let date = Calendar.current.date(byAdding: components, to: self.startOfDay)
return (date?.addingTimeInterval(-1))!
}
}
Usage:
let myDate = Date()
let startOfDate = myDate.startOfDay
let endOfDate = myDate.endOfDay
You don't have to set up the components to zero, just ignore them:
-(NSDate *)beginningOfDay:(NSDate *)date
{
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date];
return [calendar dateFromComponents:components];
}
Swift 3
class func today() -> NSDate {
return NSDate()
}
class func dayStart() -> NSDate {
return NSCalendar.current.startOfDay(for: NSDate() as Date) as NSDate
}
class func dayEnd() -> NSDate {
let components = NSDateComponents()
components.day = 1
components.second = -1
return NSCalendar.current.date(byAdding: components as DateComponents, to: self.dayStart() as Date)
}
Swift3 Using *XCode8
Apple is removing the NS
from the class name so that NSDate
can be swapped out to Date
. You may get a compiler warning if you try to cast them saying they will always fail, but they work fine when you run them in the playground.
I replaced my generated NSDate
in core data model with Date
and they still work.
extension Date {
func startTime() -> Date {
return Calendar.current.startOfDay(for: self)
}
func endTime() -> Date {
var components = DateComponents()
components.day = 1
components.second = -1
return Calendar.current.date(byAdding: components, to: startTime())!
}
}
For me none of the answers here and else where on stackoverflow worked. To get start of today i did this.
NSCalendar * gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
[gregorian setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSDateComponents *components = [gregorian components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay fromDate:[NSDate date]];
[components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
NSDate *beginningOfToday = [gregorian dateFromComponents:components];
Note this [gregorian setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
and [components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
.
When a calendar is created it gets initialised with current timezone and when date is extracted from its components, since NSDate has no timezone, the date from current timezone is considered as UTC timezone. So we need to set the timezone before extracting components and later when extracting date from these components.
One more way to get result:
NSDate *date = [NSDate date];
NSDateComponents *components = [[NSDateComponents alloc] init];
components.day = [[NSCalendar currentCalendar] ordinalityOfUnit:(NSCalendarUnitDay) inUnit:(NSCalendarUnitEra) forDate:date];
NSDate *dayBegin = [[NSCalendar currentCalendar] dateFromComponents:components];
components.day += 1;
NSDate *dayEnd = [[NSCalendar currentCalendar] dateFromComponents:components];
You are missing NSDayCalendarUnit
in the components.
Objective-C
NSCalendar * calendar = [NSCalendar currentCalendar];
NSDate * startDate = [calendar startOfDayForDate:[NSDate date]];
NSLog(@"start date is %@", startDate);
Since iOS 8.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+
there is a built in function in the Foundation, which you can use out of the box. No need to implement own functions.
public func startOfDay(for date: Date) -> Date
So you can use it this way:
let midnightDate = Calendar(identifier: .gregorian).startOfDay(for: Date())
It's worth to remember, that this takes upon consideration the device time zone. You can set .timeZone
on calendar
if you want to have eg UTC zone.
Link to the Apple reference pages: https://developer.apple.com/reference/foundation/nscalendar/1417161-startofday.
Just another way using dateInterval(of:start:interval:for:)
of Calendar
On return startDate
contains the start of the day and interval
the number of seconds in the day.
func startAndEnd(of date : Date) -> (start : Date, end : Date) {
var startDate = Date()
var interval : TimeInterval = 0.0
Calendar.current.dateInterval(of: .day, start: &startDate, interval: &interval, for: date)
var endDate = startDate.addingTimeInterval(interval-1)
return (start : startDate, end : endDate)
}
let (start, end) = startAndEnd(of: Date())
print(start, end)
For swift 4
var calendar = Calendar.current
calendar.timeZone = NSTimeZone(abbreviation: "UTC")! as TimeZone
let dateAtMidnight = calendar.startOfDay(for: Date())
//For End Date
var components = DateComponents()
components.day = 1
components.second = -1
let dateAtEnd = calendar.date(byAdding: components, to: dateAtMidnight)
print("dateAtMidnight :: \(dateAtMidnight)")
print("dateAtEnd :: \(dateAtEnd!)")
This is what I use for Swift 4.2:
let calendar = Calendar.current
let fromDate = calendar.startOfDay(for: Date())
let endDate = calendar.date(bySettingHour: 23, minute: 59, second: 59, of: Date())
Works like a charm for me.
You could add this to an extension for start and end dates on Date
, however keep in mind that adding an extension increases compile time (unless in the same file as the class), so if you only need it at one place or in one class... don't use an extension.
In Swift 3 and above
extension Date {
var startOfDayDate: Date {
return Calendar.current.startOfDay(for: self)
}
var endOfDayDate: Date {
let nextDayDate = Calendar.current.date(byAdding: .day, value: 1, to: self.startOfDayDate)!
return nextDayDate.addingTimeInterval(-1)
}
}
Usage:
var currentDayStart = Date().startOfDayDate
var currentDayEnd = Date().endOfDayDate
extension Date {
func stringFrom(dateFormat: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = dateFormat
return formatter.string(from: self)
}
func firstSecondInDay() -> Date {
let dayStr = self.stringFrom(dateFormat: "yyyy-MM-dd")
let firstSecondStr = "\(dayStr) 00:00:00"
let format = DateFormatter()
format.dateFormat = "yyyy-MM-dd HH:mm:ss"
return format.date(from: firstSecondStr)!
}
func lastSecondInDay() -> Date {
let dayStr = self.stringFrom(dateFormat: "yyyy-MM-dd")
let laseSecondStr = "\(dayStr) 23:59:59"
let format = DateFormatter()
format.dateFormat = "yyyy-MM-dd HH:mm:ss"
return format.date(from: laseSecondStr)!
}
}
Just for reference, simple way to set Start and End of the day in Swift 4,
var comp: DateComponents = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute, .second], from: Date())
comp.hour = 0
comp.minute = 0
comp.second = 0
comp.timeZone = TimeZone(abbreviation: "UTC")!
//Set Start of Day
let startDate : Date = Calendar.current.date(from: comp)!
print(“Day of Start : \(startDate)")
//Set End of Day
comp.hour = 23
comp.minute = 59
comp.second = 59
let endDate : Date = Calendar.current.date(from:comp)!
print("Day of End : \(endDate)")
Calendar units should be thought of as intervals. As of iOS 10 Calendar
has some nice methods for this
let day = Calendar.autoupdatingCurrent.dateInterval(of: .day, for: Date())
day?.end
day?.start
You can use the same method, to get the start/end of any calendar component (week/month/year etc)
来源:https://stackoverflow.com/questions/13324633/nsdate-beginning-of-day-and-end-of-day