Schnelle Tage zwischen zwei NSDates


110

Ich frage mich, ob es eine neue und großartige Möglichkeit gibt, die Anzahl der Tage zwischen zwei NSDates in Swift / dem "neuen" Kakao zu ermitteln.

ZB wie in Ruby würde ich tun:

(end_date - start_date).to_i

5
Ich denke, Sie müssen immer noch NSCalendar und NSDateComponents verwenden (für die es Hunderte von Antworten auf SO geben muss). - Wenn Sie nach einer "neuen und großartigen Möglichkeit" suchen, ist es hilfreich, Ihre derzeitige Lösung zum Vergleich zu zeigen.
Martin R

1
Dies ist jetzt sehr einfach und Sie müssen nichts "NS" verwenden. Ich habe eine Antwort für 2017 eingegeben, um sie zu kopieren und einzufügen.
Fattie

Antworten:


245

Sie müssen auch den Zeitunterschied berücksichtigen. Wenn Sie beispielsweise die Daten 2015-01-01 10:00und vergleichen 2015-01-02 09:00, werden Tage zwischen diesen Daten als 0 (Null) zurückgegeben, da die Differenz zwischen diesen Daten weniger als 24 Stunden (23 Stunden) beträgt.

Wenn Sie die genaue Tageszahl zwischen zwei Daten ermitteln möchten, können Sie dieses Problem folgendermaßen umgehen:

// 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 und Swift 4 Version

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
Möglicherweise möchten Sie tatsächlich nach 12 Uhr (Mittag) anstatt nach startOfDayForDate suchen. Aufgrund der Anpassung der Zeitzonen und der Sommerzeit sollte die Wahrscheinlichkeit eines Fehlschlags geringer sein.
Brandonscript

11
Das Einstellen der Daten auf Mittag kann folgendermaßen erfolgen:calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: firstDate))
MonsieurDart

Kürzere Version zum Einstellen des Mittags ( startOfDay()scheint unnötig zu sein) : calendar.date(bySettingHour: 12, minute: 0, second: 0, of: firstDate).
Jamix

52

Hier ist meine Antwort für 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
}

Ich habe dies erfolgreich mit Komponenten von @vikingosegundo post oben verwendet. Es wird eine Ganzzahl zurückgegeben, die die korrekte Anzahl von Tagen zwischen zwei Daten darstellt. <Daumen hoch>
Mein Konto

Ich mag es, aber der Funktionsname sollte "daysBetweenDates" sein
mbonness

4
Dies gibt 0 zurück, wenn wir vergleichen todayundtomorrow
tawheed

39

Ich sehe ein paar Swift3-Antworten, also füge ich meine eigenen hinzu:

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

Die Benennung fühlt sich schneller an, es ist eine Zeile und mit der neuesten dateComponents()Methode.


28

Ich habe meine Objective-C-Antwort übersetzt

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)

Ergebnis

<NSDateComponents: 0x10280a8a0>
     Day: 4

Das Schwierigste war, dass die automatische Vervollständigung darauf besteht, dass Datum und Datum vorliegen NSDate?, aber tatsächlich müssen sie NSDate!den Angaben in der Referenz entsprechen.

Ich sehe nicht ein, wie eine gute Lösung mit einem Operator aussehen würde, da Sie die Einheit jeweils anders spezifizieren möchten. Sie könnten das Zeitintervall zurückgeben, aber dann werden Sie nicht viel gewinnen.


Sieht aus wie .DayCalendarUnitveraltet. Ich glaube jetzt solltest du .CalendarUnitDaystattdessen verwenden.
TaylorAllred

2
Optionen ist jetzt ein erwarteter Parameter
Departamento B

2
Mit Swift 2 funktioniert das für mich:let components = cal.components(.Day, fromDate: startDate, toDate: endDate, options: [])
Andrej

@ TaylorAllred gerade .Dayjetzt
William GP

28

Hier ist eine sehr schöne DateErweiterung, um Unterschiede zwischen Daten in Jahren, Monaten, Tagen, Stunden, Minuten, Sekunden zu erhalten

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
datesollte sinceDatein Funktionsparametern sein.
TheTiger

@TheTiger - Vielen Dank, dass Sie den größten Fehler dieser Antwort hervorgehoben haben. Ich werde die Antwort bald praktisch testen und aktualisieren.
Krunal

1
Gern geschehen! Ich habe es getestet daysund es funktioniert gut.
TheTiger

1
Gute Antwort. Ich würde nur vorschlagen func years(since date: Date) -> Int? { return Calendar.current.dateComponents[.year], from: date, to: self).years }, und Sie könnten es so nennen let y = date1.years(since: date2). Dies könnte mit modernen Namenskonventionen vereinbar sein.
Rob

18

Update für 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

Hier ist die Antwort für Swift 3 (getestet für 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!
}

Dann können Sie es so nennen

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

