【问题标题】:Swift days between two NSDates两个 NSDate 之间的快速日子
【发布时间】:2014-09-03 14:18:26
【问题描述】:

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

例如就像在 Ruby 中我会做的那样:

(end_date - start_date).to_i

【问题讨论】:

  • 我认为您仍然必须使用 NSCalendar 和 NSDateComponents (SO 上必须有数百个答案)。 - 如果您正在寻找“新的和令人敬畏的可能性”,那么展示您当前的解决方案以进行比较会很有帮助。
  • 现在这很容易,您不必使用任何“NS”。我输入了 2017 年的答案,进行复制和粘贴。

标签: ios swift date date-difference


【解决方案1】:

您还必须考虑时差。例如,如果您比较日期 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)

【讨论】:

  • 您实际上可能想要检查 12pm(中午)而不是 startOfDayForDate - 由于调整时区和 DST,应该不太可能出错。
  • 将日期设置为中午可以这样:calendar.date(bySettingHour: 12, minute: 00, second: 00, of: calendar.startOfDay(for: firstDate))
  • 设置中午的短版(startOfDay()似乎没有必要):calendar.date(bySettingHour: 12, minute: 0, second: 0, of: firstDate)
  • 谁能解释一下当 Calendar.current 的 timeZone 与初始化 firstDate/secondDate 的时区不匹配时会发生什么(例如从时间格式化程序)?如果我们想知道我们知道在给定时区(不同于当前)的日期和当前时区的当前日期之间的距离(以天为单位),我们应该怎么做 - 我们应该如何转换日历?跨度>
  • 另请注意,这将无法正常工作 - 例如firstDate = 2021-01-13 22:00:00 +0000,startOfDay = 2021-01-13 22:00:00 +0000,secondDate = 2021-01-14 12:54:46 +0000,startOfDay = 2021-01 -13 22:00:00 +0000 。即使在第二天考虑 secondDate,使用 startOfDay 也会使两个日期相等。出于这个原因,我认为最好使用将小时值 / 24 添加到天值并向上取整。
【解决方案2】:

这是我对 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”
  • 如果我们比较 todaytomorrow,则返回 0
【解决方案3】:

我看到了几个 Swift3 答案,所以我会添加自己的:

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

命名感觉更Swifty,一行一行,使用最新的dateComponents()方法。

