I'm trying to calculate the next and previous pay days for a user, when given a specified date.
I'm storing two properties for the user like so:
public var firstPayDay: NSDate {
get { return (NSDate(dateNumber: self["firstPayDay"] as! NSNumber)) }
set(date) {
self["firstPayDay"] = date.dateNumber()
self.payDayOfWeek = self.firstPayDay.dayOfWeek(zeroBased: true)!
// day of week from 0 - 6
}
}
When first introduced to the app, the user is asked to provide their next pay day. This is stored as an integer, e.g. 20160126 on the user object, and the following convenience is used to convert it to NSDate:
convenience init(dateNumber: NSNumber) { let dateFormatter = NSDateFormatter() dateFormatter.dateFormat = "yyyyMMdd" self.init(timeInterval:0, sinceDate: dateFormatter.dateFromString(dateNumber.stringValue)!) }
public var payDayOfWeek: Int {
get { return (self["payDayOfWeek"] as! Int) }
set(dayOfWeek) { self["payDayOfWeek"] = dayOfWeek }
}
When firstPayDay is set, payDayOfWeek is updated using the 0-based (Sunday ⇢ Saturday) index of the weekday.
I can use this method just fine to get the next and previous pay days for weekly pay periods like this, but I'm struggling how to do this for bi-weekly pay periods?
To determine the previous and next bi-weekly pay days, I would need to calculate in increments of two weeks from the first pay day, then determine where the specified day fits in between the two. How would I do that in code with the two known properties firstPayDay
and payDayOfWeek
?
Expected output:
user.firstPayDay = NSDate(fromNumber: 20160101) // January 1st, 2016
print(user.payDayOfWeek) // 5 (Friday, inferred from firstPayDay setter)
// For Today, 20160126
print(user.nextPayDayForDate(20160126)) // should return 20160129, the closest Friday an even two weeks from 20160101 and in the future
print(user.prevPayDayForDate(20160126)) // should return 20160115, the closest Friday an even two weeks from 20160101 and in the past
// For Saturday, 20160130
print(user.nextPayDayForDate(20160130)) // should return 20160212, the closest Friday an even two weeks from 20160101 and in the future
print(user.prevPayDayFordate(20160130)) // should return 20160129, the closest Friday an even two weeks from 20160101 and in the past
Work in Progress
internal func nextWeekBasedPayDate(payFrequency: PayFrequency, firstPayDay: NSDate) -> NSDate? {
let interval: Int = payFrequency == .Weekly ? 7 : 14
let calendar: NSCalendar = NSCalendar.autoupdatingCurrentCalendar()
guard
let date: NSDate = calendar.dateByAddingUnit(.Day, value: interval, toDate: self.previousWeekBasedPayDate(payFrequency, firstPayDay), options: [])! else { return nil }
return date.at12pm()
}
internal func previousWeekBasedPayDate(payFrequency: PayFrequency, firstPayDay: NSDate) -> NSDate? {
let interval: Int = payFrequency == .Weekly ? 7 : 14
let calendar: NSCalendar = NSCalendar.autoupdatingCurrentCalendar()
guard
let daysSinceFirstPayDate: Int = calendar.components([ .Day ], fromDate: firstPayDay, toDate: self, options: []).day,
let daysSincePreviousPayDate: Int = daysSinceFirstPayDate % interval + (daysSinceFirstPayDate < 0 ? interval : 0),
let date: NSDate = calendar.dateByAddingUnit(.Day, value: -daysSincePreviousPayDate, toDate: self, options: [])! else { return nil }
return date.at12pm()
}
internal func at12pm() -> NSDate? {
let cal = NSCalendar.currentCalendar()
return cal.dateBySettingHour(12, minute: 0, second: 0, ofDate: self, options: [])
}
Then to calculate a pay period:
func currentPayPeriod(payFrequency: PayFrequency, firstPayDay: NSDate) -> [NSDate]? {
var startDate: NSDate
var endDate: NSDate
switch payFrequency {
case .Monthly:
startDate = self.startOfMonth()!
endDate = self.endOfMonth()!
case .Weekly, .BiWeekly:
startDate = (self.previousPayDay(payFrequency, firstPayDay: firstPayDay))!
endDate = (self.nextPayDay(payFrequency, firstPayDay: firstPayDay)?.addDays(-1))!
case .SemiMonthly:
startDate = self.startOfMonth()!
endDate = self.middleOfMonth()!
}
return [startDate, endDate]
}
This seems to work perfectly:
/*
payPeriod [NSDate] 2 values
[0] __NSTaggedDate * 2016-01-24 20:00:00 UTC 0xe41bc5564c000000
[1] __NSTaggedDate * 2016-01-30 20:00:00 UTC 0xe41bc5d4dc000000
*/