get number of weekdays in a select month?

How can I get the number of weekdays (Monday to Friday) in a select month?

The month has to be in the context of a year, as in a date object.

Brute-force method:

set dateWithMonth to (current date)

set thisMonth to dateWithMonth's month
set nWeekdays to 0

repeat with i from 1 to 31
	set dateWithMonth's day to i
	if (dateWithMonth's month is not thisMonth) then exit repeat
	set w to dateWithMonth's weekday
	if not ((w is Saturday) or (w is Sunday)) then set nWeekdays to nWeekdays + 1
end repeat

return nWeekdays

I’ll try to think of a date-math version.

Excellent Thankyou

much more code but probably faster (however I’m sure Nigel will provide the ultimate solution :wink: )


set numberOfWeekdays to numberOfWeekdaysInCurrentMonthForDate(current date)


on numberOfWeekdaysInCurrentMonthForDate(aDate)
	
	-- calculate number of days of aDate
	set numberOfDaysInCurrentMonth to 32 - ((aDate + (32 - (aDate's day)) * days)'s day)
	
	-- calculate the weekday of 1st day of current month
	tell (aDate - (((aDate's day) - 1) * days)) to set weekDayOfFirstOfMonth to its weekday
	
	-- each month has always at least 8 weekend days
	set defaultNumberOfWorkingDays to numberOfDaysInCurrentMonth - 8
	
	if weekDayOfFirstOfMonth is Sunday then
		-- + 1 weekend day if numberOfDaysInCurrentMonth > 28
		return defaultNumberOfWorkingDays - ((numberOfDaysInCurrentMonth > 28) as integer)
	else if weekDayOfFirstOfMonth is Thursday then
		-- + 1 weekend day if numberOfDaysInCurrentMonth = 31
		return defaultNumberOfWorkingDays - ((numberOfDaysInCurrentMonth = 31) as integer)
	else if weekDayOfFirstOfMonth is Friday then
		-- + 1 weekend day if numberOfDaysInCurrentMonth = 30
		-- + 2 weekend days if numberOfDaysInCurrentMonth = 31
		set additionWeekendDays to numberOfDaysInCurrentMonth - 29
		if additionWeekendDays < 1 then
			return defaultNumberOfWorkingDays
		else
			return defaultNumberOfWorkingDays - additionWeekendDays
		end if
	else if weekDayOfFirstOfMonth is Saturday then
		-- + 1 weekend day if numberOfDaysInCurrentMonth = 29
		-- + 2 weekend days if numberOfDaysInCurrentMonth > 29
		if numberOfDaysInCurrentMonth < 29 then
			return defaultNumberOfWorkingDays
		else if numberOfDaysInCurrentMonth = 29 then
			return defaultNumberOfWorkingDays - 1
		else
			return defaultNumberOfWorkingDays - 2
		end if
	else
		return defaultNumberOfWorkingDays
	end if
end numberOfWeekdaysInCurrentMonthForDate

Hello.

Here is my take, I use the fact that there is 20 days in a month, whatever day the month starts with.

if it is 29 days, and starts with a saturday, then the daynumber is the same as above.

If the week starts with a thursday, and there is 31 days in the month, then I have an extranous saturday, so I must substract 9 weekend-days from the number of days in the month.

If the week starts with a friday and there is 31 days I have two extra weekend - days that must be subtracted from the number of days of the month, 1 less to subtract if the number of days is 30.

Else if it starts with saturday, or sunday, I add 2 resepctively 1 to the 8 weekend days, which I then subtract from the total number of days in the month.

set numberOfWeekdays to numberOfWorkingdaysInMonthFor(current date)

on numberOfWorkingdaysInMonthFor(aDate)
	
	-- calculate number of days of aDate
	set numberOfDaysInCurrentMonth to 32 - ((aDate + (32 - (aDate's day)) * days)'s day)
	
	if numberOfDaysInCurrentMonth = 28 then return 20
	
	-- calculate the weekday of 1st day of current month
	tell (aDate - (((aDate's day) - 1) * days)) to set weekDayOfFirstOfMonth to its weekday
	
	
	if weekDayOfFirstOfMonth is Thursday then
		if numberOfDaysInCurrentMonth = 31 then return numberOfDaysInCurrentMonth - 9
		return numberOfDaysInCurrentMonth - 8
	end if
	
	if weekDayOfFirstOfMonth is Friday then
		if numberOfDaysInCurrentMonth = 31 then return numberOfDaysInCurrentMonth - 10
		if numberOfDaysInCurrentMonth = 30 then return numberOfDaysInCurrentMonth - 9
	end if
	
	if weekDayOfFirstOfMonth is Saturday then
		if numberOfDaysInCurrentMonth = 29 then
			return numberOfDaysInCurrentMonth - 9
		else
			return numberOfDaysInCurrentMonth - 10
		end if
	end if
	
	if weekDayOfFirstOfMonth is Sunday then return numberOfDaysInCurrentMonth - 9
	
	return numberOfDaysInCurrentMonth - 8

end numberOfWorkingdaysInMonthFor

Edit

After fixing this, I’ll have to go back and see how much this differs from Stefan’s solution. :slight_smile:
I didn’t take height for the early week-ending or start a thursday/friday, with a daynumber of 30/31 the first time around.

nice one, but it doesn’t consider the thursday case with 31 days which should result 22

Hello Stefan.

It wouldn’t look good without the way of calculating number of days in a month, which I snagged from you (together with most of the code). :slight_smile:

I have fixed the “fluctuating week-ending”, it should be all right now.

you gave me the idea to distinguish the days of the months instead of the weekdays


set numberOfWeekdays to numberOfWeekdaysInCurrentMonthForDate(current date)

on numberOfWeekdaysInCurrentMonthForDate(aDate)
	
	-- calculate number of days of aDate
	set numberOfDaysInCurrentMonth to 32 - ((aDate + (32 - (aDate's day)) * days)'s day)
	
	-- calculate the weekday of 1st day of current month
	tell (aDate - (((aDate's day) - 1) * days)) to set weekDayOfFirstOfMonth to its weekday
	
	-- each month has always at least 8 weekend days
	set defaultNumberOfWorkingDays to numberOfDaysInCurrentMonth - 8
	
	if numberOfDaysInCurrentMonth = 28 then
		return defaultNumberOfWorkingDays
	else if numberOfDaysInCurrentMonth = 29 then
		return defaultNumberOfWorkingDays - ((weekDayOfFirstOfMonth is Sunday or weekDayOfFirstOfMonth is Saturday) as integer)
	else if numberOfDaysInCurrentMonth = 30 then
		if (weekDayOfFirstOfMonth is Friday) then
			return defaultNumberOfWorkingDays - 1
		else if (weekDayOfFirstOfMonth is Saturday) then
			return defaultNumberOfWorkingDays - 2
		end if
	else
		if (weekDayOfFirstOfMonth is Thursday) then
			return defaultNumberOfWorkingDays - 1
		else if (weekDayOfFirstOfMonth is Friday) then
			return defaultNumberOfWorkingDays - 2
		end if
	end if
	return defaultNumberOfWorkingDays
end numberOfWeekdaysInCurrentMonthForDate

or


set numberOfWeekdays to numberOfWeekdaysInCurrentMonthForDate(current date)

on numberOfWeekdaysInCurrentMonthForDate(aDate)
	
	-- calculate number of days of aDate
	set numberOfDaysInCurrentMonth to 32 - ((aDate + (32 - (aDate's day)) * days)'s day)
	
	-- calculate the weekday of 1st day of current month
	tell (aDate - (((aDate's day) - 1) * days)) to set weekDayOfFirstOfMonth to its weekday
	
	-- each month has always at least 8 weekend days
	set defaultNumberOfWorkingDays to numberOfDaysInCurrentMonth - 8
	
	if numberOfDaysInCurrentMonth = 28 then
		return defaultNumberOfWorkingDays
	else if numberOfDaysInCurrentMonth = 29 then
		return defaultNumberOfWorkingDays - ((weekDayOfFirstOfMonth is Sunday or weekDayOfFirstOfMonth is Saturday) as integer)
	else if (numberOfDaysInCurrentMonth = 30 and weekDayOfFirstOfMonth is Friday) or (numberOfDaysInCurrentMonth = 31 and weekDayOfFirstOfMonth is Thursday) then
		return defaultNumberOfWorkingDays - 1
	else if (numberOfDaysInCurrentMonth = 30 and weekDayOfFirstOfMonth is Saturday) or (numberOfDaysInCurrentMonth = 31 and weekDayOfFirstOfMonth is Friday) then
		return defaultNumberOfWorkingDays - 2
	else
		return defaultNumberOfWorkingDays
	end if
end numberOfWeekdaysInCurrentMonthForDate


That is even better Stefan. :slight_smile:

The only thing amiss here, is the subtraction of hollidays that fall upon workingdays. But that I must postpone, too much to read. Maybe the computer industry is a conspiracy instigated by opticans? :smiley:

Hi.

This is the best I’ve got so far. It seems to be essentially the same as Stefan’s except that it uses the weekday of the last day in the month, since we’ve already calculated that date. I’d like to do something clever with the weekday numbers, but I’m in the middle of a situation at home at the moment.

set dateWithMonth to (current date)

tell dateWithMonth to tell it + (32 - (its day)) * days to set {day:monthLength, weekday:endWeekday} to it - (its day) * days
set nWeekdays to monthLength - 8

if (monthLength is 29) and (endWeekday is Sunday) then
	set nWeekdays to nWeekdays - 1
else if (monthLength is 30) then
	if (endWeekday is Sunday) then
		set nWeekdays to nWeekdays - 2
	else if (endWeekday is Monday) or (endWeekday is Saturday) then
		set nWeekdays to nWeekdays - 1
	end if
else if (monthLength is 31) then
	if (endWeekday is Sunday) or (endWeekday is Monday) then
		set nWeekdays to nWeekdays - 2
	else if (endWeekday is Tuesday) or (endWeekday is Saturday) then
		set nWeekdays to nWeekdays - 1
	end if
end if

return nWeekdays

Hello.

I was actually thinking of something like using numbers before you mentioned it, but too lazy. :slight_smile:

I hope everything is ok. :expressionless:

This is my version, which I have hopefully checked thoroughly enough! I use the day numbers, but skips the sunday, as it complicates the whole algortihm, (as do saturday).

I add the day-number of the first day of the month to the number of days of the month, if the number is greater than 35 I add the difference to the 8 weekend-days, but if the difference is 3 then I subtract one (month starts with a saturday).


set numberOfWeekdays to numberOfWorkingdaysInMonthFor(current date)

on numberOfWorkingdaysInMonthFor(aDate)
	
	-- calculate number of days of aDate
	set numberOfDaysInCurrentMonth to 32 - ((aDate + (32 - (aDate's day)) * days)'s day)
	
	if numberOfDaysInCurrentMonth = 28 then
		set workingdays to 20
	else
		
		tell (aDate - (((aDate's day) - 1) * days)) to set weekDayOfFirstOfMonth to its weekday
		
		if weekDayOfFirstOfMonth is Sunday then
			set workingdays to numberOfDaysInCurrentMonth - 9
		else
			-- thursday friday and saturday, will cause a 5th weekend if the daynumber of the month
			-- is from 29 to 31 days, the case with sunday is handled above.
			set lap to ((weekDayOfFirstOfMonth as integer) + numberOfDaysInCurrentMonth) - 35
			if lap ≥ 1 and lap ≤ 2 then
			else if lap = 3 then
				-- Justifies the calulation, as it may come out wrongly for saturday
				-- since a weekend consists of 2 days at most.
				set lap to 2
			else
				set lap to 0 -- No adjustment needed.
			end if
			set workingdays to numberOfDaysInCurrentMonth - (8 + lap)
		end if
	end if
	return workingdays
end numberOfWorkingdaysInMonthFor

workdaysInMonth(current date)

on workdaysInMonth(dateWithMonth)
	tell dateWithMonth to tell it + (32 - (its day)) * days to set {day:monthLength, weekday:endWeekday} to it - (its day) * days
	if (monthLength is 28) then return 20
	return monthLength - 8 - ((endWeekday < monthLength - 28) as integer) - ((endWeekday mod 7 < monthLength - 27) as integer)
end workdaysInMonth

Nigel’s solution is what I had in mind. There are always 28 days (4 weeks) in a month and there is only an overlap of 1, 2 or 3 days. You only have to consider if that overlap is in the weekend or not. Kudos to Nigel (again)!

brilliant :smiley:

I can just say as Stefan: Absolutely Brilliant Nigel. :slight_smile:

Thanks. :slight_smile: But it was only a matter of compressing the logic from my earlier script. Even less efficiently, it could be rendered thus:

workdaysInMonth(current date)

on workdaysInMonth(dateWithMonth)
	tell dateWithMonth + (32 - (dateWithMonth's day)) * days to tell it - day * days to return day - 8 - (((its weekday) < day - 28) as integer) - (((its weekday) mod 7 < day - 27) as integer) * ((day > 28) as integer)
end workdaysInMonth