Swift - Check if date is in next week / month. ( isDateInNextWeek() isDateInNextMonth() )

喜你入骨 提交于 2020-01-01 19:33:50

问题


We have those convenient functions in calendar:

let calendar = NSCalendar.currentCalendar()
calendar.isDateInToday(date)
calendar.isDateInTomorrow(date)

But I am missing those two:

  • calendar.isDateInNextWeek(date)

  • calendar.isDateInNextMonth(date)

As @Rob mentioned, the meaning is: "in the calendar week starting this coming Sunday, going through the following Saturday"

I having a hard time figuring out how to implement those functions in a robust way that covers all the corner cases.

Can someone provide an assistant?


回答1:


The algorithm is not very spectacular:

  1. Calculate start of current week
  2. Use result from 1 to calculate start of next week
  3. Use result from 2 to calculate start of week after next week
  4. Date is in next week if its on or after the start of next week and before the start of the week after next week

NSCalendar does most of the work for us. But it looks still a bit intimidating, at least if you don't use these date methods often. You should read the documentation of all of these methods to understand them.

let calendar = NSCalendar.currentCalendar()
let date = NSDate()

var startOfThisWeek: NSDate?
if !calendar.rangeOfUnit(.CalendarUnitWeekOfMonth, startDate: &startOfThisWeek, interval: nil, forDate: date) {
    fatalError("Can't calculate start of this week")
}
let startOfNextWeek = calendar.dateByAddingUnit(.CalendarUnitWeekOfMonth, value: 1, toDate: startOfThisWeek!, options: nil)!
let startOfNextNextWeek = calendar.dateByAddingUnit(.CalendarUnitWeekOfMonth, value: 1, toDate: startOfNextWeek, options: nil)!

Just switch .CalendarUnitWeekOfMonth with .CalendarUnitMonth and you get the calculation for this, next and the following month.

var startOfThisMonth: NSDate?
if !calendar.rangeOfUnit(.CalendarUnitMonth, startDate: &startOfThisMonth, interval: nil, forDate: date) {
    fatalError("Can't calculate start of this month")
}
let startOfNextMonth = calendar.dateByAddingUnit(.CalendarUnitMonth, value: 1, toDate: startOfThisMonth!, options: nil)!
let startOfNextNextMonth = calendar.dateByAddingUnit(.CalendarUnitMonth, value: 1, toDate: startOfNextMonth, options: nil)!

 

And the test:

let testDate = NSDate(timeIntervalSinceNow: 7*24*60*60)

if startOfThisWeek <= testDate && testDate < startOfNextWeek {
    println("\(testDate) is this week")
}
if startOfNextWeek <= testDate && testDate < startOfNextNextWeek {
    println("\(testDate) is next week")
}
if startOfThisMonth <= testDate && testDate < startOfNextMonth {
    println("\(testDate) is this month")
}
if startOfNextMonth <= testDate && testDate < startOfNextNextMonth {
    println("\(testDate) is next month")
}

Result:

"2015-03-22 02:55:19 +0000 is next week"
"2015-03-22 02:55:19 +0000 is this month"

Sounds right.

And if you want to use <=, ==, < etc. instead of the ugly (and confusing) NSComparisonResult you need this as well:

public func ==(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs === rhs || lhs.compare(rhs) == .OrderedSame
}

public func <(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.compare(rhs) == .OrderedAscending
}

extension NSDate: Comparable { }

This is iOS8 code. If you want to write code that works on older versions you have to replace dateByAddingUnit:value::toDate:options: by good old NSDateComponents. i.e:

let offSetComponents = NSDateComponents()
offSetComponents.weekOfMonth = 1
let startOfNextWeek = calendar.dateByAddingComponents(offSetComponents, toDate: startOfThisWeek, options: nil)



回答2:


Problem solved using Swift 5 and extending Calendar.

extension Calendar {
  private var currentDate: Date { return Date() }

  func isDateInThisWeek(_ date: Date) -> Bool {
    return isDate(date, equalTo: currentDate, toGranularity: .weekOfYear)
  }

  func isDateInThisMonth(_ date: Date) -> Bool {
    return isDate(date, equalTo: currentDate, toGranularity: .month)
  }

  func isDateInNextWeek(_ date: Date) -> Bool {
    guard let nextWeek = self.date(byAdding: DateComponents(weekOfYear: 1), to: currentDate) else {
      return false
    }
    return isDate(date, equalTo: nextWeek, toGranularity: .weekOfYear)
  }

  func isDateInNextMonth(_ date: Date) -> Bool {
    guard let nextMonth = self.date(byAdding: DateComponents(month: 1), to: currentDate) else {
      return false
    }
    return isDate(date, equalTo: nextMonth, toGranularity: .month)
  }

  func isDateInFollowingMonth(_ date: Date) -> Bool {
    guard let followingMonth = self.date(byAdding: DateComponents(month: 2), to: currentDate) else {
      return false
    }
    return isDate(date, equalTo: followingMonth, toGranularity: .month)
  }
}

Usage:

let day: Double = 60 * 60 * 24
let currentDate = Date() // Thu 25 Apr 2019
let futureDate = Date(timeInterval: 3 * day, since: currentDate) // Sun 28 Apr 2019
if Calendar.current.isDateInThisWeek(futureDate) { 
    // this will be executed if first day of week is Monday
} else {
    // this will be executed if first day of week is Sunday
}

Code evaluates if the dates are in the range (sameWeek, nextWeek) according to Calendar instance.

If Calendar instance is current it will determine start of the week (Monday or Sunday) according to device settings, but if you want different behaviour you can change Calendar instance: Calendar(identifier: .chinese).isDateInNextWeek(someDate)

This code also works if we're trying to create DateComponents with improper values like so: DateComponents(year: 2019, month: 13). In this case it creates date 01 Jan 2020.




回答3:


I came up with this naive code:

extension NSCalendar
{
    func isDateInNextWeek(value: NSDate) -> Bool
    {
        let componentsValue = self.components(.WeekOfYearCalendarUnit | .YearCalendarUnit,  fromDate: value)
        let componentsToday = self.components(.WeekOfYearCalendarUnit | .YearCalendarUnit,  fromDate: NSDate())

        let weekOfYearValue = componentsValue.weekOfYear
        let weekOfYearToday = componentsToday.weekOfYear
        let yearValue = componentsValue.year
        let yearToday = componentsToday.year

        if(yearValue < yearToday)
        {
            return false
        }
        else if(yearValue == yearToday && weekOfYearValue - weekOfYearToday == 1)
        {
           return true
        }
        else if(yearValue - yearToday == 1 && weekOfYearValue == 1 && weekOfYearToday == 52)
        {
            return true
        }
        else
        {
            return false
        }
    }
}

But I must be missing some edge cases here.



来源:https://stackoverflow.com/questions/29055654/swift-check-if-date-is-in-next-week-month-isdateinnextweek-isdateinnext

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!