两个NSDate之间的短暂日子


110

我想知道是否有一些新的和令人敬畏的可能性来获取Swift /“新”可可中两个NSDate之间的天数?

例如像在Ruby中,我会这样做:

(end_date - start_date).to_i

5
我认为您仍然必须使用NSCalendar和NSDateComponents(为此必须有数百个答案)。-如果您正在寻找“新的和令人敬畏的可能性”,那么展示当前的解决方案进行比较将很有帮助。
Martin R

1
现在,这非常容易,您无需使用任何“ NS”。我输入了2017年的答案,以进行复制和粘贴。
Fattie

Answers:


245

您还必须考虑时差。例如,如果您比较日期2015-01-01 10:002015-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的可能性较小。
brandonscript '16

11
可以将日期设置为中午可以这样进行:calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: firstDate))
MonsieurDart

设定中午较短的版本(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帖子的组件一起使用。它返回一个整数,表示两个日期之间的正确天数。<竖起大拇指>
删除我的帐户

我喜欢它,但函数名称应为“ daysBetweenDates”
mbonness

4
如果我们正在比较today,则返回0,并且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

最困难的部分是自动完成功能坚持fromDate和toDate将为NSDate?,但实际上它们必须NSDate!如参考资料中所示。

我看不到操作员的良好解决方案会是什么样子,因为您希望在每种情况下都以不同的方式指定单位。您可以返回时间间隔,但不会获得太多收益。


看起来.DayCalendarUnit已弃用。我相信现在您应该使用它.CalendarUnitDay
TaylorAllred

2
选项现在是期望的参数
Departamento B

2
运行Swift 2可以为我工作:let components = cal.components(.Day, fromDate: startDate, toDate: endDate, options: [])
Andrej

@TaylorAllred只是.Day现在
威廉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-非常感谢您强调此答案的最大错误。.我将尽快测试并更新答案。
Krunal

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

Swift 3.感谢上面的EminBuğraSaralstartOfDay建议。

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)。这不会考虑夏令时转换。
Martin R

我认为NSDate会对此进行调整,因为它始终使用GMT,而夏令时只是一种格式化或本地化。当然,它在几个月,几年或任何长度可变的情况下都会变得棘手。
Daniel Schlaug 2014年

1
@MartinR我必须尝试使其相信它,但实际上,现在我也看到维基百科提到了这一点。你是对的。感谢您对我的固执。
Daniel Schlaug 2014年

1
在那里,编辑正确。但是那种浮华消失了。
Daniel Schlaug 2014年

1
它由位置,时间点和日历系统定义。希伯来历有a月。有一个很棒的wwdc视频:执行日历计算-每个可可编码器的必看项。
vikingosegundo 2014年

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而言,没有“新”的Cocoa。Swift只是直接映射到已经存在的相同的旧详细Cocoa 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

Erin的方法更新为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."
        }
    }

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

更简单的选择是在日期上创建扩展名

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,value:1,to:Date())!

令dc =明天.completeOffset(来自:Date())


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

这是Emin针对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点时使用防护罩毫无意义。它永远不会失败。
Leo Dabus

在将小时设置为中午之前调用startOfDay也是没有意义的。
Leo Dabus

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
该措施的dates-即1月21日日至2月22日的数字部分之间的差异将给予1天,不是32天为它应该
彼得·约翰逊
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.