[Tutor] Accuracy of time.sleep()

Tim Peters tim.peters at gmail.com
Sat Dec 4 20:17:05 CET 2004


[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.

>    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).

> 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.

[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)

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.


More information about the Tutor mailing list