Reputation: 6772
I hope this is not outside of the abilities of Python generators, but I'd like to build one so that every time the function is called, it returns the next minute up until the end time.
So the function reads in a start and end time, and returns the time on a minute by minute basis until all the time in between has been covered.
How would this be implemented? TIA
Upvotes: 1
Views: 2906
Reputation: 44142
Here is generator I use for this purpose. It could be enhanced to stop after some time, or you could keep that logic in your calling code, or in some other iterators arround.
import time
def sleep_gen(period):
"""Generator, returning not sooner, then period seconds since last call.
returned value: time of next planned start (no need to use this value)
"""
next_time = 0
while True:
now = time.time()
sleep_time = max(next_time - now, 0)
next_time = now + sleep_time + period
time.sleep(sleep_time)
yield next_time
You could use following code to test the behaviour
import plac
import time
from itertools import count, izip
import random
#sleep_gen to be defined here
@plac.annotations(
period= ("planned period for cycling in seconds (default: %(default)s)", "positional", None, float),
min_duration=("minimal real 'action' duration in seconds (default:%(default)s)", "positional", None, float),
max_duration=("maximal 'real action' duration in seconds (default:%(default)s)", "positional", None, float),
)
def main(period = 1.0, min_duration = 0.1, max_duration = 2.0):
"""Tries to start some randomly long action in regular periods"""
print """call with -h for help.
period : %(period)f
min_duration: %(min_duration)f
max_duration: %(max_duration)f""" % locals()
try:
last_time = now = time.time()
header = ( "%5s|" +"%14s|" +"%14s|" +"%8s|" +"%8s|" +"%14s|") % ("cycle", "last_time", "now", "action", "real", "next_time")
row = "%(i) 5d|%(last_time)14.2f|%(now)14.2f|%(action_dur)8.3f|%(real_dur)8.3f|%(next_time)14.2f|"
print header
action_dur = real_dur = 0.0
for i, next_time in izip(count(), sleep_gen(period)):
#we care about starting the action on time, not ending
now = time.time() #sleep_gen just tried to keep the period on schedule
real_dur = now - last_time
print row % locals()
last_time = now
#performing the "action"
action_dur = random.random() * (max_duration - min_duration) + min_duration
time.sleep(action_dur)
except KeyboardInterrupt:
print "...cancelled."
if __name__ == "__main__":
plac.call(main)
Calling it from command line:
$ python cycle.py
call with -h for help.
period : 1.000000
min_duration: 0.100000
max_duration: 2.000000
cycle| last_time| now| action| real| next_time|
0| 1337368558.55| 1337368558.55| 0.000| 0.002| 1337368559.55|
1| 1337368558.55| 1337368559.59| 1.042| 1.042| 1337368560.59|
2| 1337368559.59| 1337368561.32| 1.722| 1.723| 1337368562.32|
3| 1337368561.32| 1337368562.32| 0.686| 1.000| 1337368563.32|
4| 1337368562.32| 1337368563.32| 0.592| 1.000| 1337368564.32|
5| 1337368563.32| 1337368564.75| 1.439| 1.439| 1337368565.75|
6| 1337368564.75| 1337368566.08| 1.323| 1.323| 1337368567.08|
7| 1337368566.08| 1337368567.08| 0.494| 0.999| 1337368568.08|
8| 1337368567.08| 1337368568.20| 1.120| 1.121| 1337368569.20|
9| 1337368568.20| 1337368569.20| 0.572| 1.000| 1337368570.20|
10| 1337368569.20| 1337368570.20| 0.586| 1.000| 1337368571.20|
11| 1337368570.20| 1337368571.20| 0.309| 0.999| 1337368572.20|
12| 1337368571.20| 1337368572.20| 0.290| 1.000| 1337368573.20|
13| 1337368572.20| 1337368573.25| 1.052| 1.053| 1337368574.25|
14| 1337368573.25| 1337368574.25| 0.737| 1.000| 1337368575.25|
15| 1337368574.25| 1337368575.83| 1.579| 1.579| 1337368576.83|
...cancelled.
Comparing your question with my answer:
Upvotes: 1
Reputation: 19339
Just because fewer lines of code is always better (tm):
def minutes(s, e):
secs = (e - s).seconds
return (s + datetime.timedelta(minutes = x) for x in xrange(secs / 60 + 1))
Use it like this:
>>> today = datetime.datetime(2012, 1, 31, 15, 20)
>>> for m in minutes(today, today + datetime.timedelta(minutes = 5)):
... print m
2012-01-31 15:20:00
2012-01-31 15:21:00
2012-01-31 15:22:00
2012-01-31 15:23:00
2012-01-31 15:24:00
2012-01-31 15:25:00
Upvotes: 2
Reputation: 39933
The datetime module is quite awesome. There are two datatypes you need to know about: datetime
and timedelta
. datetime
is a point in time, while timedelta
is a period of time. Basically, what I'm going to do here is start at a time and end at a time (as a datetime
object), and progressively add 1 minute.
This obviously has the caveat that you have to figure out how to get your start and end time into a datetime
. There are a number of ways to do this: through the constructor, right now, from UTC timestamp, etc.
import datetime
def minute_range(start, end, step=1):
cur = start
while cur < end:
yield cur
cur += datetime.timedelta(minutes=step)
Upvotes: 7