7

Swift 3. Vielen Dank an Emin Buğra Saral für den startOfDayVorschlag.

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

Verwendung:

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
Es wäre definitiv besser, beide auf Mittag zu verschieben, als auf 00:00 , um viele Probleme zu vermeiden.
Fattie

3

Die Dinge, die in Swift eingebaut sind, sind immer noch sehr einfach. Wie sie in diesem frühen Stadium sein sollten. Sie können jedoch Ihre eigenen Inhalte hinzufügen, wobei das Risiko besteht, dass Operatoren und globale Domänenfunktionen überlastet werden. Sie sind jedoch lokal für Ihr Modul.

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
Sie nicht verwenden (24 * 60 * 60) für die Dauer eines Tages. Dies berücksichtigt keine Sommerzeitübergänge.
Martin R

Ich denke, NSDate würde sich darauf einstellen, da es immer GMT verwendet und die Sommerzeit nur eine Formatierung oder Lokalisierung ist. Sicher wird es für Monate, Jahre oder irgendetwas von wirklich variabler Länge schwieriger.
Daniel Schlaug

1
@MartinR Ich musste es versuchen, um es zu glauben, aber jetzt, wo ich es tat, sah ich auch, dass Wikipedia dies erwähnt. Du hast Recht. Danke, dass du stur mit mir bist.
Daniel Schlaug

1
Dort bearbeitet, um korrekt zu sein. Aber die Auffälligkeit verschwand irgendwie.
Daniel Schlaug

1
Es wird durch Ort, Zeitpunkt und Kalendersystem definiert. Der hebräische Kalender hat einen Schaltmonat. Es gibt ein großartiges wwdc-Video: Kalenderberechnung durchführen - ein Muss für jeden Kakaocodierer.
Vikingosegundo

3

Hier ist meine Antwort für 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

Es gibt noch kaum eine Swift-spezifische Standardbibliothek. nur die schlanken grundlegenden numerischen, Zeichenfolgen- und Sammlungstypen.

Es ist durchaus möglich, solche Abkürzungen mithilfe von Erweiterungen zu definieren, aber was die eigentlichen Out-of-the-Box-APIs betrifft, gibt es keinen "neuen" Kakao. Swift wird nur direkt denselben alten ausführlichen Cocoa-APIs zugeordnet, die bereits vorhanden sind.


2

Ich werde meine Version hinzufügen, obwohl dieser Thread ein Jahr alt ist. Mein Code sieht folgendermaßen aus:

    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."

Ich hoffe es hilft jemandem.

Prost,


2

Erins Methode wurde auf Swift 3 aktualisiert. Dies zeigt Tage ab heute (ohne Berücksichtigung der Tageszeit).

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

Dies ergibt einen absoluten Unterschied in Tagen zwischen einigen Dateund heute:

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

und dann benutze es:

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

2

Sie können die folgende Erweiterung verwenden:

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

Dann können Sie es so nennen:

startDate.daysTo(endDate)

1

Swift 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

Alle Antworten sind gut. Für Lokalisierungen müssen wir jedoch eine Anzahl von Dezimaltagen zwischen zwei Daten berechnen. So können wir das nachhaltige Dezimalformat bereitstellen.

// 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!
    }
}

Dann benutze es gerne

    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."
        }
    }

0

Schnelle 3 - Tage von heute bis heute

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

Rufen Sie die Funktion so auf

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

0

Eine einfachere Möglichkeit wäre, am Datum eine Erweiterung zu erstellen

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




}

Wenn Sie Jahr, Monat, Tage und Stunden als Zeichenfolge benötigen, verwenden Sie diese Option

var morgen = Calendar.current.date (byAdding: .day, Wert: 1, bis: Date ())!

let dc = morgen.completeOffset (von: Date ())


0

Schöner handlicher Einzeiler:

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

0

Swift 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

Dies ist eine aktualisierte Version von Emins Antwort für Swift 5, die den Vorschlag enthält, Mittag anstelle von Mitternacht als endgültige Zeit für den Vergleich von Tagen zu verwenden. Es behandelt auch den möglichen Ausfall verschiedener Datumsfunktionen, indem ein optionales zurückgegeben wird.

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

Sie sollten den nativen Swift-Kalender verwenden (NS löschen). Die Verwendung eines Schutzes beim Einstellen der Stunde auf 12 Uhr ist sinnlos. Es wird niemals scheitern.
Leo Dabus

Das Aufrufen von startOfDay vor dem Einstellen der Stunde auf Mittag ist ebenfalls sinnlos.
Leo Dabus

0

Schnelle 5.2.4-Lösung:

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

Version 2017, kopieren und einfügen

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
das misst die Differenz zwischen dem Anzahlteil der Daten - dh der 21. Januar bis 22. Februar wird 1 Tag geben, nicht 32 Tage, wie es sollte
Peter Johnson
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.