Ответ 1
Вам не хватает NSDayCalendarUnit
в
NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
-(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];
}
Когда я вызываю: [self endOfDay: [NSDate date]]; Я получаю первый месяц... Почему? Я использую эти два метода, потому что мне нужен интервал, начиная с первой секунды первой даты (beginOfDay: date1) до последней секунды второй даты (endOfDay: Date2)...
Вам не хватает NSDayCalendarUnit
в
NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];
// 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 Простой и более точный ответ.
Время начала: 00:00:00
Время окончания: 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)
В iOS 8+ это действительно удобно; ты можешь сделать:
let startOfDay = NSCalendar.currentCalendar().startOfDayForDate(date)
Чтобы получить конец дня, просто используйте методы NSCalendar для 23 часов, 59 минут, 59 секунд, в зависимости от того, как вы определяете конец дня.
// 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. (См. Раздел " Календарные расчеты")
Мои быстрые расширения для 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 5.1 - XCode 11 с классом Date
вместо NSDate
и Calender
вместо 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))!
}
}
Использование:
let myDate = Date()
let startOfDate = myDate.startOfDay
let endOfDate = myDate.endOfDay
Вам не нужно устанавливать компоненты в ноль, просто игнорируйте их:
-(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 Использование * XCode8
Apple удаляет NS
из имени класса, поэтому NSDate
может быть заменен на Date
. Вы можете получить предупреждение о компиляторе, если попытаетесь отбросить их, сказав, что они всегда будут терпеть неудачу, но они отлично работают, когда вы запускаете их на игровой площадке.
Я заменил сгенерированный NSDate
в основной модели данных на Date
, и они все еще работают.
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())!
}
}
Для меня ни один из ответов здесь, а также где работает stackoverflow. Чтобы начать сегодня, я сделал это.
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];
Обратите внимание на [gregorian setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
и [components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
.
Когда создается календарь, он инициализируется текущим часовым поясом и когда дата извлекается из его компонентов, поскольку NSDate не имеет часовой пояс, дата из текущего часового пояса рассматривается как часовой пояс UTC. Поэтому нам нужно установить часовой пояс перед извлечением компонентов, а затем при извлечении даты из этих компонентов.
В Swift 3 и выше
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)
}
}
Использование:
var currentDayStart = Date().startOfDayDate
var currentDayEnd = Date().endOfDayDate
Вам не хватает NSDayCalendarUnit
в компонентах.
Еще один способ получить результат:
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];
Objective-C
NSCalendar * calendar = [NSCalendar currentCalendar];
NSDate * startDate = [calendar startOfDayForDate:[NSDate date]];
NSLog(@"start date is %@", startDate);
Просто еще один способ, используя dateInterval(of:start:interval:for:)
Calendar
При возврате startDate
содержит начало дня и interval
количества секунд в дне.
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)
Так как iOS 8.0+ / macOS 10.12+ / tvOS 10.0+ / watchOS 3.0+
есть встроенная функция в Foundation, которую вы можете использовать из коробки. Нет необходимости реализовывать собственные функции.
public func startOfDay(for date: Date) -> Date
Итак, вы можете использовать его следующим образом:
let midnightDate = Calendar(identifier: .gregorian).startOfDay(for: Date())
Стоит помнить, что это учитывает часовой пояс устройства. Вы можете установить .timeZone
на calendar
, если хотите иметь, например, зону UTC.
Ссылка на справочные страницы Apple: https://developer.apple.com/reference/foundation/nscalendar/1417161-startofday.
Для быстрой 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!)")
Вот что я использую для 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())
Работает как шарм для меня. Вы можете добавить это расширение для даты начала и окончания по Date
, однако имейте в виду, что добавление расширения увеличивается время компиляции (если не в том же файле, класса), так что если вам это нужно только в одном месте или в одном классе... не используйте расширение.
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)!
}
}
Просто для справки, простой способ установить начало и конец дня в 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)")
Календарные единицы следует рассматривать как интервалы. В iOS 10 Calendar
есть несколько хороших методов для этого.
let day = Calendar.autoupdatingCurrent.dateInterval(of: .day, for: Date())
day?.end
day?.start
Вы можете использовать тот же метод, чтобы получить начало/конец любого календарного компонента (неделя/месяц/год и т.д.)