Швидкі дні між двома NSDates


111

Мені цікаво, чи є якась нова і дивовижна можливість отримати кількість днів між двома NSDates в Свіфті / "новим" какао?

Наприклад, як у Ruby я би робив:

(end_date - start_date).to_i

5
Я думаю, що вам все одно доведеться використовувати NSCalendar та NSDateComponents (на що ТАК повинно бути сотні відповідей). - Якщо ви шукаєте щось "нове та дивовижне", тоді було б корисно показати ваше порівняння для порівняння.
Мартін Р

1
Це зараз дуже просто, і вам не доведеться нічого використовувати "NS". Я набрав відповідь за 2017 рік, щоб скопіювати та вставити.
Fattie

Відповіді:


247

Ви також повинні врахувати різницю у часі. Наприклад, якщо ви порівнюєте дати 2015-01-01 10:00і 2015-01-02 09:00дні між цими датами повернуться як 0 (нуль), оскільки різниця між цими датами становить менше 24 годин (це 23 години).

Якщо ваша мета - отримати точний номер дня між двома датами, ви можете вирішити цю проблему так:

// Assuming that firstDate and secondDate are defined
// ...

let calendar = NSCalendar.currentCalendar()

// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDayForDate(firstDate)
let date2 = calendar.startOfDayForDate(secondDate)

let flags = NSCalendarUnit.Day
let components = calendar.components(flags, fromDate: date1, toDate: date2, options: [])

components.day  // This will return the number of day(s) between dates

Swift 3 та версія Swift 4

let calendar = Calendar.current

// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: firstDate)
let date2 = calendar.startOfDay(for: secondDate)

let components = calendar.dateComponents([.day], from: date1, to: date2)

14
Ви, можливо, захочете перевірити на 12 вечора (полудень) замість startOfDayForDate - має бути менше шансів забитися через налаштування часових поясів та DST.
фірмовий сценарій

11
Встановити дати до полудня можна так:calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: firstDate))
Мсьє,

Коротша версія для встановлення полудня (startOfDay() здається непотрібним): calendar.date(bySettingHour: 12, minute: 0, second: 0, of: firstDate).
jamix

52

Ось моя відповідь за Swift 2:

func daysBetweenDates(startDate: NSDate, endDate: NSDate) -> Int
{
    let calendar = NSCalendar.currentCalendar()

    let components = calendar.components([.Day], fromDate: startDate, toDate: endDate, options: [])

    return components.day
}

Я успішно використовував це з компонентами @vikingosegundo публікацією вище. Він повертає ціле число, що представляє правильну кількість днів між двома датами. <великі пальці вгору>
Видалити мій рахунок

Мені це подобається, але назва функції має бути "daysBet BetweenDates"
mbonness

4
Це повертає 0, якщо ми порівнюємо todayіtomorrow
tawheed

39

Я бачу пару відповідей Swift3, тож я додам свою:

public static func daysBetween(start: Date, end: Date) -> Int {
   Calendar.current.dateComponents([.day], from: start, to: end).day!
}

Найменування відчувається швидше, це один рядок і використовується останній dateComponents()метод.


28

Я переклав свою відповідь Objective-C

let start = "2010-09-01"
let end = "2010-09-05"

let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"

let startDate:NSDate = dateFormatter.dateFromString(start)
let endDate:NSDate = dateFormatter.dateFromString(end)

let cal = NSCalendar.currentCalendar()


let unit:NSCalendarUnit = .Day

let components = cal.components(unit, fromDate: startDate, toDate: endDate, options: nil)


println(components)

результат

<NSDateComponents: 0x10280a8a0>
     Day: 4

Найскладніше було те, що автозавершення наполягає на тому, що він би був NSDate?, і справді вони повинні бути такими, NSDate!як показано в посиланні.

Я не бачу, як би виглядало хороше рішення з оператором, оскільки ви хочете вказати пристрій по-різному в кожному випадку. Ви можете повернути часовий проміжок, але, якщо ви не отримаєте багато.


