Improvement of an time stamp's matching analysis for an applet

I have designed an applet, that performs several routine tasks:

  1. once a day, on a specific times stored in the Preference file (plist)
  2. every hour, at the exact minute defined in the Preference file (plist).

The format for the point (1) is (as an example): “14:45” (every day, at this time, routing will be launched)
The format for the point (2) is (as an example): “:15” (which means at 00:15, 01:15 etc…)

The information from the preference file is transferrend into the corresponding properties during the applet initialisation (which is called in the on run/end run handler), which I will skip it here for simplicity.

property dailyRoutineTasks : “14:45”
property hourlyRoutineTasks : “:15”

The important part of the code is in idle handler, also for simplicity I am positing only the code that is relevant for monitoring the daily schedule (since it is very similar to the hourly schedule).

Basically, what I have so far is that every idle cycle (= every one second):
a) formatting the current time;
b) comparing the current (formatted) time with that of the stored into dailyRoutineTasks variable;
c) when are equal → call the subroutine (for launching all daily tasks).

But then I realised that for the full minute (in our example is until the time change into 14:46), I will be calling the subroutine (aprox) 60 times!!!
To avoid this I am using a boolean (suspendDailyRoutineTask = true/false).

Is there any better way to achieve this?

Thanks !

This is the code I have so far :

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"

property dailyRoutineTasks : "14:45"
property hourlyRoutineTasks : ":15"
property suspendHourlyRoutineTask : false
property suspendDailyRoutineTask : false


on idle
	set df to current application's NSDateFormatter's new()
	df's setDateFormat:"HH:mm"
	set theDateString to (df's stringFromDate:(current date)) as text
	
	if not suspendDailyRoutineTask then -- initially this is 'false'. After become true (when the times are the same), this is ignored (thus avoiding calling the subroutines 60 times.
		if theDateString = dailyRoutineTasks then -- 14:45
			set suspendDailyRoutineTask to true
			runRoutineTasks()
		end if
	else -- resetting the suspend boolean, which will occurs after the minute change (=60 seconds)
		if ((text 4 thru -1 of theDateString) as integer) > ((text 4 thru -1 of dailyRoutineTasks) as integer) then -- here we are comparing only the minutes of the correpsonding tie stamp. And when 1 minutes is passed the 1st part of the comparison will be bigger than the second, and thus setting the boolean back to false, allowing again the analysis of the block of code above 
			set suspendDailyRoutineTask to false
		end if
	end if
        return 1
end idle

on runRoutineTasks()
	-- task_1
	-- task_2
end runRoutineTasks

I think, you can simplify the script. And, you can set formatter only once, when initialize.


use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions
use framework "Foundation"

property dailyRoutineTasks : "14:45"
property hourlyRoutineTasks : ":15"
property df : missing value

on run
	set df to current application's NSDateFormatter's new()
	df's setDateFormat:"HH:mm"
end run

on idle
	set theDateString to (df's stringFromDate:(current date)) as text
	if theDateString = dailyRoutineTasks then -- 14:45
		runRoutineTasks()
		delay 61 -- adding 61 seconds to the minute of theDateString be changed
	end if
	return 30 -- idle seconds
end idle

on runRoutineTasks()
	-- task_1
	-- task_2
end runRoutineTasks

Thanks Robert for your feedback.
But I cannot add the delay 61, since the same applet perform many other functions every idle cycle.
Thanks anyway for your suggestion.

Hi.

The idle time — a number of seconds — is set by the result returned by the ‘idle’ handler, so the required interval should be returned explicitly, as in KniazidisR’s script above. If the result’s 0 or non-numeric, the default interval is 30 seconds. The interval’s timed from the moment the idle handler exits and may be a little longer than requested if the computer’s busy with other tasks at the time. So you can’t rely on the cycle being regular. If you want the applet to be polled, say, every hour, you have to subtract its execution time by calculating the number of seconds to the next call time and hoping for the best with interval itself.

One solution in your case would be to have the ‘idle’ handler called on the quarter-hour marks in every hour and to perform the relevant task if the actual time’s within a few seconds of what’s required. Obviously you’d need to modify the following according to your circumstances:

global dailyRoutineTasks, hourlyRoutineTasks

on run
	-- Read the plist file and convert the relevant times to seconds. Something like:
	set dailyRoutineTasks to 14 * hours + 45 * minutes -- Time of day.
	set hourlyRoutineTasks to 15 * minutes -- Time of hour.
end run

on idle
	-- Execute if the current time is within a few seconds of the desired execution time(s).
	set now to time of (current date)
	if ((now > dailyRoutineTasks - 15) and (now < dailyRoutineTasks + 15)) then
		task1()
	else
		set now to now mod hours
		if ((now > hourlyRoutineTasks - 15) and (now < hourlyRoutineTasks + 15)) then task2()
	end if
	
	-- Request to be called again on the next quarter hour mark.
	return 15 * minutes - (time of (current date)) mod (15 * minutes)
end idle

on task1()
	-- say "Task 1"
end task1

on task2()
	-- say "Task 2"
end task2

Hi Nigel,

  1. Sorry by mistake. I do have ‘return 1’ in my code. I just forgot to add it in this simplified version. I now fixed the original script in post #1.
  2. As I mentioned before, I cannot change the frequency of the idle cycles, since there are several additional tasks that relay in idle cycling every 1 sec.
  3. I will explore the idea of delegating this specific recurrent tasks on 15 minutes routine. But I am not sure I will gain speed/efficiency.
    Thanks a lot for the suggestion.
    L.