# 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! 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