NSDate начало дня и конец дня


104
    -(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) ...


2
Если вы хотите установить часы на нулевое время в формате UTC, более простой способ получить timeIntervalSince ..., усечь часы, а затем преобразовать обратно в NSDate. И это будет работать для другого часового пояса, если вы сначала отрегулируете временной интервал с помощью secondsFromGMT часового пояса, а затем отрегулируете противоположным образом после усечения. (Конец дня, очевидно, на 23: 59: 59.999 позже, что можно получить простым сложением, пока у вас есть временной интервал.)
Hot Licks

Указание "конца дня" как произвольного времени до его фактического окончания (в данном случае одна секунда) кажется рецептом для сбойного программного обеспечения. Если использовать 1 мс, глюки будут реже. Или вы можете использовать 23 часа, чтобы с большей вероятностью были обнаружены ошибки. :-)
Эдвард Брей

Ответы:


33

Вы пропали NSDayCalendarUnitв

NSDateComponents *components = [cal components:( NSMonthCalendarUnit | NSYearCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit ) fromDate:date];

1
У меня странное поведение: если я добавлю компонент дня, я получу 23:00 предыдущего дня.
Mike M

3
@Mike использует часовой пояс: components.timeZone = [NSTimeZone timeZoneWithName: @ "GMT"];
dimaxyu

@dimaxyu прав. В быстром темпе: components.timeZone = NSTimeZone (name: "GMT")
Том Бевеландер

221

Начало дня / конец дня - 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

1
NSYearCalendarUnit, NSMonthCalendarUnitИ NSDayCalendarUnitбыло устаревшим IOS 8. Используйте NSCalendarUnitYear, NSCalendarUnitMonthи NSCalendarUnitDayвместо этого!
T Blank

6
Для начала дня используйте [[NSCalendar currentCalendar] startOfDayForDate: ...] для iOS8 +
GingerBreadMane

5
Это возвращает startOfDay в текущем часовом поясе! Ожидается, что NSDate будет в формате UTC, но этот конвертер возвращает время, скорректированное для часового пояса по умолчанию.
Tom Bevelander

3
Почему endOfDay необязателен? Существуют ли ситуации, когда startOfDay не равен нулю, а endOfDay может быть равен нулю?
Мансуров Руслан

1
Итак, в зависимости от того, как вы это используете, вы
должны

29

Swift 5 Простой и точный ответ.

Время начала: 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)

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)
    }
}

2
Чем это отличается от let dateAtEnd = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: Date())?
Андрес Канелла

1
Нет разницы .
Август Лин

21

В 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 . (См. Раздел: Календарные вычисления )

Методы NSCalendar, обсуждаемые NSHipster .


17

Мои расширения Swift для 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
    }
}

Вы можете просто установить для секунды значение -1 и получить то же самое без использования dateByAddingTimeInterval
Бен Синклер

чтобы избежать проблем с часовым поясом, добавьте: components.timeZone = NSTimeZone (name: "GMT")
Том Бевеландер

12

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

Я получаю сообщение об ошибке необъявленного типа, Dateкогда пробую его на детской площадке
Джон Доу

1
Здесь у меня работает. Вы импортировали UIKit с import UIKit?
Бен

2
@Ben Date and Calendar отсутствует в UIKit, он в Foundation, однако UIKit импортирует Foundation
Arbitur

4

Вам не нужно обнулять компоненты, просто игнорируйте их:

-(NSDate *)beginningOfDay:(NSDate *)date
{
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *components = [calendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:date];
    return [calendar dateFromComponents:components];
}

2
"Новые" доступны:, [calendar startOfDayForDate:date]но -endOfDayForDate:, к сожалению , их нет ...
Джулиан Ф. Вайнерт

4

Для меня ни один из ответов здесь и еще где работал 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. Поэтому нам нужно установить часовой пояс перед извлечением компонентов, а затем при извлечении даты из этих компонентов.


4

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)
    }

4

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())!
  }
}

4

В 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

2

Еще один способ получить результат:

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];


2

Цель-C

NSCalendar * calendar = [NSCalendar currentCalendar];
NSDate * startDate = [calendar startOfDayForDate:[NSDate date]];
NSLog(@"start date is %@", startDate);

2

Для 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!)")

2

Я думаю, что наиболее лаконичный способ сделать это в Swift выглядит следующим образом:

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)
    }
}

Отлично! Спасибо!
Баран Эмре,

1

Поскольку 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 .


1

Еще один способ использования 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)

1

Вот что я использую для 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, однако имейте в виду, что добавление расширения увеличивает время компиляции (если оно не находится в том же файле, что и класс), поэтому, если оно вам нужно только в одном месте или в одном классе. .. не используйте расширение.


0
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)!
    }
}

0

Просто для справки, простой способ установить начало и конец дня в 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)")

-1

Календарные единицы следует рассматривать как интервалы. Начиная с iOS 10 Calendarесть несколько хороших методов для этого

let day = Calendar.autoupdatingCurrent.dateInterval(of: .day, for: Date())
day?.end
day?.start

Вы можете использовать тот же метод, чтобы получить начало / конец любого компонента календаря (неделя / месяц / год и т. Д.)


Это неверно. day?.endоценивается как 00:00 завтра, а не до 23:59 сегодня.
Кейси Вагнер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.