Tijoux
Tijoux

Reputation: 25

Timer for variable time delay

I would like a timer (Using Python 3.8 currently) that first checks the system time or Naval Observatory clock, so it can be started at any time and synch with the 00 seconds.

I'm only interested in the number of seconds on the system clock or Naval Observatory. At the top of every minute, i.e when the seconds = 00 I need to write data to a DataFrame or database, then sleep again for another 60 seconds.

I first checked the system time, determined how long it is from the 00 seconds, and placed the first delay for that amount. After that it should delay or sleep for 60 seconds, then run again. Data is constantly changing but at this point I only need to write the data every 60 seconds, would like to also have it have the capability of using other time frames like 5 minutes, 15 minutes etc, but once the first is done the other time frames will be easy.

Here is my lame attempt, it runs a few iterations then quits, and I'm sure it's not very efficient

def time_delay():
    sec = int(time.strftime('%S'))
    if sec != 0:
        wait_time = 60 - sec
        time.sleep(wait_time)
        sec = int(time.strftime('%S'))
        wait_time = 60 - sec
        
    elif time.sleep(60):
        time_delay()    

Upvotes: 1

Views: 312

Answers (2)

martineau
martineau

Reputation: 123453

This isn't quite as general as you wanted, and as I said in a comment, it's not possible to synchronize exactly with the time source due to the inherent overhead involved (but you can get pretty close).

What the following does is define a Thread subclass that can be used to call a user specified function at fixed intervals starting at a whole multiple of seconds on the system clock.

It does this by first delaying until the next multiple of the specified number of seconds will occur, and then from that point on calling the function every interval number of seconds while compensating for however long the call to the specified function took to execute.

from threading import Thread, Event
import time


class TimedCalls(Thread):
    def __init__(self, func, interval, multiple):
        super().__init__()
        self.func = func
        self.interval = interval
        if multiple == 0:
            self.multiple = 60  # Avoid ZeroDivisionError.
        else:
            self.multiple = multiple
        self.stopped = Event()

    def cancel(self):
        self.stopped.set()

    def start(self):
        # Initially sync to a whole multiple of the interval.
        frac = self.multiple  - (time.time() % self.multiple)
        time.sleep(frac)
        super().start()

    def run(self):
        next_call = time.time()
        while not self.stopped.is_set():
            self.func(next_call)  # Target activity.
            next_call = next_call + self.interval
            # Block until beginning of next interval (unless canceled).
            self.stopped.wait(next_call - time.time())


def my_function(tm):
    print(f"{tm} → {time.strftime('%H:%M:%S', time.localtime(tm))}")


# Call function every 10 sec starting at multiple of 5 secs
timed_calls = TimedCalls(my_function, 10, 5)
timed_calls.start()
try:
    time.sleep(125)  # Let the timed calls occur.
finally:
    timed_calls.cancel()
print('done')

Sample output:

1617662925.0018559 → 15:48:45
1617662935.0018559 → 15:48:55
1617662945.0018559 → 15:49:05
1617662955.0018559 → 15:49:15
1617662965.0018559 → 15:49:25
1617662975.0018559 → 15:49:35
1617662985.0018559 → 15:49:45
1617662995.0018559 → 15:49:55
1617663005.0018559 → 15:50:05
1617663015.0018559 → 15:50:15
1617663025.0018559 → 15:50:25
1617663035.0018559 → 15:50:35
1617663045.0018559 → 15:50:45
done

Upvotes: 1

Tim Roberts
Tim Roberts

Reputation: 54698

This will call a function when the seconds are 0:

def time_loop(job):
    while True:
        if int(time.time()) % 60 == 0:
            job()
        time.sleep( 1 )

Upvotes: 1

Related Questions