3

I need my script to sleep till the next 15 minute hourly interval, e.g. on the hour, quarter past, half past, and quarter too.

It will look something like this

While True:
    //do something
    sleepy_time = //calculate time to next interval
    time.sleep(sleepy_time)

You could write a series of if statements to check what the current minutes past the hour is then do ‘if current < 15’ and ‘if current < 30’ etc but that seems messy and inefficient.

EDIT: Building on @martineau's answer this is the code I went with.

import datetime, time

shouldRun = True
if datetime.datetime.now().minute not in {0, 15, 30, 45}:
    shouldRun = False
# Synchronize with the next quarter hour.
while True:
    if shouldRun == False:
        current_time = datetime.datetime.now()
        seconds = 60 - current_time.second
        minutes = current_time.minute + 1
        snooze = ((15 - minutes%15) * 60) + seconds
        print('minutes:', minutes, 'seconds', seconds, ' sleep({}):'.format(snooze))
        localtime = time.asctime( time.localtime(time.time()))
        print("sleeping at " + localtime)
        time.sleep(snooze)  # Sleep until next quarter hour.
        shouldRun = True
    else:
        localtime = time.asctime( time.localtime(time.time()))
        print("STUFF HAPPENS AT " + localtime)
        shouldRun = False

The difference between his answer and this is that this run the else block only once per interval then if the minute is still on the 0, 15, 30, 45 interval calculates the extra seconds to add to the minutes to sleep till the next interval.

Flatlyn
  • 2,040
  • 6
  • 40
  • 69
  • https://stackoverflow.com/questions/8600161/executing-periodic-actions-in-python – Andrii Muzalevskyi Sep 30 '17 at 19:34
  • 1
    @AndreyMuzalevsky I don’t want it too run every 15 minutes. I want it to run when it is on the hour, quarter past, half past, and quarter. Running every 15 minutes doesn’t work if the script started at say 17:10 because then the next running won’t be till 17:25 rather than 17:15. – Flatlyn Sep 30 '17 at 19:38
  • Will crontab work for you? – Andrii Muzalevskyi Sep 30 '17 at 19:53
  • @AndreyMuzalevsky unfortunately not. The company hosting the server has restricted from jobs so you can’t have them running every 15 minutes. Joe’s answer seems to work – Flatlyn Sep 30 '17 at 19:55
  • If your hosting doesn't provide you ability to run jobs - you may also trap into max-execution-time limit... – Andrii Muzalevskyi Sep 30 '17 at 20:07
  • Also in this conditions, it might be critical if smth goes wrong during the work cycle, it will stop all the script together with scheduler, so I advice you to execute your job in separate thread – Andrii Muzalevskyi Sep 30 '17 at 20:07

5 Answers5

5

You can achieve this using datetime...

A call to datetime.datetime.now() will return a datetime which you can get the current minute past the hour with .minute.

Once we have the number of minutes past the hour, we can do that modulo 15 to get the number of minutes to the next interval of 15.

From here, simply do a call to time.sleep() with that number of minutes times 60 (60 seconds in a minute).

The code for this may look something like:

import datetime, time

minutesToSleep = 15 - datetime.datetime.now().minute % 15
time.sleep(minutesToSleep * 60)
print("time is currently at an interval of 15!")
Joe Iddon
  • 20,101
  • 7
  • 33
  • 54
  • Will that just give an interval of 15 minutes based on the start time of the script or 15 minutes based on the hour. e.g. if the script was started at 17:10 the next interval would be 17:25 rather than 17:15? – Flatlyn Sep 30 '17 at 19:36
  • I don't think this is correct. At 2:02 it will only sleep for 2 minutes. – Philip B. Sep 30 '17 at 19:39
  • Perfect! For some reason I had it in my head you couldn’t do modulus on time figures because they were increments of 60 rather than base 10. I’ll accept it once the timer lets me. – Flatlyn Sep 30 '17 at 19:40
  • 1
    After the edit to `15 - datetime.datetime.now().minute % 15` I agree. – Philip B. Sep 30 '17 at 19:41
  • @PhilipBlondé sorry about that! – Joe Iddon Sep 30 '17 at 19:41
4

time.sleep(15*60 - time.time() % (15*60))

15*60 is a numer of seconds in every 15 mins.

time.time() % (15*60) would be the number of seconds passed in the current 15 min frame (since time 0 is 00:00 by definition). It grows from 0 at XX:00, XX:15, XX:30, XX:45, and up to 15*60-1 (actually, 15*60-0.(0)1 — depends on the precision of time measurements), and then starts to grow from 0 again.

