Ngày nhanh giữa hai NSDates


110

Tôi đang tự hỏi liệu có khả năng mới và tuyệt vời nào đó để nhận được số ngày giữa hai NSDates trong Swift / Cocoa "mới" không?

Ví dụ như trong Ruby, tôi sẽ làm:

(end_date - start_date).to_i

5
Tôi nghĩ bạn vẫn phải sử dụng NSCalendar và NSDateComponents (phải có hàng trăm câu trả lời trên SO). - Nếu bạn đang tìm kiếm thứ gì đó "khả năng mới và tuyệt vời" thì sẽ rất hữu ích nếu bạn đưa ra giải pháp hiện tại để so sánh.
Martin R

1
Điều này bây giờ rất dễ dàng và bạn không cần phải sử dụng "NS" bất cứ điều gì. Tôi đã nhập một câu trả lời cho năm 2017, để sao chép và dán.
Fattie, 20/07/17

Câu trả lời:


245

Bạn cũng phải xem xét sự khác biệt về thời gian. Ví dụ: nếu bạn so sánh ngày 2015-01-01 10:002015-01-02 09:00, ngày giữa những ngày đó sẽ trả về là 0 (không) vì sự khác biệt giữa những ngày đó là dưới 24 giờ (đó là 23 giờ).

Nếu mục đích của bạn là lấy số ngày chính xác giữa hai ngày, bạn có thể giải quyết vấn đề này như sau:

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

Phiên bản Swift 3 và 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
Bạn có thể thực sự muốn kiểm tra 12h trưa (trưa) thay vì startOfDayForDate - sẽ ít có khả năng bị nhiễu hơn do điều chỉnh múi giờ và DST.
brandonscript

11
Đặt ngày thành buổi trưa có thể được thực hiện như sau:calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: firstDate))
MonsieurDart

Phiên bản ngắn hơn để thiết lập buổi trưa ( startOfDay()dường như là không cần thiết) : calendar.date(bySettingHour: 12, minute: 0, second: 0, of: firstDate).
jamix

52

Đây là câu trả lời của tôi cho 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
}

Tôi đã sử dụng thành công điều này với các thành phần của @vikingosegundo bài đăng ở trên. Nó trả về một số nguyên đại diện cho số ngày chính xác giữa hai ngày. <thumbs up>
Xóa tài khoản của tôi

Tôi thích nó nhưng tên hàm cần được "daysBetweenDates"
mbonness

4
Điều này trả về 0 nếu chúng ta đang so sánh todaytomorrow
tawheed

39

Tôi thấy một vài câu trả lời Swift3 nên tôi sẽ thêm câu trả lời của riêng mình:

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

Việc đặt tên có cảm giác Swifty hơn, nó là một dòng và sử dụng dateComponents()phương pháp mới nhất .


28

Tôi đã dịch câu trả lời Objective-C của mình

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)

kết quả

<NSDateComponents: 0x10280a8a0>
     Day: 4

Phần khó nhất là tính năng tự động hoàn thành khẳng định từ Ngày đến ngày sẽ như vậy NSDate?, nhưng thực sự chúng phải NSDate!như được hiển thị trong tham chiếu.

Tôi không thấy giải pháp tốt với toán tử sẽ như thế nào, vì bạn muốn chỉ định đơn vị khác nhau trong mỗi trường hợp. Bạn có thể trả lại khoảng thời gian, nhưng bạn sẽ không thu được nhiều.


Có vẻ như .DayCalendarUnitkhông được dùng nữa. Tôi tin rằng bây giờ bạn nên sử dụng .CalendarUnitDaythay thế.
TaylorAllred

2
tùy chọn hiện là một tham số mong đợi
Departamento B

2
Chạy Swift 2, điều này phù hợp với tôi:let components = cal.components(.Day, fromDate: startDate, toDate: endDate, options: [])
Andrej

@TaylorAllred chỉ .Daytại
William GP

28

Ở đây rất hay, Datetiện ích mở rộng để có sự khác biệt giữa các ngày trong năm, tháng, ngày, giờ, phút, giây

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
datenên có sinceDatetrong các tham số chức năng.
TheTiger

@TheTiger - Cảm ơn bạn rất nhiều vì đã nêu ra sai lầm lớn nhất của câu trả lời này .. Tôi sẽ kiểm tra thực tế và cập nhật câu trả lời sớm.
Krunal

1
Hân hạnh! Tôi đã thử nghiệm nó daysvà nó hoạt động tốt.
TheTiger

1
Câu trả lời tốt. Tôi chỉ gợi ý func years(since date: Date) -> Int? { return Calendar.current.dateComponents[.year], from: date, to: self).years }, và bạn có thể gọi nó là let y = date1.years(since: date2). Điều đó có thể phù hợp hơn với các quy ước đặt tên hiện đại.
Rob

18

Cập nhật cho 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

Đây là câu trả lời cho Swift 3 (được thử nghiệm cho 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!
}

Sau đó, bạn có thể gọi nó như thế này

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

