Delay commands after defined time from initial in python - python

I am trying to delay commands with a delta from the initial time of execution. In other words, I want a start time point that the rest of the commands are delayed in reference to that start time point. Example:
print("start")
# command that delays for a minute since the start
print("it's been a 1 min")
# command that delays for n minute since the start
print("it's been n min")
I am trying to switch from sleep(), but haven't found something that fits what I need. Any assistance is appreciated.

import time
initial_time = int(time.time()) #frame of reference in seconds
def delta_sleep(s):
"""
Parameters:
s: seconds since elapsed to sleep until
"""
if int(time.time()) > initial_time + s:
# check if the delta time has already passed
return
else:
# find time needed to sleep to reach the specified param 's'
needed_sleep = (initial_time+s) - int(time.time())
time.sleep(needed_sleep)
# example
print("The program has started")
delta_sleep(5)
print("Five seconds have passed")
delta_sleep(8)
print("8 seconds have passed")
delta_sleep(1) # does not sleep at all

Related

print time every n seconds using datetime and % operator

How do I print the time every 10 seconds based off of using the % operator and the datetime package? This only prints once...
from datetime import datetime, timezone, timedelta
import time
now_utc = datetime.now(timezone.utc)
while True:
if (now_utc - datetime.now(timezone.utc)).total_seconds() % 10 == 0:
print(time.ctime())
In response to comments on the question: you can do this without datetime and %. The benefit is that it's much simpler.
import time
POLLING_PERIOD = 0.1 # seconds
if __name__ == '__main__':
prev_time = time.time()
print(time.ctime())
while True:
cur_time = time.time()
if cur_time - prev_time >= 10:
prev_time = cur_time
print(time.ctime())
time.sleep(POLLING_PERIOD)
This script gets the seconds from Unix epoch and prints the current time every 10s. You can adjust the polling period to minimize spinning and ensure that the time is printed only every 10s, rather than 11s, 12s, etc. due to poor resolution.
Please note that this script is susceptible to drift due to inaccuracy of time.sleep() and will eventually print a time which is greater than 10s since the last printed time.
After running some experiments on a system with low load, time.sleep() performs very well over several hours if the sleep time is adjusted based on the difference between the previous and current times.
import time
REPORTING_INTERVAL = 10 # seconds
if __name__ == '__main__':
prev_time = time.time()
sleep_time_adj = 0
print(time.ctime())
while True:
time.sleep(REPORTING_INTERVAL - sleep_time_adj)
print(time.ctime())
cur_time = time.time()
sleep_time_adj = cur_time - prev_time - REPORTING_INTERVAL
prev_time = cur_time
It really comes down to how accurate this needs to be, how long it's going to run, and the system it will run on.

Ensure that an arbitrary code takes 15 minutes to run

I need code that will do this:
def foo():
now = datetime.datetime.utcnow()
# Some code will run that takes a variable amount of time
# (less than 15 minutes)
# This code should run no sooner than 15 minutes after `now`
Note that this is not the same as using time.sleep! time.sleep would halt the entire process, but I need computation in foo() to happen and for foo() to return no sooner than 15 minutes after it begins.
You need to calculate the time between the current time and the desired restart time. Then sleep for that amount of time.
wait_time = 15 # minutes
restart_time = datetime.datetime.now() + datetime.timedelta(minutes=wait_time)
# execute code that takes a long time
# for example, let's just sleep for some time
random_time = random.randint(1, wait_time)
time.sleep(random_time * 60)
print("See you again at ", restart_time)
# Now, calculate how long you need to sleep to resume at restart_time
sleep_time = restart_time - datetime.datetime.now()
# Sleep for that amount of time
time.sleep(sleep_time.total_seconds())
print("Hi, I'm back ", datetime.datetime.now())
datetime is not needed, because we do not need to think in human clock terms (hours, minutes, seconds).
All we need is a number of seconds since any fixed moment in the past and time.monotonic does exactly that.
import time
DELAY = 900 # in seconds
start = time.monotonic()
# computation here
end = time.monotonic()
duration = end - start
time.sleep(DELAY - duration)
Last three lines can be written as time.sleep(start + DELAY - time.monotonic()), I split it for simplicity.
import time
import random
import datetime
wait = random.randint(1, 14)
now = datetime.datetime.utcnow()
print(now)
time.sleep(wait * 60)
now = datetime.datetime.utcnow()
print(now)
I think this solves it.