15*60 - time.time() % (15*60) is the number of seconds left till the end of the 15-min frame. It, with a basic math, decreases from 15*60 to 0.

So, you need to sleep that many seconds.

However, keep in mind that sleep will not be very precise. It takes some time to process the internal instructions between time.time() is measured, and time.sleep() is actually called on the system level. Nano-fractions of a second, probably. But in most cases it is acceptable.

Also, keep in mind that time.sleep() does not always sleeps for how long it was asked to sleep. It can be waked up by signals sent to the process (e.g., SIGALRM, SIGUSR1, SIGUSR2, etc). So, besides sleeping, also check that the right time has been reached after time.sleep(), and sleep again if it was not.

Sergey Vasilyev
  • 3,919
  • 3
  • 26
  • 37
1
import time

L = 15*60

while True:
    #do something

    #get current timestamp as an integer and round to the
    #nearest larger or equal multiple of 15*60 seconds, i.e., 15 minutes
    d = int(time.time())

    m = d%L        
    sleepy_time = 0 if m == 0 else (L - m)

    print(sleepy_time)
    time.sleep(sleepy_time)
ewcz
  • 12,819
  • 1
  • 25
  • 47
1

I don't think @Joe Iddon's answer is quite right, although it's close. Try this (note I commented-out lines I didn't want running and added a for loop to test all possible values of minute):

import datetime, time

# Synchronize with the next quarter hour.
#minutes = datetime.datetime.now().minute
for minutes in range(0, 59):
    if minutes not in {0, 15, 30, 45}:
        snooze = 15 - minutes%15
        print('minutes:', minutes, ' sleep({}):'.format(snooze * 60))
        #time.sleep(snooze)  # Sleep until next quarter hour.
    else:
        print('minutes:', minutes, ' no sleep')
martineau
  • 119,623
  • 25
  • 170
  • 301
  • Should the `snooze = 15 - minutes%15` line not be `snooze = (15 - minutes%15) * 60` as at the moment you times it by 60 in the print line but if you uncomment the sleep line to actually sleep it would only sleep by the difference of minutes not the difference in seconds. Other than that I'm not seeing the difference between this an Joe's other than his doesn't have the testing code and his combines the getting of the current minute and the modulus into one operation? – Flatlyn Oct 01 '17 at 02:35
  • jskrwyk: The code in the testing loop isn't optimized for speed. The significant functional difference between @Joe's and mine is what the amount if time spent sleeping when the current time happens to fall exactly on one of the quarter hours. – martineau Oct 01 '17 at 18:44
  • jskrwyk: In the simplest terms. Joe's technique will `sleep()` 15 minutes if the minutes of current time is exactly at `00`, `15`, `30`, or `45` after the hour whereas my version will skip the call to `sleep()` altogether since no is needed. – martineau Oct 03 '17 at 00:51
  • 1
    I've accepted the answer but ultimately I tweaked it as well. I changed it so that it doesn't sleep on the 0, 15, 30, and 45 minute the first time but a few seconds later after it's done the stuff it should at those intervals it will allow the code to run the sleep stuff but calculate the extra seconds to round it off to the 1, 16, 31, 46 minutes. I've added the code I used to the question as an edit. – Flatlyn Oct 05 '17 at 12:03
0
import schedule
import time

# Define a function named "job" to print a message
def job():
    print("Job is running.")

# Set the interval for running the job function to 15 minutes
interval_minutes = 15

# Loop over the range of minutes with a step of interval_minutes
for minute in range(0, 60, interval_minutes):
    # Format the time string to be in the format of "MM:SS"
    time_string = f"{minute:02d}:00" if minute < 60 else "00:00"
    # Schedule the job function to run at the specified time every hour
    schedule.every().hour.at(time_string).do(job)

# Infinite loop to keep checking for any pending job
while True:
    schedule.run_pending()
    # Sleep for 1 second to avoid high CPU usage
    time.sleep(1)
Carlos BL
  • 1
  • 1
  • Hey Carlos, welcome to Stack Overflow! Thank you for your answer. The community would appreciate if in addition to the code you would provide explanation / comments talking more about how it works. – mingaleg Feb 06 '23 at 21:11
  • Users find it difficult to understand code only answers with no explanation. Please add some description explaining what is does and how it solves the problem or add comments in the source code at appropriate places. – Azhar Khan Feb 10 '23 at 08:56