7

Swift 3. Cảm ơn Emin Buğra Saral ở trên về startOfDaygợi ý.

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

Sử dụng:

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
chắc chắn sẽ tốt hơn nếu chuyển cả hai sang buổi trưa, thay vì 00:00 để tránh nhiều vấn đề.
Fattie

3

Những thứ được tích hợp trong swift vẫn rất cơ bản. Vì họ phải ở giai đoạn đầu này. Nhưng bạn có thể thêm nội dung của riêng mình với rủi ro đi kèm với các toán tử quá tải và các hàm miền toàn cầu. Tuy nhiên, chúng sẽ là cục bộ cho mô-đun của bạn.

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
Không sử dụng (24 * 60 * 60) trong khoảng thời gian một ngày. Điều này không tính đến việc chuyển đổi thời gian tiết kiệm ánh sáng ban ngày.
Martin R

Tôi nghĩ NSDate sẽ điều chỉnh điều đó vì nó luôn sử dụng GMT và tiết kiệm ánh sáng ban ngày chỉ là một định dạng hoặc bản địa hóa dựa trên đó. Tuy nhiên, chắc chắn nó sẽ phức tạp hơn trong nhiều tháng, nhiều năm hoặc bất kỳ thứ gì có độ dài thực sự thay đổi.
Daniel Schlaug,

1
@MartinR Tôi đã phải thử nó để tin vào điều đó nhưng thực sự, bây giờ tôi đã làm, tôi cũng thấy rằng wikipedia đề cập đến điều này. Bạn nói đúng. Cảm ơn vì đã cứng đầu với tôi.
Daniel Schlaug

1
Ở đó, đã chỉnh sửa để được chính xác. Nhưng sự hào nhoáng đã biến mất.
Daniel Schlaug

1
nó được xác định theo vị trí, thời điểm và hệ thống lịch. lịch tiếng Do Thái có một tháng nhuận. có một video wwdc tuyệt vời: thực hiện tính toán theo lịch - điều không thể bỏ qua đối với mọi coder ca cao.
vikingosegundo

3

Đây là câu trả lời của tôi cho 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

Hầu như không có bất kỳ thư viện tiêu chuẩn dành riêng cho Swift nào; chỉ là các kiểu số, chuỗi và tập hợp cơ bản.

Hoàn toàn có thể xác định các đường tắt như vậy bằng cách sử dụng các tiện ích mở rộng, nhưng theo như các API ngoài hộp thực tế, không có Ca cao "mới"; Swift chỉ ánh xạ trực tiếp đến các API Cocoa dài dòng cũ giống như chúng đã tồn tại.


2

Tôi sẽ thêm phiên bản của mình mặc dù chủ đề này đã được một năm. Mã của tôi trông như thế này:

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

Tôi hi vọng nó giúp ích cho ai đó.

Chúc mừng,


2

Phương thức của Erin được cập nhật lên Swift 3, Điều này hiển thị các ngày kể từ hôm nay (bất kể thời gian trong ngày)

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

Điều này trả lại sự khác biệt tuyệt đối về số ngày giữa một số Datevà hôm nay:

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

và sau đó sử dụng nó:

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

2

Bạn có thể sử dụng tiện ích mở rộng sau:

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

Sau đó, bạn có thể gọi nó như thế này:

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

Tất cả câu trả lời là tốt. Nhưng đối với Bản địa hóa, chúng tôi cần tính số ngày thập phân ở giữa hai ngày. để chúng tôi có thể cung cấp định dạng thập phân bền vững.

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

Sau đó sử dụng nó như

    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

Swift 3 - Các ngày từ hôm nay cho đến ngày

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

Gọi chức năng như thế này

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

0

tùy chọn dễ dàng hơn sẽ là tạo tiện ích mở rộng vào Ngày

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




}

nếu bạn cần năm tháng ngày và giờ như chuỗi sử dụng

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

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


0

Một lớp lót tiện dụng đẹp mắt:

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

Đây là phiên bản cập nhật của câu trả lời của Emin dành cho Swift 5, kết hợp đề xuất sử dụng buổi trưa thay vì nửa đêm làm thời gian xác định để so sánh các ngày. Nó cũng xử lý lỗi tiềm ẩn của các chức năng ngày khác nhau bằng cách trả về một tùy chọn.

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

Bạn nên sử dụng Lịch gốc của Swift (bỏ NS). Việc sử dụng bảo vệ khi đặt giờ đến 12 giờ đêm là vô nghĩa. Nó sẽ không bao giờ thất bại.
Leo Dabus

gọi startOfDay trước khi đặt giờ thành buổi trưa, điều đó cũng vô nghĩa.
Leo Dabus

0

Giải pháp 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

Phiên bản 2017, sao chép và dán

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
đo lường sự khác biệt giữa phần số của các ngày - Tức là ngày 21 tháng 1 đến ngày 22 tháng 2 sẽ cho 1 ngày chứ không phải 32 ngày như bình thường
Peter Johnson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.