Python 3.7 - How do I execute a loop with a start and stop time using datetime.now().strftime() on the minute?

I'm creating a loop which executes every 5 seconds, starting at the startTime variable and ending at the stopTime variable. However, the code below is disregarding the minutes within my startTime and endTime variables and only executing on the hour. For example, even though my startTime is '1130', the code is executing 11:05, rather than ending the loop. I have the same problem with the endTime variable. If the current time is 12:45, the code still executes even though the endTime variable is '1230'. The code will stop executing at '1300'.
frequency = 5
startTime = '1130'
endTime = '1230'
while True:
now = datetime.now().strftime('%H:%M:%S')
if startTime <= now <= endTime:
print('Loop is working. Time is: ',now)
time.sleep(frequency)
else:
print('Loop is stopped')
break
I live in Central Time, so I tried switching to Eastern timezone by modifying the "now" variable to:
now = datetime.now(timezone('US/Eastern')).strftime('%H:%M:%S.%f %Z')
but I still get the same problem when I substitute eastern times with startTime and endTime when using the eastern datetime.now().
Is executing code at a precise minute possible with strftime()?
EDIT: (this is now the answer to the real question (oops))
If you want to wait till for example 11:30 (which was the real question)
you can calculate the time (in seconds) the program should sleep (and let it sleep for that time):
def wait_till(hour, minute, second=0):
# get system time (and date)
now_time = datetime.datetime.now()
# create time point we are waiting for (this year, this month and this day)
wait_till_time = datetime.datetime(year=now_time.year, month=now_time.month, day=now_time.day, hour=hour, minute=minute, second=second)
# calculate time we want to wait for and convert to seconds
wait_for = (wait_till_time - now_time).total_seconds()
# check if it's going to be tomorrow (if we would sleep for a negative amount of seconds)
if wait_for < 0:
# add one day
wait_till_time = wait_till_time.replace(day=now_time.day+1)
# recalculate (not very beautiful, but i don't know a better way)
wait_for = (wait_till_time - now_time).total_seconds()
# printing this waiting time (in seconds)
print("waiting for",wait_for,"seconds")
# sleeping for that time
time.sleep(wait_for)
# printing the new now time, so we can see how accurate it is
print("its now",datetime.datetime.now())
and say for example:
wait_till(20, 24) # waiting till 20:24 (today)
and get:
waiting for 15.32297 seconds
its now 2019-03-11 20:24:00.003857
which is pretty darn close to what we wanted (20:24:00.000000) and this delay is probably only caused by the calculation lag of formatting the string.
(The old stuff ...)
if it's not important that it takes 100% 5s (but rather 100.04546642303467% --> it will get off a little bit every time) you can just do
import time
frequency = 5 #every 5 seconds
start_time = time.time()
while 1:
elspsed_time = time.time() - start_time
print(elspsed_time)
time.sleep(frequency)
but if you need the 100% you can try this autocorrecting solution:
import time
from threading import Timer
frequency = 5 #every 5 seconds
start_time = time.time()
def what_to_do_after_5s():
elapsed_time = time.time() - start_time
print(elapsed_time)
# next call
Timer(5.0 - (elapsed_time - int(elapsed_time)), what_to_do_after_5s, ()).start()
what_to_do_after_5s()
and we can see that it autocorrects:
0.0
5.000170707702637
10.000272989273071
15.000539064407349
20.001248836517334
25.00046443939209
30.000929355621338
35.00142860412598
40.0007688999176
45.00128436088562
50.00045442581177
55.000683069229126
60.00123882293701
65.00095415115356
70.0015127658844

Sleep till next 15 minute hourly interval (00:00, 00:15, 00:30, 00:45)

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.
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!")
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.
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')
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)
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)

Python, Raspberry pi, call a task every 10 milliseconds precisely

