Hari-hari cepat antara dua NSDates


111

Saya bertanya-tanya apakah ada kemungkinan baru dan mengagumkan untuk mendapatkan jumlah hari antara dua NSDates di Swift / Cocoa "baru"?

Misalnya seperti di Ruby saya akan melakukan:

(end_date - start_date).to_i

5
Saya pikir Anda masih harus menggunakan NSCalendar dan NSDateComponents (yang pasti ada ratusan jawaban di SO). - Jika Anda mencari sesuatu yang "kemungkinan baru dan menakjubkan" maka akan sangat membantu jika Anda menunjukkan solusi Anda saat ini untuk perbandingan.
Martin R

1
Ini sekarang sangat mudah, dan Anda tidak perlu menggunakan "NS" apa pun. Saya mengetik jawaban untuk 2017, untuk menyalin dan menempel.
Fattie

Jawaban:


247

Anda juga harus mempertimbangkan perbedaan waktu. Misalnya jika Anda membandingkan tanggal 2015-01-01 10:00dan 2015-01-02 09:00, hari-hari di antara tanggal tersebut akan kembali sebagai 0 (nol) karena perbedaan antara tanggal tersebut kurang dari 24 jam (itu 23 jam).

Jika tujuan Anda adalah mendapatkan nomor hari yang tepat di antara dua tanggal, Anda dapat mengatasi masalah ini seperti ini:

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

Versi Swift 3 dan 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
Anda mungkin sebenarnya ingin memeriksa jam 12 siang (siang) daripada startOfDayForDate - kemungkinan besar tidak akan macet karena menyesuaikan zona waktu dan DST.
brandonscript

11
Mengatur tanggal ke tengah hari dapat dilakukan seperti ini:calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: firstDate))
MonsieurDart

Versi yang lebih pendek untuk menetapkan siang ( startOfDay()tampaknya tidak perlu): calendar.date(bySettingHour: 12, minute: 0, second: 0, of: firstDate).
jamix

52

Inilah jawaban saya untuk 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
}

Saya berhasil menggunakan ini dengan komponen posting @vikingosegundo di atas. Ini mengembalikan bilangan bulat yang mewakili jumlah hari yang benar antara dua tanggal. <jempol ke atas>
Hapus Akun Saya

Saya suka tapi nama fungsinya harus "daysBetweenDates"
mbonness

4
Ini mengembalikan 0 jika kita membandingkan todaydantomorrow
tauhid

39

Saya melihat beberapa jawaban Swift3 jadi saya akan menambahkan jawaban saya sendiri:

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

Penamaannya terasa lebih Swifty, satu baris, dan menggunakan dateComponents()metode terbaru .


28

Saya menerjemahkan jawaban Objective-C saya

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)

hasil

<NSDateComponents: 0x10280a8a0>
     Day: 4

Bagian tersulit adalah bahwa pelengkapan otomatis menegaskan fromDate dan toDate akan NSDate?, tetapi memang harus NSDate!seperti yang ditunjukkan dalam referensi.

Saya tidak melihat bagaimana solusi yang baik dengan operator akan terlihat, karena Anda ingin menentukan unit secara berbeda di setiap kasus. Anda dapat mengembalikan interval waktu, tetapi Anda tidak mendapatkan banyak keuntungan.


Sepertinya .DayCalendarUnitsudah usang. Saya percaya sekarang Anda harus menggunakan .CalendarUnitDaysebagai gantinya.
TaylorAllred

2
opsi sekarang menjadi parameter yang diharapkan
Departamento B

2
Menjalankan Swift 2 ini bekerja untuk saya:let components = cal.components(.Day, fromDate: startDate, toDate: endDate, options: [])
Andrej

@TaylorAllred hanya .Daysekarang
William GP

28

Ini sangat bagus, Dateperpanjangan untuk mendapatkan perbedaan antara tanggal dalam tahun, bulan, hari, jam, menit, detik

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
dateharus sinceDatedalam parameter fungsi.
TheTiger

@TheTiger - Terima kasih banyak telah menyoroti kesalahan terbesar dari jawaban ini .. Saya akan segera menguji dan memperbarui jawaban.
Krunal

1
Dengan senang hati! Saya telah mengujinya daysdan berfungsi dengan baik.
TheTiger

1
Jawaban yang bagus. Saya hanya menyarankan func years(since date: Date) -> Int? { return Calendar.current.dateComponents[.year], from: date, to: self).years }, dan Anda bisa menyebutnya sebagai let y = date1.years(since: date2). Itu mungkin lebih konsisten dengan konvensi penamaan modern.
Rob

18

Pembaruan untuk 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

