Giorni veloci tra due NSDate


111

Mi chiedo se ci sia qualche nuova e fantastica possibilità di ottenere il numero di giorni tra due NSDate in Swift / il "nuovo" Cocoa?

Ad esempio, come in Ruby, farei:

(end_date - start_date).to_i

5
Penso che tu debba ancora usare NSCalendar e NSDateComponents (per i quali ci devono essere centinaia di risposte su SO). - Se stai cercando qualcosa di "nuova e fantastica possibilità", allora sarebbe utile mostrare la tua attuale soluzione per il confronto.
Martin R

1
Ora è molto semplice e non devi usare nulla "NS". Ho digitato una risposta per il 2017, da copiare e incollare.
Fattie

Risposte:


247

Devi considerare anche la differenza di fuso orario. Ad esempio, se confronti le date 2015-01-01 10:00e 2015-01-02 09:00, i giorni tra quelle date verranno restituiti come 0 (zero) poiché la differenza tra quelle date è inferiore a 24 ore (è 23 ore).

Se il tuo scopo è ottenere il numero esatto del giorno tra due date, puoi aggirare questo problema in questo modo:

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

Versione Swift 3 e 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
In realtà potresti voler controllare per 12pm (mezzogiorno) invece di startOfDayForDate - dovrebbe essere meno probabile che si bork a causa della regolazione dei fusi orari e dell'ora legale.
brandonscript

11
L'impostazione delle date a mezzogiorno può essere eseguita in questo modo:calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: firstDate))
MonsieurDart

Versione più corta per l'impostazione di mezzogiorno ( startOfDay()sembra essere inutile): calendar.date(bySettingHour: 12, minute: 0, second: 0, of: firstDate).
jamix,

52

Ecco la mia risposta per 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
}

L'ho usato con successo con i componenti del post @vikingosegundo sopra. Restituisce un numero intero che rappresenta il numero corretto di giorni tra due date. <pollice in alto>
Elimina il mio account

Mi piace ma il nome della funzione dovrebbe essere "daysBetweenDates"
mbonness

4
Questo restituisce 0 se confrontiamo todayetomorrow
tawheed

39

Vedo un paio di risposte Swift3, quindi aggiungerò la mia:

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

La denominazione sembra più Swifty, è una riga e utilizza il dateComponents()metodo più recente .


28

Ho tradotto la mia risposta 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)

risultato

<NSDateComponents: 0x10280a8a0>
     Day: 4

La parte più difficile è stata che il completamento automatico insiste che fromDate e toDate sarebbero NSDate?, ma in effetti devono essere NSDate!come mostrato nel riferimento.

Non vedo come sarebbe una buona soluzione con un operatore, poiché si desidera specificare l'unità in modo diverso in ogni caso. Potresti restituire l'intervallo di tempo, ma non guadagnerai molto.


Sembra che .DayCalendarUnitsia deprecato. Credo che ora dovresti usare .CalendarUnitDayinvece.
TaylorAllred

2
opzioni è ora un parametro atteso
Departamento B

2
L'esecuzione di Swift 2 funziona per me:let components = cal.components(.Day, fromDate: startDate, toDate: endDate, options: [])
Andrej

@TaylorAllred proprio .Dayora
William GP

28

Ecco Dateun'estensione molto carina per ottenere la differenza tra le date in anni, mesi, giorni, ore, minuti, secondi

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
datedovrebbe essere sinceDatenei parametri della funzione.
TheTiger

@TheTiger - Grazie mille per aver evidenziato l'errore più grande di questa risposta .. Praticamente testerò e aggiornerò la risposta presto.
Krunal

1
Il piacere è tutto mio! L'ho testato dayse funziona bene.
TheTiger

1
Buona risposta. Suggerirei solo func years(since date: Date) -> Int? { return Calendar.current.dateComponents[.year], from: date, to: self).years }, e potresti chiamarlo come let y = date1.years(since: date2). Ciò potrebbe essere più coerente con le moderne convenzioni di denominazione.
Rob il

18

Aggiornamento per 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