Схоже .DayCalendarUnit, застаріло. Я вважаю, що тепер ви повинні використовувати .CalendarUnitDayзамість цього.
ТейлорАлред

2
Параметри тепер очікуваний параметр
Департамент B

2
Запуск Swift 2 для мене працює:let components = cal.components(.Day, fromDate: startDate, toDate: endDate, options: [])
Андрій

@TaylorAllred тільки .Dayзараз
William GP

28

Ось дуже приємне Dateрозширення, щоб отримати різницю між датами в роках, місяцях, днях, годинах, хвилинах, секундах

extension Date {

    func years(sinceDate: Date) -> Int? {
        return Calendar.current.dateComponents([.year], from: sinceDate, to: self).year
    }

    func months(sinceDate: Date) -> Int? {
        return Calendar.current.dateComponents([.month], from: sinceDate, to: self).month
    }

    func days(sinceDate: Date) -> Int? {
        return Calendar.current.dateComponents([.day], from: sinceDate, to: self).day
    }

    func hours(sinceDate: Date) -> Int? {
        return Calendar.current.dateComponents([.hour], from: sinceDate, to: self).hour
    }

    func minutes(sinceDate: Date) -> Int? {
        return Calendar.current.dateComponents([.minute], from: sinceDate, to: self).minute
    }

    func seconds(sinceDate: Date) -> Int? {
        return Calendar.current.dateComponents([.second], from: sinceDate, to: self).second
    }

}

1
dateмає бути sinceDateу функціональних параметрах.
TheTiger

@TheTiger - Дякую вам за те, що ви висвітлили найбільшу помилку цієї відповіді. Я практично перевіряю та скоро оновлю відповідь.
Крунал

1
Моє задоволення! Я перевірив це, daysі він працює чудово.
TheTiger

1
Хороша відповідь. Я б тільки запропонував func years(since date: Date) -> Int? { return Calendar.current.dateComponents[.year], from: date, to: self).years }, і ви можете назвати це як let y = date1.years(since: date2). Це може відповідати сучасним умовам іменування.
Роб

18

Оновлення для Swift 3 iOS 10 Beta 4

func daysBetweenDates(startDate: Date, endDate: Date) -> Int {
    let calendar = Calendar.current
    let components = calendar.dateComponents([Calendar.Component.day], from: startDate, to: endDate)
    return components.day!
}

10

Ось відповідь на Swift 3 (перевірена на IOS 10 Beta)

func daysBetweenDates(startDate: Date, endDate: Date) -> Int
{
    let calendar = Calendar.current
    let components = calendar.components([.day], from: startDate, to: endDate, options: [])
    return components.day!
}

Тоді ви можете назвати це так

let pickedDate: Date = sender.date
let NumOfDays: Int = daysBetweenDates(startDate: pickedDate, endDate: Date())
    print("Num of Days: \(NumOfDays)")

7

Швидкий 3. Дякуємо Еміну Бугра Сарал вище за startOfDayпропозицію.

extension Date {

    func daysBetween(date: Date) -> Int {
        return Date.daysBetween(start: self, end: date)
    }

    static func daysBetween(start: Date, end: Date) -> Int {
        let calendar = Calendar.current

        // Replace the hour (time) of both dates with 00:00
        let date1 = calendar.startOfDay(for: start)
        let date2 = calendar.startOfDay(for: end)

        let a = calendar.dateComponents([.day], from: date1, to: date2)
        return a.value(for: .day)!
    }
}

Використання:

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let start = dateFormatter.date(from: "2017-01-01")!
let end = dateFormatter.date(from: "2018-01-01")!

let diff = Date.daysBetween(start: start, end: end) // 365

1
напевно було б краще перенести їх обох до полудня, а не до 00:00, щоб уникнути багатьох проблем.
Fattie

3

Речі, вбудовані у швидкий, як і раніше, є дуже основними. Як вони повинні бути на цій ранній стадії. Але ви можете додати свої власні речі з ризиком, пов’язаним із перевантаженням операторів та глобальних доменних функцій. Вони будуть локальними для вашого модуля.