I'm currently trying to have a function called every 10ms to acquire data from a sensor.
Basically I was triggering the callback from a gpio interrupt but I changed my sensor and the one I'm currently using doesn't have a INT pin to drive the callback.
So my goal is to have the same behavior but with an internal interrupt generated by a timer.
I tried this from this topic
import threading
def work ():
threading.Timer(0.25, work).start ()
print(time.time())
print "stackoverflow"
work ()
But when I run it I can see that the timer is not really precise and it's deviating over time as you can see.
1494418413.1584847
stackoverflow
1494418413.1686869
stackoverflow
1494418413.1788757
stackoverflow
1494418413.1890721
stackoverflow
1494418413.1992736
stackoverflow
1494418413.2094712
stackoverflow
1494418413.2196639
stackoverflow
1494418413.2298684
stackoverflow
1494418413.2400634
stackoverflow
1494418413.2502584
stackoverflow
1494418413.2604961
stackoverflow
1494418413.270702
stackoverflow
1494418413.2808678
stackoverflow
1494418413.2910736
stackoverflow
1494418413.301277
stackoverflow
So the timer is deviating by 0.2 milliseconds every 10 milliseconds which is quite a big bias after few seconds.
I know that python is not really made for "real-time" but I think there should be a way to do it.
If someone already have to handle time constraints with python I would be glad to have some advices.
Thanks.
This code works on my laptop - logs the delta between target and actual time - main thing is to minimise what is done in the work() function because e.g. printing and scrolling screen can take a long time.
Key thing is to start the next timer based on difference between the time when that call is made and the target.
I slowed down the interval to 0.1s so it is easier to see the jitter which on my Win7 x64 can exceed 10ms which would cause problems with passing a negative value to thte Timer() call :-o
This logs 100 samples, then prints them - if you redirect to a .csv file you can load into Excel to display graphs.
from multiprocessing import Queue
import threading
import time
# this accumulates record of the difference between the target and actual times
actualdeltas = []
INTERVAL = 0.1
def work(queue, target):
# first thing to do is record the jitter - the difference between target and actual time
actualdeltas.append(time.clock()-target+INTERVAL)
# t0 = time.clock()
# print("Current time\t" + str(time.clock()))
# print("Target\t" + str(target))
# print("Delay\t" + str(target - time.clock()))
# print()
# t0 = time.clock()
if len(actualdeltas) > 100:
# print the accumulated deltas then exit
for d in actualdeltas:
print d
return
threading.Timer(target - time.clock(), work, [queue, target+INTERVAL]).start()
myQueue = Queue()
target = time.clock() + INTERVAL
work(myQueue, target)
Typical output (i.e. don't rely on millisecond timing on Windows in Python):
0.00947008617187
0.0029628920052
0.0121824719378
0.00582923077099
0.00131316206917
0.0105631524709
0.00437298744466
-0.000251418553351
0.00897956530515
0.0028528821332
0.0118192949105
0.00546301269675
0.0145723546788
0.00910063698529
I tried your solution but I got strange results.
Here is my code :
from multiprocessing import Queue
import threading
import time
def work(queue, target):
t0 = time.clock()
print("Target\t" + str(target))
print("Current time\t" + str(t0))
print("Delay\t" + str(target - t0))
print()
threading.Timer(target - t0, work, [queue, target+0.01]).start()
myQueue = Queue()
target = time.clock() + 0.01
work(myQueue, target)
And here is the output
Target 0.054099
Current time 0.044101
Delay 0.009998
Target 0.064099
Current time 0.045622
Delay 0.018477
Target 0.074099
Current time 0.046161
Delay 0.027937999999999998
Target 0.084099
Current time 0.0465
Delay 0.037598999999999994
Target 0.09409899999999999
Current time 0.046877
Delay 0.047221999999999986
Target 0.10409899999999998
Current time 0.047211
Delay 0.05688799999999998
Target 0.11409899999999998
Current time 0.047606
Delay 0.06649299999999997
So we can see that the target is increasing per 10ms and for the first loop, the delay for the timer seems to be good.
The point is instead of starting again at current_time + delay it start again at 0.045622 which represents a delay of 0.001521 instead of 0.01000
Did I missed something? My code seems to follow your logic isn't it?
Working example for #Chupo_cro
Here is my working example
from multiprocessing import Queue
import RPi.GPIO as GPIO
import threading
import time
import os
INTERVAL = 0.01
ledState = True
GPIO.setmode(GPIO.BCM)
GPIO.setup(2, GPIO.OUT, initial=GPIO.LOW)
def work(queue, target):
try:
threading.Timer(target-time.time(), work, [queue, target+INTERVAL]).start()
GPIO.output(2, ledState)
global ledState
ledState = not ledState
except KeyboardInterrupt:
GPIO.cleanup()
try:
myQueue = Queue()
target = time.time() + INTERVAL
work(myQueue, target)
except KeyboardInterrupt:
GPIO.cleanup()

Categories