NSDate beginning of day and end of day
IosNsdateNsdatecomponentsIos Problem Overview
-(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) ...
Ios Solutions
Solution 1 - Ios
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
Solution 2 - Ios
Swift 5 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)
Extra
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: noon)!
let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: noon)!
let specificDate = Date("2020-01-01")
extension Date {
init(_ dateString:String) {
let dateStringFormatter = DateFormatter()
dateStringFormatter.dateFormat = "yyyy-MM-dd"
dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
let date = dateStringFormatter.date(from: dateString)!
self.init(timeInterval:0, since:date)
}
}
Solution 3 - Ios
You are missing NSDayCalendarUnit
in
NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
Solution 4 - Ios
In iOS 8+ this is really convenient; you can do:
let startOfDay: Date = Calendar.current.startOfDay(for: Date())
To get the end of day then just use the Calendar methods for 23 hours, 59 mins, 59 seconds, depending on how you define end of day.
// Swift 5.0
let components = DateComponents(hour: 23, minute: 59, second: 59)
let endOfDay = Calendar.current.date(byAdding: components, to: startOfDay)
Apple iOS NSCalendar Documentation. (See Section: Calendrical Calculations)
Solution 5 - Ios
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
}
}
Solution 6 - Ios
Swift 5.1 - XCode 11 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
Solution 7 - Ios
I think the most succinct way to do this in Swift is as follows:
extension Date {
func startOfDay() -> Date {
return Calendar.current.startOfDay(for: self)
}
func endOfDay() -> Date {
return Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: self) ?? self
}
}
Solution 8 - Ios
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
Solution 9 - Ios
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];
}
Solution 10 - Ios
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.
Solution 11 - Ios
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)
}
Solution 12 - Ios
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())!
}
}
Solution 13 - Ios
Objective-C
NSCalendar * calendar = [NSCalendar currentCalendar];
NSDate * startDate = [calendar startOfDayForDate:[NSDate date]];
NSLog(@"start date is %@", startDate);
Solution 14 - Ios
You are missing NSDayCalendarUnit
in the components.
Solution 15 - Ios
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];
Solution 16 - Ios
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!)")
Solution 17 - Ios
In Swift 5.3, both Calendar.current.startOfDay() and Calendar.current.date(bySettingHour:, minute:, second:, of:) are timezone specific. If you want your date to normalize to the same time regardless of timezone, you should use GMT time like this.
// Normalize time of aDate to 12:00 GMT
let aDate = Date()
let twelveGMT = 12 + TimeZone.current.secondsFromGMT() / 3600
let normalizedDate = Calendar.current.date(bySettingHour: twelveGMT, minute: 0, second: 0, of: aDate)
Solution 18 - Ios
Updating @Zelko's Answer to get the calendar:
extension Date {
func startOfDay(in calendar: Calendar = .current) -> Date {
calendar.startOfDay(for: self)
}
func endOfDay(in calendar: Calendar = .current) -> Date {
var components = DateComponents()
components.day = 1
components.second = -1
return calendar.date(byAdding: components, to: startOfDay(in: calendar))!
}
func startOfMonth(in calendar: Calendar = .current) -> Date {
let components = calendar.dateComponents([.year, .month], from: startOfDay(in: calendar))
return calendar.date(from: components)!
}
func endOfMonth(in calendar: Calendar = .current) -> Date {
var components = DateComponents()
components.month = 1
components.second = -1
return calendar.date(byAdding: components, to: startOfMonth(in: calendar))!
}
}
Note:
It is very important to use the correct calendar when your app supports more regions
Solution 19 - Ios
Just another way using dateInterval(of:start:interval:for:)
of Calendar
and the dedicated DateInterval struct
On return startDate
contains the start of the day and interval
the number of seconds in the day.
func dateInterval(of date : Date) -> DateInterval {
var startDate = Date()
var interval : TimeInterval = 0.0
Calendar.current.dateInterval(of: .day, start: &startDate, interval: &interval, for: date)
return DateInterval(start: startDate, duration: interval-1)
}
let interval = dateInterval(of: Date())
print(interval.start, interval.end)
Solution 20 - Ios
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.
Solution 21 - Ios
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.
Solution 22 - Ios
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)!
}
}
Solution 23 - Ios
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)")
Solution 24 - Ios
Since the Calendar
struct contains startOfDay(for:)
, I find it best to encapsulate an endOfDay(for:)
method within an extension on Calendar
rather than within Date
.
The following code can be placed within a Playground file for quick execution.
import Foundation
extension Calendar {
func endOfDay(for date: Date) -> Date {
// Get the start of the date argument.
let dayStart = self.startOfDay(for: date)
// Add one day to the start of the day
// in order to get the start of the following day.
guard let nextDayStart = self.date(byAdding: .day, value: 1, to: dayStart) else {
preconditionFailure("Expected start of next day")
}
// Create date components that will subtract a single
// second from the start of the next day. This will
// allow you to get the last hour, last minute, and last
// second of the previous day, which is the day for the
// date argument that was passed to this method.
var components = DateComponents()
components.second = -1
// Add the date components to the date for the next
// day, which will perform the subtraction of the single
// second. This will return the end of the day for the date
// that was passed into this method.
guard let dayEnd = self.date(byAdding: components, to: nextDayStart) else {
preconditionFailure("Expected end of day")
}
// Simply return the date value.
return dayEnd
}
}
To see the formatted date, create a DateFormatter
and print the output.
let formatter = DateFormatter() formatter.dateStyle = .long formatter.timeStyle = .long Calendar.current.endOfDay(for: Date()) // "Dec 31, 2020 at 11:59 PM"
Solution 25 - Ios
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?.start
day?.end.addingTimeInterval(-1)
You can use the same method, to get the start/end of any calendar component (week/month/year etc)
The caveat here is that there isn't a single point in time that can be defined as the end (given that we have a start). The above will give one second before the end of the day (23:59:59).