Inilah jawaban untuk Swift 3 (diuji untuk 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!
}

Kemudian Anda bisa menyebutnya seperti ini

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

7

Swift 3. Terima kasih kepada Emin Buğra Saral di atas atas startOfDaysarannya.

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

Pemakaian:

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
pasti akan lebih baik untuk memindahkan mereka berdua pada siang hari, daripada 00:00 untuk menghindari banyak masalah.
Fattie

3

Hal-hal yang dibangun dengan cepat masih sangat mendasar. Seperti seharusnya pada tahap awal ini. Tetapi Anda dapat menambahkan barang Anda sendiri dengan risiko yang datang dengan operator yang membebani dan fungsi domain global. Mereka akan menjadi lokal untuk modul Anda.

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
Jangan gunakan (24 * 60 * 60) untuk jangka waktu satu hari. Ini tidak memperhitungkan transisi waktu musim panas.
Martin R

Saya pikir NSDate akan menyesuaikan untuk itu karena selalu menggunakan GMT dan penghematan siang hari hanyalah pemformatan atau pelokalan di atasnya. Yang pasti itu menjadi lebih rumit selama berbulan-bulan, bertahun-tahun atau apa pun dengan panjang yang sangat bervariasi.
Daniel Schlaug

1
@MartinR Saya harus mencobanya untuk percaya tetapi memang, sekarang setelah saya melakukannya, saya juga melihat bahwa wikipedia menyebutkan ini. Anda benar. Terima kasih telah keras kepala dengan saya.
Daniel Schlaug

1
Di sana, diedit agar benar. Tapi kilasan semacam itu hilang.
Daniel Schlaug

1
itu ditentukan oleh lokasi, titik waktu dan sistem kalender. kalender ibrani memiliki bulan kabisat. ada video wwdc yang bagus: melakukan perhitungan kalender - yang harus dilihat oleh setiap pembuat kode kakao.
vikingosegundo

3

Inilah jawaban saya untuk 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

Hampir tidak ada pustaka standar khusus Swift; hanya jenis numerik dasar, string, dan koleksi.

Sangat mungkin untuk mendefinisikan singkatan seperti itu menggunakan ekstensi, tetapi sejauh API out-of-the-box yang sebenarnya berjalan, tidak ada Cocoa "baru"; Swift hanya memetakan langsung ke API Kakao verbose lama yang sama seperti yang sudah ada.


2

Saya akan menambahkan versi saya meskipun utas ini berumur satu tahun. Kode saya terlihat seperti ini:

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

Saya harap ini membantu seseorang.

Bersulang,


2

Metode Erin diperbarui ke Swift 3, Ini menunjukkan hari dari hari ini (dengan mengabaikan waktu)

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

Ini mengembalikan perbedaan mutlak dalam hari antara beberapa Datedan hari ini:

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

dan kemudian gunakan:

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

2

Anda dapat menggunakan ekstensi berikut:

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

Kemudian, Anda bisa menyebutnya seperti ini:

startDate.daysTo(endDate)

1

Swift 3.2.0

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

Semua jawaban bagus. Tetapi untuk Pelokalan kita perlu menghitung jumlah hari desimal di antara dua tanggal. sehingga kami dapat menyediakan format desimal berkelanjutan.

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

Lalu gunakan seperti itu

    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

Ini adalah versi terbaru dari jawaban Emin untuk Swift 5 yang menggabungkan saran untuk menggunakan siang hari, bukan tengah malam sebagai waktu yang pasti untuk membandingkan hari. Ini juga menangani potensi kegagalan berbagai fungsi tanggal dengan mengembalikan opsional.

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

Anda harus menggunakan Kalender asli Swift (lepaskan NS). Penggunaan penjaga saat mengatur jam ke 12 siang tidak ada gunanya. Itu tidak akan pernah gagal.
Leo Dabus

memanggil startOfDay sebelum menyetel jam ke tengah hari, itu juga tidak ada gunanya.
Leo Dabus

0

Swift 3 - Hari dari hari ini sampai saat ini

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

Fungsi panggilan seperti ini

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

0

pilihan yang lebih mudah adalah membuat ekstensi pada Tanggal

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




}

jika Anda membutuhkan hari bulan tahun dan jam sebagai string gunakan ini

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

biarkan dc = besok.completeOffset (dari: Tanggal ())


0

Satu kapal bagus yang berguna:

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

0

Cepat 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

Solusi 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

Versi 2017, salin dan tempel

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
yang mengukur perbedaan antara jumlah porsi tanggal- Ie 21 Januari sampai 22 Februari akan memberikan 1 hari, bukan 32 hari sebagaimana mestinya
Peter Johnson
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.