Reputation: 6433
-(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) ...
Upvotes: 131
Views: 76865
Reputation: 3971
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 startOfWeek: Date {
Calendar.current.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: self).date!
}
var endOfWeek: Date {
var components = DateComponents()
components.weekOfYear = 1
components.second = -1
return Calendar.current.date(byAdding: components, to: startOfWeek)!
}
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 week = Start of next week minus 1 second
// End of month = Start of next month minus 1 second
Upvotes: 283
Reputation: 120092
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))!
}
}
It is very important to use the correct calendar when your app supports more regions
Upvotes: 3
Reputation: 1379
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)
NSCalendar methods discussed by NSHipster.
Upvotes: 33
Reputation: 6049
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"
Upvotes: 0
Reputation: 894
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).
Upvotes: -1
Reputation: 285270
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)
Upvotes: 1
Reputation: 41
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)
Upvotes: 4
Reputation: 1092
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
}
}
Upvotes: 12
Reputation: 1279
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)
}
}
Upvotes: 44
Reputation: 761
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
Upvotes: 17
Reputation: 1913
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
Upvotes: 7
Reputation: 10329
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.
Upvotes: 1
Reputation: 10752
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)")
Upvotes: 0
Reputation: 15991
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!)")
Upvotes: 2
Reputation: 4196
Objective-C
NSCalendar * calendar = [NSCalendar currentCalendar];
NSDate * startDate = [calendar startOfDayForDate:[NSDate date]];
NSLog(@"start date is %@", startDate);
Upvotes: 4
Reputation: 2192
You are missing NSDayCalendarUnit
in
NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
Upvotes: 35
Reputation: 3905
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)!
}
}
Upvotes: 0
Reputation: 1414
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())!
}
}
Upvotes: 4
Reputation: 12968
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.
Upvotes: 1
Reputation: 5230
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)
}
Upvotes: 4
Reputation: 14102
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
}
}
Upvotes: 17
Reputation: 514
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.
Upvotes: 5
Reputation: 18825
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];
Upvotes: 2
Reputation: 336
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];
}
Upvotes: 4