let now = NSDate()
let seventies = NSDate(timeIntervalSince1970: 0)

// Standard solution still works
let days = NSCalendar.currentCalendar().components(.CalendarUnitDay, 
           fromDate: seventies, toDate: now, options: nil).day

// Flashy swift... maybe...
func -(lhs:NSDate, rhs:NSDate) -> DateRange {
    return DateRange(startDate: rhs, endDate: lhs)
}

class DateRange {
    let startDate:NSDate
    let endDate:NSDate
    var calendar = NSCalendar.currentCalendar()
    var days: Int {
        return calendar.components(.CalendarUnitDay, 
               fromDate: startDate, toDate: endDate, options: nil).day
    }
    var months: Int {
        return calendar.components(.CalendarUnitMonth, 
               fromDate: startDate, toDate: endDate, options: nil).month
    }
    init(startDate:NSDate, endDate:NSDate) {
        self.startDate = startDate
        self.endDate = endDate
    }
}

// Now you can do this...
(now - seventies).months
(now - seventies).days

19
Не використовуйте (24 * 60 * 60) протягом тривалості дня. Це не враховує переходів літнього часу.
Мартін

Я думаю, що NSDate підкоригує це, оскільки він завжди використовує GMT, а літній час - це лише форматування або локалізація. Напевно, вона стає складнішою на місяці, роки чи щось із дійсно змінною довжиною, хоча.
Даніель Шлауг

1
@MartinR Мені довелося спробувати це повірити, але справді зараз, коли я це зробив, я також побачив, що у Вікіпедії це згадується. Ви праві. Дякую за те, що вперто зі мною.
Даніель Шлауг

1
Там відредаговано, щоб бути правильним. Але вигадливість начебто пішла.
Даніель Шлауг

1
він визначається місцем розташування, моментом часу та календарною системою. у єврейському календарі є високосний місяць. є чудове відео wwdc: виконання календаря - обов'язковий перегляд для кожного кодера какао.
vikingosegundo

3

Ось моя відповідь за Swift 3:

func daysBetweenDates(startDate: NSDate, endDate: NSDate, inTimeZone timeZone: TimeZone? = nil) -> Int {
    var calendar = Calendar.current
    if let timeZone = timeZone {
        calendar.timeZone = timeZone
    }
    let dateComponents = calendar.dateComponents([.day], from: startDate.startOfDay, to: endDate.startOfDay)
    return dateComponents.day!
}

2

Навряд чи існує стандартна бібліотека Swift; лише основні числові, рядкові та колекційні типи.

Визначити такі скорочення можна цілком за допомогою розширень, але, що стосується фактичних вичерпаних API, немає "нового" какао; Swift просто карта безпосередньо на ті самі старі багатослівні API какао, як вони вже є.


2

Я збираюся додати свою версію, хоча ця нитка вже рік. Мій код виглядає приблизно так:

    var name = txtName.stringValue // Get the users name

    // Get the date components from the window controls
    var dateComponents = NSDateComponents()
    dateComponents.day = txtDOBDay.integerValue
    dateComponents.month = txtDOBMonth.integerValue
    dateComponents.year = txtDOBYear.integerValue

    // Make a Gregorian calendar
    let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)

    // Get the two dates we need
    var birthdate = calendar?.dateFromComponents(dateComponents)
    let currentDate = NSDate()

    var durationDateComponents = calendar?.components(NSCalendarUnit.CalendarUnitDay, fromDate: birthdate!, toDate: currentDate, options: nil)

    let numberOfDaysAlive = durationDateComponents?.day

    println("\(numberOfDaysAlive!)")

    txtGreeting.stringValue = "Hello \(name), You have been alive for \(numberOfDaysAlive!) days."

Я сподіваюся, що це комусь допоможе.

Ура,


2

Метод Еріна оновлено до Swift 3. Це показує дні з сьогоднішнього дня (без урахування часу доби)

func daysBetweenDates( endDate: Date) -> Int 
    let calendar: Calendar = Calendar.current 
    let date1 = calendar.startOfDay(for: Date()) 
    let date2 = calendar.startOfDay(for: secondDate) 
    return calendar.dateComponents([.day], from: date1, to: date2).day! 
}

