I’m trying to figure out a reliable way to check whether I’m sleeping, ie, whether a time is between a “bedtime” and a “wakeup” time. It needs to work no matter what weird hours you might set as a bedtime and a wakeup time. Here’s what I have so far… It works in most normal conditions, but there are still problems. Anyone up for a little puzzle who can help me figure it out? I think this is pretty close but still seem to be issues.
set cd to current date
set hours of cd to 17
set wakeup to current date
set hours of wakeup to 8
set bedtime to current date
set hours of bedtime to 13
log cd
log bedtime
log wakeup
if (time of cd < time of bedtime and time of cd > time of wakeup) or ((time of bedtime < time of wakeup) and (time of cd > time of wakeup or time of cd < time of bedtime)) then
set msg to "I'm awake..."
else
set msg to "I'm sleeping..."
end if
Please tell us what you mean by: “It works in most normal conditions, but there are still problems.”. For examples: what is “normal”, and second, give details for at least one problem.
adam, i’m not sure what possible combinations might be wrong… like if you go to bed at 2 am and wakeup at 1 am, for instance, might be wrong. but so far what i’ve checked seemed to be right. i guess i was just asking for a logic test here. that or consider it good code and sharable!
kel, dates won’t work because, say for instance bedtime is 9 pm. It’s 10 pm now. if i set it by date, 9 pm would be bedtime 9 pm tomorrow, since i couldn’t obviously be setting it if i was in bed… it’s times, not dates you have to check. current date is a place holder only. but what is “time of current date” anyway?
It seems to me that the best way to do this is to base your times on some previous date (doesn’t matter when - just ago) so that you can set it up to discover whether you’re between two elapsed times and thus “sleeping”. Have you considered that approach? If you need date manipulations, see: Nigel Garvey’s DateTips.
I can’t really see much wrong with the logic, WoodenBrain - although it might be possible to simplify the syntax slightly:
property wakeup : 8 * hours + 0 * minutes (* in case you want to consider minutes *)
property bedtime : 13 * hours + 0 * minutes
tell (current date)'s time to if (it > wakeup and it < bedtime) or wakeup > bedtime and (it > wakeup or it < bedtime) then
set msg to "I'm awake..."
else
set msg to "I'm sleeping..."
end if
Thanks kai. your code is really slick. But it doesn’t work if you keep the bedtime and wakup as dates rather than times. In other words, your properties work with your formulation but not mine (which are stored as dates, even if the actual DAY might be wrong). so to get around this, when sometimes I want the relative date and sometimes I want only the TIME, I do this. maybe you have an idea of how to make it slicker.
--TEST
-- these would be the init variables or set by user
set cd to (current date)
set hours of cd to 8
set minutes of cd to 45
set wakeup to (current date)
set hours of wakeup to 8
set minutes of wakeup to 30
set bedtime to current date
set hours of bedtime to 0
set minutes of bedtime to 30
-- reset actual bedtime and wakeup time based on current date
set bedhours to hours of bedtime
set bedminutes to minutes of bedtime
set wakehours to hours of wakeup
set wakeminutes to minutes of wakeup
set wakeup to (current date)
set hours of wakeup to wakehours
set minutes of wakeup to wakeminutes
set bedtime to current date
set hours of bedtime to bedhours
set minutes of bedtime to bedminutes
-- if it is tomorrow, add a day
set wakeup to (wakeup + ((wakeup < cd) as boolean as integer) * days)
set bedtime to (bedtime + ((bedtime < cd) as boolean as integer) * days)
log cd
log bedtime
log wakeup
-- here we're concerned only with time. ie, we don't care if it's wakeup tomorrow or wakeup today.
if (time of cd < time of bedtime and time of cd > time of wakeup) or ((time of bedtime < time of wakeup) and (time of cd > time of wakeup or time of cd < time of bedtime)) then
set msg to "I'm awake...."
else
set msg to "I'm sleeping..."
end if
-- here it's concerned with relative time
set secs_til_wake to (wakeup - cd)
set secs_til_bed to (bedtime - cd)
set hours_til_bed to secs_til_bed / hours
set hours_til_wake to secs_til_wake / hours
set result to {msg, hours_til_bed, hours_til_wake}
Here’s an example of how you might set the bedtime and wake dates instead of using time:
display dialog “Please enter bedtime:” default answer 13
set bedtime to (text returned of result) as integer
display dialog “Please enter wakeup:” default answer 8
set wakeup to (text returned of result) as integer
if wakeup < bedtime then set wakeup to wakeup + 24
set cd to (current date)
set t to “0”
set last_midnight to date t
set beddate to last_midnight + bedtime * hours
if beddate < cd then set beddate to beddate + 1 * days
set wakedate to beddate + (wakeup - bedtime) * hours
{beddate, wakedate}
Them you can just do the comparison of beaddate < current date < wakedate.
Edit: one more thing. You could also enter the time with minutes like “14:30”. You could coerce this to 2:30 PM with:
set bedtime to “14:30”
set beddate to date bedtime
if beddate < cd then …
Here’s an example of an idle handler using the dates that a user enters in 24 hour format:
property first_run : true
property bedtime : “22:30”
property waketime : “6:30”
global beddate, wakedate, r
on run
try
display dialog “Reset sleep time?”
display dialog “Please enter bedtime:” default answer bedtime
set bedtime to (text returned of result)
display dialog “Please enter wakeup time:” default answer waketime
set waketime to (text returned of result)
end try
set beddate to date bedtime
set wakedate to date waketime
if wakedate < beddate then set wakedate to wakedate + 1 * days
set cd to (current date)
if beddate < cd then
set sleeptime to wakedate - beddate
set beddate to beddate + 1 * days
set wakedate to beddate + sleeptime
end if
set r to 60
end run
on idle
set cd to (current date)
if cd < beddate or cd > wakedate then
set msg to “I’m awake…”
else
set msg to “I’m sleeping…”
end if
try
display dialog msg giving up after 10
on error
set r to 1
quit
end try
return r
end idle
That’s right, WoodenBrain. I avoided the use of dates, simply because I couldn’t really understand why they should be necessary at all. Since the first thing your script does is to convert them to times, would it not be better to store the times instead - and avoid all those coercions at runtime?
If times are input by the user, you could convert them to seconds quite simply:
set t to "08:30" (* simulated user input *)
time of date t
--> 30600
That said, I chose to use a slightly different method in the following suggestion, which adds some input error checking:
property wakeText : "08:30"
property bedText : "00:30"
property wakeup : missing value
property bedtime : missing value
on timeText(t)
tell (current date)
set {its hours, its minutes, its seconds} to t's words & 0
{time, time string's text 1 thru word 2}
end tell
end timeText
to setTimes(t)
tell t to try
set {{wakeup, wakeText}, {bedtime, bedText}} to my {timeText(paragraph 1), timeText(paragraph 2)}
on error
my editTimes()
end try
end setTimes
to editTimes()
setTimes(text returned of (display dialog "Please enter 24-hour clock times for:" & return & return & ¬
"¢ wakeup" & return & "¢ bedtime" default answer wakeText & return & bedText))
end editTimes
to getState(d)
tell d's time to if (it > wakeup and it < bedtime) or wakeup > bedtime and (it > wakeup or it < bedtime) then return "awake"
"asleep"
end getState
if wakeup is missing value then setTimes(wakeText & return & bedText)
repeat
tell (current date) to set {timeNow, myState} to {time string's text 1 thru word 2, "I'm " & my getState(it) & "..."}
display dialog "wakeup:" & tab & tab & wakeText & return & "bedtime:" & tab & tab & bedText & return & "time now:" & ¬
tab & timeNow & return & return & myState buttons {"Edit Times", "Done"} default button 2 cancel button 2
editTimes()
end repeat