I am trying to make a script that takes a month in a calendar (“Work”) and sums the total hours of events in it.
Shouldn’t be too hard, but it seems to be beyond me. This is as far as I have gotten.
The problem is that it does not count recurrence of events.
tell application “iCal”
set the source_calendar to calendar "Work"
copy my month_of((current date)) to {start_day, end_day}
set the returned_events to every event of source_calendar whose start date is greater than or equal to start_day and start date is less than or equal to the end_day
set these_events to {}
repeat with i from 1 to the count of the returned_events
set this_item to item i of the returned_events
if the class of this_item is event then
set the end of these_events to this_item
end if
end repeat
set the event_count to the count of these_events
set the event_summary to "TOTAL EVENTS: " & (the event_count as string)
display dialog event_summary buttons {"OK"} --Number of events, does not count recurrences.
end tell
on month_of(this_date)
set the target_month to month of this_date
– get beginning of month
– reset to midnight
set the temp_date to (this_date - (time of this_date))
repeat 31 times
if (the day of temp_date) is 1 then
set the start_day to the temp_date
exit repeat
else
set the temp_date to the temp_date - (1 * days)
end if
end repeat
– get end of month
– reset to midnight
set the temp_date to (this_date - (time of this_date))
repeat 31 times
if (the month of temp_date) is not the target_month then
set the end_day to (the temp_date - 1)
exit repeat
else
set the temp_date to the temp_date + (1 * days)
end if
end repeat
return {start_day, end_day}
end month_of
iCal doesn’t create a new event for every recurrence,
the information ist stored in a property “recurrence” of the event
Here is an example subroutine to extract the parameters of recurrence,
it returns false if there is no recurrence, otherwise max. 4 parameters like this: FREQ=WEEKLY;INTERVAL=2;BYDAY=MO,TU,WE,TH,FR;WKST=MO
that means: every second week on monday thru friday, week starts on monday.
on get_recurr(theEvent)
set {freq, intv, untl, wkst} to {"", "", "", ""}
tell application "iCal" to set rec to recurrence of theEvent
if rec is "" then return false
set oldDelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to {";"}
try
set freq to text item 1 of rec -- frequency
set intv to text item 2 of rec -- interval
set untl to text item 3 of rec -- until
set wkst to text item 4 of rec -- weekday of first day in week
end try
set AppleScript's text item delimiters to oldDelims
return {freq, intv, untl, wkst}
end get_recurr
The subroutine to calculate first and last day of the month can be much faster
using date math of AppleScript:
on month_of(this_date)
set start_date to date (short date string of (current date)) -- midnight of current date
set day of start_date to 1 -- midnight of first day of current month
copy start_date to end_date
set month of end_date to ((month of end_date as integer) + 1) -- midnight of first day of next month
set end_date to end_date - 1 -- 23:59:59 of last day of current month
return {start_date, end_date}
end month_of
on month_of(this_date)
copy this_date to start_date
tell start_date to set {day, time} to {1, 0}
copy start_date to end_date
tell end_date to set {day, day} to {32, 1}
return {start_date, end_date - 1}
end month_of
And even a little faster:
on month_of(this_date)
tell this_date to set start_date to it - (its day) * days + days - (its time)
tell start_date to tell it + 32 * days to set end_date to it - (its day) * days + 86399
return {start_date, end_date}
end month_of
Or an in-between version for kai fans:
on month_of(this_date)
tell this_date to tell it + (32 - day) * days to tell it - day * days - time + 86399 to return {it - day * days + 1, it}
end month_of
on |month range| for d
tell d + 2764800 to tell it - (time + day * days) to {it - days * (day - 1), it + 86399}
end |month range|
|month range| for current date (* or some other date *)
As already demonstrated with this kind of algorithm, a one-liner is unlikely to beat the performance of 2 or 3 similarly optimised lines (even though it can get pretty close). So, for Nigel fans, here’s another variation:
on |month range| for d
tell d to set d to it - (time + (day - 1) * days)
tell d + 2851199 to {d, it - day * days}
end |month range|
|month range| for current date (* or some other date *)