2

Це повертає абсолютну різницю в днях між деякими Dateі сьогоднішніми:

extension Date {
  func daysFromToday() -> Int {
    return abs(Calendar.current.dateComponents([.day], from: self, to: Date()).day!)
  }
}

а потім скористайтеся ним:

if someDate.daysFromToday() >= 7 {
  // at least a week from today
}

2

Ви можете використовувати таке розширення:

public extension Date {
    func daysTo(_ date: Date) -> Int? {
        let calendar = Calendar.current

        // Replace the hour (time) of both dates with 00:00
        let date1 = calendar.startOfDay(for: self)
        let date2 = calendar.startOfDay(for: date)

        let components = calendar.dateComponents([.day], from: date1, to: date2)
        return components.day  // This will return the number of day(s) between dates
    }
}

Тоді ви можете назвати це так:

startDate.daysTo(endDate)

1

Швидкий 3.2

extension DateComponentsFormatter {
    func difference(from fromDate: Date, to toDate: Date) -> String? {
        self.allowedUnits = [.year,.month,.weekOfMonth,.day]
        self.maximumUnitCount = 1
        self.unitsStyle = .full
        return self.string(from: fromDate, to: toDate)
    }
}

1

Вся відповідь хороша. Але для Локалізації нам потрібно обчислити кількість десяткових днів між двома датами. тому ми можемо забезпечити стійкий десятковий формат.

// This method returns the fractional number of days between to dates
func getFractionalDaysBetweenDates(date1: Date, date2: Date) -> Double {

    let components = Calendar.current.dateComponents([.day, .hour], from: date1, to: date2)

    var decimalDays = Double(components.day!)
    decimalDays += Double(components.hour!) / 24.0

    return decimalDays
}

1
extension Date {
    func daysFromToday() -> Int {
        return Calendar.current.dateComponents([.day], from: self, to: Date()).day!
    }
}

Тоді використовуйте як

    func dayCount(dateString: String) -> String{
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MMM dd,yyyy hh:mm a"
        let fetchedDate = dateFormatter.date(from: dateString)


        let day = fetchedDate?.daysFromToday()
        if day! > -1{
            return "\(day!) days passed."
        }else{
        return "\(day! * -1) days left."
        }
    }

1

Це оновлена ​​версія відповіді Еміна для Swift 5, яка містить пропозицію використовувати полудень замість опівночі як остаточний час для порівняння днів. Він також обробляє можливий збій різних функцій дати, повертаючи необов’язковий.

    ///
    /// This is an approximation; it does not account for time differences. It will set the time to 1200 (noon) and provide the absolute number
    /// of days between now and the given date. If the result is negative, it should be read as "days ago" instead of "days from today."
    /// Returns nil if something goes wrong initializing or adjusting dates.
    ///

    func daysFromToday() -> Int?
    {
        let calendar = NSCalendar.current

        // Replace the hour (time) of both dates with noon. (Noon is less likely to be affected by DST changes, timezones, etc. than midnight.)
        guard let date1 = calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: Date())),
              let date2 = calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: self)) else
        {
            return nil
        }

        return calendar.dateComponents([.day], from: date1, to: date2).day
    }

Вам слід скористатись родним календарем Swift (скиньте NS). Використання охорони під час встановлення години на 12 вечора безглуздо. Це ніколи не провалиться.
Лев Дабус

виклик startOfDay перед встановленням години на полудень, також безглуздо.
Лев Дабус

0

Swift 3 - дні від сьогоднішнього дня до дати

func daysUntilDate(endDateComponents: DateComponents) -> Int
    {
        let cal = Calendar.current
        var components = cal.dateComponents([.era, .year, .month, .day], from: NSDate() as Date)
        let today = cal.date(from: components)
        let otherDate = cal.date(from: endDateComponents)

        components = cal.dateComponents([Calendar.Component.day], from: (today! as Date), to: otherDate!)
        return components.day!
    }

