Get weekdays within a month

后端 未结 3 1077
鱼传尺愫
鱼传尺愫 2021-01-03 13:57

I\'m trying to get the dates within a given month.

My plan is to

  1. Get the start and the end dates of a given month,.
  2. Get all the dates that fa
相关标签:
3条回答
  • 2021-01-03 14:26

    With the help of this answer, I was able to accomplish this.

    let calendar = NSCalendar.currentCalendar()
    let normalizedStartDate = calendar.startOfDayForDate(NSDate().startOfMonth)
    let normalizedEndDate = calendar.startOfDayForDate(NSDate().endOfMonth)
    
    var dates = [normalizedStartDate]
    var currentDate = normalizedStartDate
    repeat {
        currentDate = calendar.dateByAddingUnit(NSCalendarUnit.Day, value: 1, toDate: currentDate, options: .MatchNextTime)!
        dates.append(currentDate)
    } while !calendar.isDate(currentDate, inSameDayAsDate: normalizedEndDate)
    
    let weekdays = dates.filter { !calendar.isDateInWeekend($0) }
    weekdays.forEach { date in
        print(NSDateFormatter.localizedStringFromDate(date, dateStyle: .FullStyle, timeStyle: .NoStyle))
    }
    

    And it works!

    Monday, February 1, 2016
    Tuesday, February 2, 2016
    Wednesday, February 3, 2016
    Thursday, February 4, 2016
    Friday, February 5, 2016
    Monday, February 8, 2016
    Tuesday, February 9, 2016
    Wednesday, February 10, 2016
    Thursday, February 11, 2016
    Friday, February 12, 2016
    Monday, February 15, 2016
    Tuesday, February 16, 2016
    Wednesday, February 17, 2016
    Thursday, February 18, 2016
    Friday, February 19, 2016
    Monday, February 22, 2016
    Tuesday, February 23, 2016
    Wednesday, February 24, 2016
    Thursday, February 25, 2016
    Friday, February 26, 2016
    Monday, February 29, 2016
    
    0 讨论(0)
  • 2021-01-03 14:31

    For a more general answer than the one you've given above, you could implement your own SequenceType running over/generating NSDate elements.

    Modifying the SequenceType DateRange implementation from this blog post by Adam Preble (Swift < 2.0), we can construct the following:

    /* Modified version of Adam Preble:s DateRange: http://adampreble.net/blog/2014/09/iterating-over-range-of-dates-swift/ */
    func < (left: NSDate, right: NSDate) -> Bool {
        return left.compare(right) == NSComparisonResult.OrderedAscending
    }
    
    struct DateRange : SequenceType {
        var calendar: NSCalendar
        var startDate: NSDate
        var endDate: NSDate
        var stepUnits: NSCalendarUnit
        var stepValue: Int
    
        func generate() -> Generator {
            return Generator(range: self, firstDate: true)
        }
    
        struct Generator: GeneratorType {
            var range: DateRange
            var firstDate : Bool = true
    
            mutating func next() -> NSDate? {
                if firstDate {
                    firstDate = false
                    return range.startDate
                }
    
                guard let nextDate = range.calendar.dateByAddingUnit(range.stepUnits,
                    value: range.stepValue, toDate: range.startDate,
                    options: NSCalendarOptions.MatchFirst) where !(range.endDate < nextDate) else {
                        return nil
                }
    
                range.startDate = nextDate
                return nextDate
            }
        }
    }
    

    With this, you can iterate over a range of NSDate:s, just as you could over, say, a range of integers, without explicitly needing an array of NSDate objects.

    Example usage (making use of you startOfMonth() and endOfMonth() extensions for NSDate):

    /* Your NSDate extension */
    extension NSDate {
        // ... as in your question above
    }
    
    /* Example usage */
    let formatter = NSDateFormatter()
    formatter.dateFormat = "EEE, MMM. d, yyyy"
    
    // print week days of current month, using DateRange (as well as your extension)
    if let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian) {
    
        let dateRange = DateRange(calendar: calendar,
            startDate: NSDate().startOfMonth,
            endDate: NSDate().endOfMonth,
            stepUnits: NSCalendarUnit.Day,
            stepValue: 1)
    
        for date in dateRange where !calendar.isDateInWeekend(date) {
            print(formatter.stringFromDate(date))
        }
    }
    

    Output:

    /* Mon, Feb. 1, 2016
       Tue, Feb. 2, 2016
       Wed, Feb. 3, 2016
       Thu, Feb. 4, 2016
       Fri, Feb. 5, 2016
       Mon, Feb. 8, 2016
       Tue, Feb. 9, 2016
    
       ...
    
       Thu, Feb. 25, 2016
       Fri, Feb. 26, 2016
       Mon, Feb. 29, 2016 */
    

    A month worth of dates in an array ([NSDate]) is probably not an issue, but using a sequence instead is valuable for larger date spans, and also when you need versatility, w.r.t. re-assigning size and members of an array; especially an array in which you really only use one member, in sequence, at a time.

    E.g. using an NSDate array in the example below would be quite un-necessary (and possibly yield unwanted overhead; but let's leave that as we want to avoid some commenter bringing up the subject of the sins of pre-mature optimization :) )

    // versatile use of DateRange over somewhat "large" ranges of dates
    formatter.dateFormat = "yyyy-MM-dd"
    if let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian),
        startDate = formatter.dateFromString("2016-01-01"),
        endDate = formatter.dateFromString("2016-12-31") {
    
        formatter.dateFormat = "EEE, MMM. d, yyyy"
    
        // initialize dateRange instance
        var dateRange = DateRange(calendar: calendar,
            startDate: startDate,
            endDate: endDate,
            stepUnits: NSCalendarUnit.Day,
            stepValue: 1)
    
        // print all week days of 2016
        for date in dateRange where !calendar.isDateInWeekend(date) {
            print(formatter.stringFromDate(date))
        }
        print("")
    
        // re-use same dateRange instance and print
        // all week days of July 2016 -> June 2017
        formatter.dateFormat = "yyyy-MM-dd"
        if let startDate = formatter.dateFromString("2016-07-01"),
            endDate = formatter.dateFromString("2017-06-30") {
    
            // update dateRange instance
            dateRange.startDate = startDate
            dateRange.endDate = endDate
            formatter.dateFormat = "EEE, MMM. d, yyyy"
            for date in dateRange where calendar.isDateInWeekend(date) {
                print(formatter.stringFromDate(date))
            }
        }
    }
    

    Output:

    /* Mon, Feb. 1, 2016
       Tue, Feb. 2, 2016
       Wed, Feb. 3, 2016
       Thu, Feb. 4, 2016
       Fri, Feb. 5, 2016
       Mon, Feb. 8, 2016
       Tue, Feb. 9, 2016
    
       ...
    
       Fri, Dec. 23, 2016
       Mon, Dec. 26, 2016
       Tue, Dec. 27, 2016
       Wed, Dec. 28, 2016
       Thu, Dec. 29, 2016
       Fri, Dec. 30, 2016
    
       // 2nd range print
       Fri, Jul. 1, 2016
       Mon, Jul. 4, 2016
       Tue, Jul. 5, 2016
       Wed, Jul. 6, 2016
       Thu, Jul. 7, 2016
    
       ...
    
       Fri, Jun. 23, 2017
       Mon, Jun. 26, 2017
       Tue, Jun. 27, 2017
       Wed, Jun. 28, 2017
       Thu, Jun. 29, 2017
       Fri, Jun. 30, 2017  */
    
    0 讨论(0)
  • 2021-01-03 14:40
    • Get the current calendar

      let calendar = NSCalendar.currentCalendar()
      
    • Get the month and year date components from the current date

      let components = calendar.components([.Year, .Month], fromDate: NSDate())
      
    • Get the date of the first day of the month

      let startOfMonth = calendar.dateFromComponents(components)!
      
    • Get the number of days for the current month

      let numberOfDays = calendar.rangeOfUnit(.Day, inUnit: .Month, forDate: startOfMonth).length
      
    • Create an array of NSDate instances for every day in the current month

      let allDays = Array(0..<numberOfDays).map{ calendar.dateByAddingUnit(.Day, value: $0, toDate: startOfMonth, options: [])!}
      
    • Filter the days within a weekend

      let workDays = allDays.filter{ !calendar.isDateInWeekend($0) }
      

    Swift 3:

    let calendar = Calendar.current
    let components = calendar.dateComponents([.year, .month], from: Date())
    let startOfMonth = calendar.date(from:components)!
    let numberOfDays = calendar.range(of: .day, in: .month, for: startOfMonth)!.upperBound
    let allDays = Array(0..<numberOfDays).map{ calendar.date(byAdding:.day, value: $0, to: startOfMonth)!}
    let workDays = allDays.filter{ !calendar.isDateInWeekend($0) }
    
    0 讨论(0)
提交回复
热议问题