Ecco la risposta per Swift 3 (testato per 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!
}

Allora puoi chiamarlo così

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

7

Swift 3. Grazie a Emin Buğra Saral sopra per il startOfDaysuggerimento.

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

Uso:

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
sarebbe decisamente meglio spostarli entrambi a mezzogiorno, piuttosto che a mezzanotte per evitare molti problemi.
Fattie

3

Le cose integrate in swift sono ancora molto semplici. Come dovrebbero essere in questa fase iniziale. Ma puoi aggiungere le tue cose con il rischio di sovraccaricare gli operatori e le funzioni di dominio globali. Saranno comunque locali per il tuo modulo.

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
Non utilizzare (24 * 60 * 60) per la durata di un giorno. Ciò non tiene conto delle transizioni dell'ora legale.
Martin R

Penso che NSDate si adatterebbe a questo dato che utilizza sempre GMT e l'ora legale è solo una formattazione o una localizzazione su questo. Di sicuro diventa più complicato per mesi, anni o qualcosa di veramente variabile.
Daniel Schlaug

1
@MartinR ho dovuto provarlo per crederci ma in effetti, ora che l'ho fatto ho visto anche che wikipedia lo menziona. Hai ragione. Grazie per essere testardo con me.
Daniel Schlaug

1
Lì, modificato per essere corretto. Ma la vistosità è andata via.
Daniel Schlaug

1
è definito da posizione, punto temporale e sistema di calendario. il calendario ebraico ha un mese bisestile. c'è un ottimo video wwdc: eseguire il calcolo del calendario - un must per ogni programmatore di cacao.
vikingosegundo

3

Ecco la mia risposta per 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

Non c'è ancora quasi nessuna libreria standard specifica per Swift; solo i tipi di base numerica, stringa e raccolta snelli.

È perfettamente possibile definire tali scorciatoie utilizzando le estensioni, ma per quanto riguarda le API pronte all'uso, non esiste un "nuovo" Cocoa; Swift si limita a mappare direttamente alle stesse vecchie API Cocoa verbose come già esistono.


2

Aggiungerò la mia versione anche se questo thread è vecchio di un anno. Il mio codice ha questo aspetto:

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

Spero che aiuti qualcuno.

Saluti,


2

Il metodo di Erin è stato aggiornato a Swift 3, questo mostra i giorni da oggi (ignorando l'ora del giorno)

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

Ciò restituisce una differenza assoluta di giorni tra alcuni Datee oggi:

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

e poi usalo:

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

2

Puoi usare la seguente estensione:

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

Quindi, puoi chiamarlo in questo modo:

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

Tutte le risposte sono buone. Ma per le localizzazioni abbiamo bisogno di calcolare un numero di giorni decimali tra due date. così possiamo fornire il formato decimale sostenibile.

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

Quindi usalo come

    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

Questa è una versione aggiornata della risposta di Emin per Swift 5 che incorpora il suggerimento di utilizzare mezzogiorno invece di mezzanotte come ora definitiva per il confronto dei giorni. Gestisce anche il potenziale errore di varie funzioni di data restituendo un optional.

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

Dovresti usare il calendario nativo di Swift (rilascia il NS). L'uso della guardia quando si imposta l'ora a 12pm è inutile. Non fallirà mai.
Leo Dabus

anche chiamare startOfDay prima di impostare l'ora a mezzogiorno è inutile.
Leo Dabus

0

Swift 3 - Giorni da oggi fino ad oggi

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

Chiama la funzione in questo modo

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

0

un'opzione più semplice sarebbe creare un'estensione in data

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




}

se hai bisogno di anno, mese, giorni e ore come stringa, usa questo

var domani = Calendar.current.date (byAdding: .day, value: 1, to: Date ())!

let dc = tomorrow.completeOffset (from: Date ())


0

Bello pratico un rivestimento:

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

Soluzione 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

Versione 2017, copia e incolla

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
che misura la differenza tra la parte numerica delle date - cioè dal 21 gennaio al 22 febbraio darà 1 giorno, non 32 giorni come dovrebbe
Peter Johnson
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.