Функція виклику, як це

// Days from today until date
   var examnDate = DateComponents()
   examnDate.year = 2016
   examnDate.month = 12
   examnDate.day = 15
   let daysCount = daysUntilDate(endDateComponents: examnDate)

0

простішим варіантом буде створити розширення на Date

public extension Date {

        public var currentCalendar: Calendar {
            return Calendar.autoupdatingCurrent
        }

        public func daysBetween(_ date: Date) -> Int {
            let components = currentCalendar.dateComponents([.day], from: self, to: date)
            return components.day!
        }
    }

0
  func completeOffset(from date:Date) -> String? {

    let formatter = DateComponentsFormatter()
    formatter.unitsStyle = .brief

    return  formatter.string(from: Calendar.current.dateComponents([.year,.month,.day,.hour,.minute,.second], from: date, to: self))




}

якщо вам потрібні місячні дні та години, як рядок, використовуйте це

var завтра = Calendar.current.date (byAdding: .day, значення: 1, to: Date ())!

нехай dc = завтра.completeOffset (від: Дата ())


0

Хороший зручний один лайнер:

extension Date {
  var daysFromNow: Int {
    return Calendar.current.dateComponents([.day], from: Date(), to: self).day!
  }
}

0

Швидкий 4

 func getDateHeader(indexPath: Int) -> String {
    let formatter2 = DateFormatter()
    formatter2.dateFormat = "MM-dd-yyyy"
    var dateDeadline : Date?

    dateDeadline = formatter2.date(from: arrCompletedDate[indexPath] as! String)

    let currentTime = dateDeadline?.unixTimestamp
    let calendar = NSCalendar.current

    let date = NSDate(timeIntervalSince1970: Double(currentTime!))
    if calendar.isDateInYesterday(date as Date) { return "Yesterday" }
    else if calendar.isDateInToday(date as Date) { return "Today" }
    else if calendar.isDateInTomorrow(date as Date) { return "Tomorrow" }
    else {
        let startOfNow = calendar.startOfDay(for: NSDate() as Date)
        let startOfTimeStamp = calendar.startOfDay(for: date as Date)
        let components = calendar.dateComponents([.day], from: startOfNow, to: startOfTimeStamp)
        let day = components.day!
        if day < 1 { return "\(abs(day)) days ago" }
        else { return "In \(day) days" }
    }
}

0

Рішення Swift 5.2.4:

import UIKit

let calendar = Calendar.current

let start = "2010-09-01"
let end = "2010-09-05"

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"

let firstDate = dateFormatter.date(from: start)!
let secondDate = dateFormatter.date(from: end)!

// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: firstDate)
let date2 = calendar.startOfDay(for: secondDate)

let components = calendar.dateComponents([Calendar.Component.day], from: date1, to: date2)

components.day  // This will return the number of day(s) between dates

-1

Версія 2017 року, скопіюйте та вставте

func simpleIndex(ofDate: Date) -> Int {
    
    // index here just means today 0, yesterday -1, tomorrow 1 etc.
    
    let c = Calendar.current
    let todayRightNow = Date()
    
    let d = c.date(bySetting: .hour, value: 13, of: ofDate)
    let t = c.date(bySetting: .hour, value: 13, of: todayRightNow)
    
    if d == nil || today == nil {
    
        print("weird problem simpleIndex#ofDate")
        return 0
    }
    
    let r = c.dateComponents([.day], from: today!, to: d!)
    // yesterday is negative one, tomorrow is one
    
    if let o = r.value(for: .day) {
        
        return o
    }
    else {
    
        print("another weird problem simpleIndex#ofDate")
        return 0
    }
}

-2
let calendar = NSCalendar.currentCalendar();
let component1 = calendar.component(.Day, fromDate: fromDate)
let component2 = calendar.component(.Day, fromDate: toDate)
let difference  = component1 - component2

1
що вимірює різницю між числовою частиною дат - тобто 21 січня по 22 лютого дасть 1 день, а не 32 дні, як слід
Пітер Джонсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.