[Tutor] Accuracy of time.sleep()

Dave S pythontut at pusspaws.net
Sat Dec 4 22:15:19 CET 2004


Tim Peters wrote:

First, thank you for such a brilliant answer :-)

>[Dave S <pythontut at pusspaws.net>]
>  
>
>>OK I may be pushing it,  ;-)
>>    
>>
>
>Yup <wink>.
>
>  
>
>>I need a script to sleep from any point to 8:05AM when in needs to
>>re-start.
>>
>>So I calculate the number of seconds with the following ....
>>
>>def secs_till_805():
>>   # Returns the number of seconds till 8:05AM
>>
>>   secs_5min=5*60
>>   secs_24hr=24*60*60
>>   secs_8hr=(8*60*60)+secs_5min
>>   secs_8hr_24hr=secs_24hr-secs_8hr
>>
>>   hours=int(strftime('%H'))
>>   mins=int(strftime('%M'))
>>   secs=int(strftime('%S'))
>>    
>>
>
>Ouch.  Never try to pick apart the current time by computing it more
>than once.  For example, if the time at the start of that block is
>just a fraction of a second before 9AM, it's quite possible you'll end
>up with hours==8 and mins==secs==0 (because the time is 8:59:59 at the
>time you do the "%H" business, and but it's 9:00:00 by the time you
>get to "%M").  That would throw you off by an hour.  The same kind of
>thing can happen a little before the (any) minute changes too.
>
>  
>
This is a possibility that had not enterd my mind, but also very true. 
Thanks for saving me from that particular black hole.

Its always that 1 in a thousand possibility that sends things south at 
the worst possible moment !

>>   sec_left=secs_24hr-((hours*60*60)+(mins*60)+secs)
>>
>>   # If we are before 8:05, then ...
>>   if sec_left>secs_8hr_24hr:
>>       return sec_left-secs_8hr_24hr
>>
>>   # If we are after 8:05, then ...
>>   return sec_left+secs_8hr
>>    
>>
>
>Here's a different way, computing current time only once, and using
>the datetime module to do all the fiddly work:
>
>def seconds_until(h, m=0, s=0):
>    from datetime import datetime, time, timedelta
>
>    target_time = time(h, m, s)
>    now = datetime.now()
>    target = datetime.combine(now.date(), target_time)
>    if target < now:
>        target += timedelta(days=1)
>    diff = target - now
>    return diff.seconds + diff.microseconds / 1e6
>
>This returns seconds as a float, which is good (Python's time.sleep()
>can make sense of floats, and sleep for fractional seconds).
>
>  
>
OK Im pydoc'ing & looking at datetime, a module I have not explored 
before. This is stretching me a bit but its a good way to learn.

>>Then I ...
>>
>>sleep(secs_till_805())
>>    
>>
>
>With the above, you'd do
>
>    time.sleep(seconds_until(8, 5))
>
>instead.
>
>  
>
>>I expected the script to re-start 2-3 seconds after 8:05, python
>>reloading after a long sleep etc, what I get is the script restarting at
>>08:04.55, earlier ???
>>    
>>
>
>You'll probably never know why for sure.  Python calls platform C
>library gimmicks to sleep, which in turn invoke operating system
>facilities.  Understanding the whole story would require that you
>understand everything all of those do.
>
>  
>
If only I had the time ... (no pun intended)

>[later]
>  
>
>>It must be cummulative error over 10s of thousands of seconds.
>>    
>>
>
>Maybe.
>
>  
>
>>Its a bodge (& cron or at are better) but I suppose I could calculate seconds
>>to 8:05 sleep(seconds*0.95), re calculate secs to 8:05 sleep(seconds)
>>which should reduce the error to almost zip.
>>    
>>
>
>That's also a good idea in order to avoid surprises due to crossing
>daylight time boundaries (assuming you mean 8:05 according to the
>local wall clock).  Here's a function building on the above:
>
>def sleep_until(h, m=0, s=0):
>    from time import sleep
>
>    while True:
>        delay = seconds_until(h, m, s)
>        if delay < 10.0:
>            sleep(delay)
>            return
>        else:
>            sleep(delay / 2)
>  
>

Thats neat, and more elegent than my hamfisted attempt, I err might 
borrow it for my code, on a tempory basis you understand ;-)

sleep_secs=secs_till_805()
log('II','ftsed','Sleeping for '+str(sleep_secs)+' Seconds')

# To compensate for the commulative error over 86,000 secs !
sleep(sleep_secs*0.95)
sleep(secs_till_805())

>That is, it cuts the sleep time in half repeatedly, until less than 10
>seconds remain.  It can sleep for hours at a time, but as the target
>time approaches it wakes up more frequently.  This should keep the
>program loaded in memory as the target time gets near.
>
>
>  
>
Cheers
Dave


More information about the Tutor mailing list