Having looked at a few different suggestions on SO, I've not been able to determine why the function below does not work. It seems to return 6 for some months and 5 for others. When changing weeks for days it works perfectly.
For example, trying
weeksInMonth(8, forYear 2015)
Results in 6.
I believe I have a mis-understanding of what 'firstWeekday' property on calendar does but haven't found an adequate explanation by Apple or online.
Have tried both .WeekOfMonth and .WeekOfYear. Again can't find explanation of exact difference.
Any suggestions would be greatly welcome.
func weeksInMonth(month: Int, forYear year: Int) -> Int?
{
if (month < 1 || month > 12) { return nil }
let dateString = String(format: "%4d/%d/01", year, month)
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy/MM/dd"
if let date = dateFormatter.dateFromString(dateString),
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
{
calendar.firstWeekday = 2 // Monday
let weekRange = calendar.rangeOfUnit(.WeekOfMonth, inUnit: .Month, forDate: date)
let weeksCount = weekRange.length
return weeksCount
}
else
{
return nil
}
}
Update:
Apologies my question was not clear. I'm trying to work out how many weeks there are in a month that include a Monday in them. For August this should be 5.
Your code computes the number of weeks which occur (complete or partially)
in a month. What you apparently want is the number of Mondays
in the given month. With NSDateComponents
and in particular with
the weekdayOrdinal
property you can compute the first
(weekdayOrdinal=1
) and last (weekdayOrdinal=-1
) Monday
in a month. Then compute the difference in weeks (and add one).
A possible implementation in Swift 2:
func numberOfMondaysInMonth(month: Int, forYear year: Int) -> Int?
{
let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
calendar.firstWeekday = 2 // 2 == Monday
// First monday in month:
let comps = NSDateComponents()
comps.month = month
comps.year = year
comps.weekday = calendar.firstWeekday
comps.weekdayOrdinal = 1
guard let first = calendar.dateFromComponents(comps) else {
return nil
}
// Last monday in month:
comps.weekdayOrdinal = -1
guard let last = calendar.dateFromComponents(comps) else {
return nil
}
// Difference in weeks:
let weeks = calendar.components(.WeekOfMonth, fromDate: first, toDate: last, options: [])
return weeks.weekOfMonth + 1
}
Note: That a negative weekdayOrdinal
counts backwards from the end of the month is not apparent form the documentation. It was observed in
Determine NSDate for Memorial Day in a given year and confirmed by Dave DeLong).
Update for Swift 3:
func numberOfMondaysInMonth(_ month: Int, forYear year: Int) -> Int? {
var calendar = Calendar(identifier: .gregorian)
calendar.firstWeekday = 2 // 2 == Monday
// First monday in month:
var comps = DateComponents(year: year, month: month,
weekday: calendar.firstWeekday, weekdayOrdinal: 1)
guard let first = calendar.date(from: comps) else {
return nil
}
// Last monday in month:
comps.weekdayOrdinal = -1
guard let last = calendar.date(from: comps) else {
return nil
}
// Difference in weeks:
let weeks = calendar.dateComponents([.weekOfMonth], from: first, to: last)
return weeks.weekOfMonth! + 1
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With