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. І це спрацює для іншого часового поясу, якщо спершу відрегулювати часовий інтервал секунди часового поясу від GGMT, а потім відрегулювати зворотний шлях після обрізки. (Кінець дня, очевидно, 23: 59: 59.999 пізніше, який можна отримати простим доповненням, поки у вас є часовий інтервал.)
Гарячі лизання

Зазначення "кінця дня" як довільний час до його фактичного закінчення (одна секунда в цьому випадку) здається рецептом глючного програмного забезпечення. Якщо ви використовуєте 1 мс, збої будуть рідше. Або ви можете скористатися 23 години, щоб шанси на потрапляння будь-яких помилок. :-)
Едвард Брей

Відповіді:


33

Ви пропущені NSDayCalendarUnitв

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

1
У мене дивна поведінка, коли якщо додати компонент дня, я отримую 23:00 попереднього дня.
Майк М

3
@Mike використовує часовий пояс: components.timeZone = [NSTimeZone timeZoneWithName: @ "GMT"];
dimaxyu

@dimaxyu має рацію. Швидко його: components.timeZone = NSTimeZone (назва: "GMT")
Том Bevelander,

220

Початок дня / кінець дня - 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 Бланк

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

5
Це повертає startOfDay у поточний часовий пояс! Очікується, що NSDate буде в UTC, але цей перетворювач повертає час, виправлений для часового поясу за замовчуванням.
Том Бевелендер

3
Чому endOfDay необов’язковий? Чи бувають ситуації, коли startOfDay не дорівнює нулю, тоді як endOfDay може бути нульовим?
Мансуров Руслан

1
Отже, залежно від того, яким чином ви користуєтесь цим, ви пам’ятаєте проміжок часу між кінцями та початками днів developer.apple.com/documentation/foundation/nsdate/…
atlex2

29

Швидкий 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)

Додатково

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

Дата математика

Документація NSCalendar Apple iOS . (Див. Розділ: Календарі обчислення )

Методи NSCalendar, обговорені NSHipster .


17

Мої розширення Swift для NSDate:

Швидкий 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
    }
}

Швидкий 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
Ben Sinclair

щоб уникнути проблем із часовим поясом, додайте: components.timeZone = NSTimeZone (назва: "GMT")
Том Bevelander,

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 Дата та Календар не в UIKit, його у Фонді, проте UIKit імпортує Фонд
Арбітур

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:, на жаль ...
Julian F. Weinert

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

Швидкий 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

Ціль-С

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

2

Для швидкого 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

Я думаю, що найкоротший спосіб зробити це у Свіфті:

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+у Фонді є вбудована функція, яку ви можете використовувати поза коробкою. Не потрібно реалізовувати власні функції.

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оцінює завтра до 12:00, не сьогодні до 23:59.
Кейсі Вагнер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.