A date manipulation problem (monthly events)

I’ve recently put this together (borrowing heavily from Nigel Garvey’s DateTips in ScriptBuilders.net) to calculate the dates of monthly events that always occur on the same day of the week approximately one month apart. Seems a little coarse to me, though it works. Any suggestions for streamlining it?

set tDates to {}
set Now to (current date) -- or any date given.
set WD to weekday of Now
repeat with k from 1 to 5
	set newDate to addMonths(Now, k)
	set end of tDates to getNearestWkDay(newDate, WD)
end repeat

to addMonths(oldDate, m) -- returns a date
	-- adapted from a script by Nigel Garvey (N.G.)
	copy oldDate to newDate
	-- Convert the month-offset parameter to years and months
	set {y, m} to {m div 12, m mod 12}
	-- Add the year offset into the new date's year
	set newDate's year to (newDate's year) + y
	-- Add the odd months (at 32 days per month) and set the day
	if m is not 0 then tell newDate to set {day, day} to {32 * m, day}
	-- If the day's now wrong, it doesn't exist in the target month
	-- Subtract the overflow into the following month to return to the last day of the target month
	if newDate's day is not oldDate's day then set newDate to newDate - (newDate's day) * days
	return newDate
end addMonths

to getNearestWkDay(aDate, tWeekDay)
	-- get the day of the date given (used to check later)
	set tDay to day of aDate
	-- get the days in the month so we won't run over to the nearest day
	set DaysInMo to 32 - ((aDate + (32 - (aDate's day)) * days)'s day) -- N.G. again
	set OffBy to (tWeekDay as number) - (weekday of aDate)
	-- go back or forward for closest day of the week
	if OffBy < -3 then
		set OffBy to OffBy + 7
	else if OffBy > 3 then
		set OffBy to OffBy - 7
	end if
	-- check that offset doesn't move into another month
	-- don't jump forward into next month to nearest weekday
	if tDay + OffBy > DaysInMo then set OffBy to OffBy - 7
	-- don't jump backward into former month to nearest weekday
	if tDay + OffBy < 1 then set OffBy to OffBy + 7
	set newDate to aDate + OffBy * days
	return newDate
end getNearestWkDay

are you trying to get the first (Friday) of the month kind of thing ?

mm

Hi, Adam.

Here’s a very slightly more economical version. I think it’s OK…

set tDates to {}
set Now to (current date) -- or any date given.
repeat with k from 1 to 12
	set newDate to addMonths(Now, k)
	set end of tDates to getNearestWkDay(newDate, Now)
end repeat
tDates

to addMonths(oldDate, m) -- returns a date
	-- adapted from a script by Nigel Garvey (N.G.)
	copy oldDate to newDate
	-- Convert the month-offset parameter to years and months
	set {y, m} to {m div 12, m mod 12}
	-- Add the year offset into the new date's year
	set newDate's year to (newDate's year) + y
	-- Add the odd months (at 32 days per month) and set the day
	if m is not 0 then tell newDate to set {day, day} to {32 * m, day}
	-- If the day's now wrong, it doesn't exist in the target month
	-- Subtract the overflow into the following month to return to the last day of the target month
	if newDate's day is not oldDate's day then set newDate to newDate - (newDate's day) * days
	return newDate
end addMonths

on getNearestWkDay(aDate, refDate)
	-- Get the nearest date to aDate that has the same weekday as refDate.
	set newDate to refDate + quantise(aDate - refDate, weeks)
	-- If it's not in the same month, get the second-nearest such date!
	if (newDate's month is aDate's month) then
		return newDate
	else if (newDate comes before aDate) then
		return newDate + weeks
	else
		return newDate - weeks
	end if
end getNearestWkDay

(* Round to nearest multiple of q (q/2 away from zero) *)
-- from aRounderRound by NG, ScriptBuilders.
on quantise(n, q)
	(n * 2 div q - n div q) * q
end quantise

Very much better Mr. G! :slight_smile: It was indeed all that to-ing and fro-ing to test for the nearest correct weekday without falling back into or leaping forward into a different month that needed some punch. Quantise [sic] with your simpler test following does it [sic, because I would have called it quantize]. Now, I’ve only to sort out what quantise is actually doing. :rolleyes: I’ll grab aRounderRound which I had not seen before (perceived it to be pre-X only) and have a squint at it. [Edit: I’ve altered the system designation on aRounderRound to 9/X from 9)

Thanks,

Adam