【讨论】:

    【解决方案4】:

    这里非常好,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
        }
    
    }
    

    【讨论】:

    • date 在函数参数中应该是sinceDate
    • @TheTiger - 非常感谢您指出这个答案的最大错误。我会尽快测试和更新答案。
    • 我的荣幸!我已经为days 测试过它,它工作正常。
    • 好答案。我只建议func years(since date: Date) -> Int? { return Calendar.current.dateComponents[.year], from: date, to: self).years },你可以称它为let y = date1.years(since: date2)。这可能更符合现代命名约定。
    【解决方案5】:

    我翻译了我的Objective-C answer

    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
    • options 现在是预期参数
    • 运行 Swift 2 这对我有用:let components = cal.components(.Day, fromDate: startDate, toDate: endDate, options: [])
    • @TaylorAllred 现在就.Day
    【解决方案6】:

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

    【讨论】:

      【解决方案7】:

      Swift 5. 感谢以上Emin Buğra SaralstartOfDay 建议。

      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
      // or
      let diff = start.daysBetween(date: end) // 365
      

      【讨论】:

      • 最好把它们都移到中午,而不是00:00,以避免很多问题。
      【解决方案8】:

      这是 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)")
      

      【讨论】:

        【解决方案9】:

        斯威夫特 5

        工作中,需要把两天的时间设置成一样,如果按秒就错了

        func daysBetween(start: Date, end: Date) -> Int {
            let start = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: start)!
            let end = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: end)!
            return Calendar.current.dateComponents([.day], from: start, to: end).day ?? 0
        }
        

        【讨论】:

          【解决方案10】:

          swift 内置的东西还是很基础的。因为他们应该处于这个早期阶段。但是您可以添加自己的东西,但会带来重载运算符和全局域函数的风险。不过,它们将在您的模块本地。

          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
          

          【讨论】:

          • 不要在一天中使用 (24*60*60)。这不考虑夏令时转换。
          • 我认为 NSDate 会对此进行调整,因为它始终使用 GMT,而夏令时只是一种格式或本地化。当然,几个月、几年或任何长度可变的东西都会变得更棘手。
          • @MartinR 我不得不尝试相信它,但事实上,既然我这样做了,我也看到维基百科提到了这一点。你是对的。谢谢你对我的固执。
          • 那里,编辑正确。但是那种浮华的感觉消失了。
          • 它由位置、时间点和日历系统定义。希伯来历有闰月。有一个很棒的 wwdc 视频:执行日历计算 - 每个可可编码器的必看视频。
          【解决方案11】:

          这是 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 点时使用警卫是没有意义的。它永远不会失败。
          • 在将小时设置为中午之前调用 startOfDay 也是没有意义的。
          • 这可以是计算变量而不是函数。
          【解决方案12】:

          这是我对 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!
          }
          

          【讨论】:

            【解决方案13】:

            您可以使用以下扩展名:

            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)
            

            【讨论】:

              【解决方案14】:

              目前几乎没有任何 Swift 特定的标准库;只是精简的基本数字、字符串和集合类型。

              使用扩展来定义这样的简写是完全可能的,但就实际开箱即用的 API 而言,没有“新”的 Cocoa; Swift 只是直接映射到旧的冗长 Cocoa API,因为它们已经存在。

              【讨论】:

                【解决方案15】:

                即使这个帖子已经有一年了,我也会添加我的版本。我的代码如下所示:

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

                我希望它对某人有所帮助。

                干杯,

                【讨论】:

                  【解决方案16】:

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

                  【讨论】:

                    【解决方案17】:

                    这会返回一些 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
                    }
                    

                    【讨论】:

                      【解决方案18】:

                      更简单的选择是在 Date 上创建扩展

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

                      【讨论】:

                        【解决方案19】:

                        斯威夫特 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)
                            }
                        }
                        

                        【讨论】:

                          【解决方案20】:

                          所有答案都很好。但是对于本地化,我们需要计算两个日期之间的小数天数。所以我们可以提供可持续的十进制格式。

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

                          【讨论】:

                            【解决方案21】:

                            好方便的一个班轮:

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

                            【讨论】:

                              【解决方案22】:

                              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)
                              

                              【讨论】:

                                【解决方案23】:
                                  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 tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date())!

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

                                【讨论】:

                                  【解决方案24】:
                                  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."
                                          }
                                      }
                                  

                                  【讨论】:

                                    【解决方案25】:
                                    extension Date {
                                    static func - (recent: Date, previous: Date) -> DateComponents {
                                        var dateComponents = DateComponents()
                                        dateComponents.year = Calendar.current.dateComponents([.day], from: previous, to: recent).year
                                        dateComponents.month = Calendar.current.dateComponents([.month], from: previous, to: recent).month
                                        dateComponents.day = Calendar.current.dateComponents([.day], from: previous, to: recent).day
                                        dateComponents.hour = Calendar.current.dateComponents([.hour], from: previous, to: recent).hour
                                        dateComponents.minute = Calendar.current.dateComponents([.minute], from: previous, to: recent).minute
                                        dateComponents.second = Calendar.current.dateComponents([.second], from: previous, to: recent).second
                                        return dateComponents
                                       }
                                    }
                                    

                                    【讨论】:

                                    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
                                    【解决方案26】:

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

                                    【讨论】:

                                      【解决方案27】:
                                      let calendar = NSCalendar.currentCalendar();
                                      let component1 = calendar.component(.Day, fromDate: fromDate)
                                      let component2 = calendar.component(.Day, fromDate: toDate)
                                      let difference  = component1 - component2
                                      

                                      【讨论】:

                                      • 衡量日期数字部分之间的差异 - 即1 月 21 日至 2 月 22 日将给予 1 天,而不是应有的 32 天
                                      【解决方案28】:

                                      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
                                      

                                      【讨论】:

                                      • 这是另一个答案的副本,只是添加了日期解析,从未被要求
                                      猜你喜欢
                                      • 2012-05-30
                                      • 2010-12-31
                                      • 2021-08-23
                                      • 1970-01-01
                                      • 1970-01-01
                                      • 1970-01-01
                                      • 2013-12-29
                                      • 2017-10-20
                                      • 2011-11-01
                                      相关资源
                                      最近更新 更多