Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculate business days and weekend days between 2 NSDates

I'm trying to calculate the amount of workdays + business days between 2 NSDates but I can't seem to find a proper solution. I can currently find the amount of Days between 2 NSDates like so:

func reloadData(){
    let cal = NSCalendar.currentCalendar()

    var daysInt = 0

    let days = cal.components(.Day, fromDate: selectedDateTimePointTwo, toDate: selectedDateTimePointOne, options: [])
    daysInt = days.day

    workDaysLabel.text = "work days: \(daysInt)"
    weekendDaysLabel.text = "weekend days: "
}

Can anyone point me in the right direction?

like image 526
Rutger Huijsmans Avatar asked Sep 12 '16 02:09

Rutger Huijsmans


2 Answers

First, if you are using Swift 2 you should make NSDate conform to Comparable protocol:

extension NSDate: Comparable { }

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

Second, You can use Calendar isDateInWeekend to check if any date it is a weekend day or not, and you can use dateByAddingUnit to get add a day to the start date until the end date:

Create those extensions to help you:

edit/update: Swift 4

extension Calendar {
    static let iso8601 = Calendar(identifier: .iso8601)
}


extension Date {
    var isDateInWeekend: Bool {
        return Calendar.iso8601.isDateInWeekend(self)
    }
    var tomorrow: Date {
        return Calendar.iso8601.date(byAdding: .day, value: 1, to: noon)!
    }
    var noon: Date {
        return Calendar.iso8601.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
    }
}

and a method to count the days:

func coutDays(from start: Date, to end: Date) -> (weekendDays: Int, workingDays: Int) {
    guard start < end else { return (0,0) }
    var weekendDays = 0
    var workingDays = 0
    var date = start.noon
    repeat {
        if date.isDateInWeekend {
            weekendDays +=  1
        } else {
            workingDays += 1
        }
        date = date.tomorrow
    } while date < end
    return (weekendDays, workingDays)
}

Testing:

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let start = DateComponents(calendar: .iso8601, year: 2016).date!  // "Jan 1, 2016, 12:00 AM"
        let end = DateComponents(calendar: .iso8601, year: 2017).date!  // "Jan 1, 2017, 12:00 AM"
        print(coutDays(from: start, to: end))  // 105, 261
    }
}
like image 139
Leo Dabus Avatar answered Sep 28 '22 00:09

Leo Dabus


Here is Swift 5 version. I also used the current device calendar instead of the iso one

func nextDay(in calendar: Calendar) -> Date? {
    return calendar.date(byAdding: .day, value: 1, to: self)
}

func daysCount(until endDate: Date) -> (workingDays: Int, weekends: Int) {
    let calendar = Calendar.current
    var weekends = 0
    var workingDays = 0
    var date = self
    while date < endDate {
        if calendar.isDateInWeekend(date) {
            weekends += 1
        } else {
            workingDays += 1
        }

        guard let nextDay = date.nextDay(in: calendar) else {
            fatalError("Failed to instantiate a next day")
        }

        date = nextDay
    }

    return (workingDays, weekends)
}
like image 28
mukaissi Avatar answered Sep 28 '22 01:09

mukaissi