Time Duration in python - python-3.x

Given two strings :
ex:
start_time="3:00 PM"
Duration="3:10"
Start time is in 12-hour clock format (ending in AM or PM), and duration time
that indicates the number of hours and minutes
Assume that the start times are valid times.The minutes in the duration time will
be a whole number less than 60, but the hour can be any whole number.
I need to add the duration time to the start time and return the result
(WITHOUT ANY USE OF LIBRARIES).
The result should be in 12-hour clock format (ending in AM or PM) indicates the
number of hours and minutes
ex:
start_time = "6:30 PM"
Duration = "205:12"
# Returns: 7:42 AM
I Tried and finally got the required answer but unable to produce correct AM or PM for
the result after addition.
what I Tried:
start_time = "6:30 PM"
Duration = "205:12"
#My answer =7:42
#expected :7:42 AM
Can someone help me with the logic to produce correct AM or PM after addition of start
time and Duration.
def add_time(a,b):
a=a.split()
b=b.split()
be=int(a[0][:a[0].find(':')])
af=int(a[0][a[0].find(':')+1:])
be1 = int(b[0][:b[0].find(':')])
af1 = int(b[0][b[0].find(':') + 1:])
return(((be+be1)//24)+1)
s=be+(be1)%12
p=af+af1
if ((s>12) and (p<60)) :
return(str(s-12)+":"+str(p))
elif ((s<12) and (p>60)) :
f = p-60
if len(str(f))<=1:
return(str(s+1)+":"+str('0'+str(f)))
else:
return (str(s + 1)+":"+(str(f)))
elif ((s<12) and (p<60)) :
return(str(s)+":"+str(p))
elif ((s>12) and (p>60)):
f=p-60
if len(str(f)) <= 1:
return (str((s -12)+1)+":"+('0' + str(f)))
else:
return (str((s -12)+1)+":"+(str(f)))
print(add_time("10:10 PM", "3:30"))
# Returns: 1:40 AM
print(add_time("11:43 PM", "24:20"))
# Returns: 12:03 AM

Your code does not seem to cover all edge cases, e.g. add_time("11:43 PM", "1:20") returns None because the case s==12 is not covered.
Therefore one should put <= instead of < in the respective if conditions. The case where the addition of the minutes leads to hours greater than 12 although the addition of the hours itself did not, is not covered either. So we should check the minutes first and the hours after that instead of simultaneously.
To make the code more readable, we use f-strings and can use str.split() with an argument, forgive me for changing the code quite a bit:
def add_time(a,b):
start = a.split()
start_h, start_m = [int(val) for val in start[0].split(':')]
start_app = start[1]
dur_h, dur_m = [int(val) for val in b.split(':')]
end_m = start_m+dur_m
end_h = end_m//60
end_m %= 60
end_h += start_h+dur_h
if (end_h//12)%2==0:
end_app = start_app
else:
end_app = 'AM' if start_app=='PM' else 'PM'
return f'{end_h:02}:{end_m:02} {end_app}'

Related

find an open slot in google calendar api between time

i am struggling to iterate over a time interval in google calendar api and check for an open slot, for example:
i have a time interval from 12:10 pm to 6:30 pm and there are 3 events from 12:10 to 3 pm and 3:10 to 4:25 and 5: 00 to 6:20 pm, on that day and the event i want to check has time duration of 25 minutes and i want to iterate over that time period, and possibly place it between 4:25pm to 5:50pm.
i have tried this but its not correct way:
def set_window_filter(title, windowx1, windowx2, event_duration, calendar, next_day):
schedule_date = None
if (windowx1 is not None) and (windowx2 is not None):
x = datetime.strptime(
windowx1, '%I:%M %p')
y = datetime.strptime(
windowx2, '%I:%M %p')
start_date = datetime.combine(
next_day.date(), x.time())
event_between_date = start_date
event_end_date = event_between_date + timedelta(minutes=event_duration)
end_date = datetime.combine(
next_day.date(), y.time())
while event_end_date <= end_date:
if event_end_date >= end_date - timedelta(minutes=(event_duration)):
return None, False
if event_between_date < event_end_date:
event_value, events = calendar.calendar_event_func(
event_between_date, event_end_date)
print('event_value, events: ', event_value, events)
if event_value is None:
print('event_value: ', event_value)
schedule_date = event_between_date.strftime(
'%A, %d, %B, %Y %I:%M %p')
return schedule_date, False
else:
event_between_date += timedelta(minutes=10)
event_end_date += timedelta(minutes=10)
basically windowx1 and windowx2 are input of time period like 1 PM TO 5 PM
event_value, events = calendar.calendar_event_func(
event_between_date, event_end_date)
this function will return event value(i.e True) and events list if finds any events between the given time period and None and [] if none found,
what could be a better way than this.
This function below will find the earliest available time when a meeting of a desired duration can be scheduled. For example, if the desired window is from 5 to 10, the desired duration is 1 hour and there is an existing meeting from 5:30 to 6:45, the function will return the 6:45 time.
def findFirstOpenSlot(events,startTime,endTime,duration):
def parseDate(rawDate):
#Transform the datetime given by the API to a python datetime object.
return datetime.datetime.strptime(rawDate[:-6]+ rawDate[-6:].replace(":",""), '%Y-%m-%dT%H:%M:%S%z')
eventStarts = [parseDate(e['start'].get('dateTime', e['start'].get('date'))) for e in events]
eventEnds = [parseDate(e['end'].get('dateTime', e['end'].get('date'))) for e in events]
gaps = [start-end for (start,end) in zip(eventStarts[1:], eventEnds[:-1])]
if startTime + duration < eventStarts[0]:
#A slot is open at the start of the desired window.
return startTime
for i, gap in enumerate(gaps):
if gap > duration:
#This means that a gap is bigger than the desired slot duration, and we can "squeeze" a meeting.
#Just after that meeting ends.
return eventEnds[i]
#If no suitable gaps are found, return none.
return None
The function parameters are as follows:
events: a list of raw event objects as obtained via events.get.
startTime , endTime: The start and end of the desired window where the new event should be placed, as a python datetime.
duration: the duration of the new event, as a python timedelta.
The function will return None if it is impossible to schedule the meeting. With that, you can proceed to create the new event with event.insert.

Convert user input to time which changes boolean value for the duration entered?

I'm working on this side project game to grasp python better. I'm trying to have the user enter the amount of time the character has to spend busy, then not allow the user to do the same thing until they have completed the original time entered. I have tried a few methods with varying error results from my noob ways. (timestamps, converting input to int and time in different spots, timeDelta)
def Gold_mining():
while P.notMining:
print('Welcome to the Crystal mines kid.\nYou will be paid in gold for your labour,\nif lucky you may get some skill points or bonus finds...\nGoodluck in there.')
print('How long do you wish to enter for?')
time_mining = int(input("10 Gold Per hour. Max 8 hours --> "))
if time_mining > 0 and time_mining <= 8:
time_started = current_time
print(f'You will spend {time_mining} hours digging in the mines.')
P.gold += time_mining * 10
print(P.gold)
P.notMining = False
End_Time = (current_time + timedelta(hours = 2))
print(f'{End_Time} time you exit the mines...')
elif time_mining > 8:
print("You can't possibly mine for that long kid, go back and think about it.")
else:
print('Invalid')
After the set amount of time i would like for it to change the bool value back to false so that you can mine again.
"Crystal Mining" is mapped to a different key for testing so my output says "Inventory" but would say "Crystal Mining" when it works properly and currently looks like this:
*** Page One ***
Intro Page
02:15:05
1 Character Stats
2 Rename Character
3 Inventory
4 Change Element
5 Menu
6 Exit
Num: 3
Welcome to the Crystal mines kid.
You will be paid in gold for your labour,
if lucky you may get some skill points or bonus finds...
Goodluck in there.
How long do you wish to enter for?
10 Gold Per hour. Max 8 hours --> 1
You will spend 1 hours digging in the mines.
60
Traceback (most recent call last):
File "H:\Python ideas\input_as_always.py", line 176, in <module>
intro.pageInput()
File "H:\Python ideas\input_as_always.py", line 45, in pageInput
self.pageOptions[pInput]['entry']()
File "H:\Python ideas\input_as_always.py", line 134, in Gold_mining
End_Time = (current_time + timedelta(hours = 2))
TypeError: can only concatenate str (not "datetime.timedelta") to str

Execute python script every 15 mins starting from 9:30:42 till 15:00:42 every day from Mon-Fri

I need a way to execute my python script every 15 mins starting from 9:30:42 till 15:00:42 every day from Mon-Fri.
I have explored APScheduler with cron syntax but can't figure out how to code the above condition. I tried below but doesn't work (execute is my function name)
sched.add_cron_job(execute, day_of_week='mon-fri', hour='9:30:42-15:00:42', minute='*/15')
Any pointer is deeply appreciated.
As far as I can tell you won't be able to do what you want with a single job.
This is the closest I could get with one:30-59/15 9-14 * * 1-5 which equates to Every 15 minutes, minutes 30 through 59 past the hour, between 09:00 AM and 02:59 PM, Monday through Friday.
Although it isn't exactly what you wanted I hope this helps as a base.
I wrote custom code to solve my problem. Posting here in case it helps someone. Any optimisations suggestions are welcome.
The first infinite loop starts the job when the start time is hit. The 2nd infinite loop wakes up every x minutes to check if next run time has approached. If yes, it executes else goes back to sleep. If the end time for execution has reached, then it breaks out
def execute_schedule_custom():
start_time_of_day = datetime.combine(date.today(), time(9, 30, 42))
next_run_time = start_time_of_day
end_time_of_day = datetime.combine(date.today(), time(15, 0, 42))
interval = 15
sleep_secs = 60 * 5 #sleep for 5 mins
while True:
if datetime.now() >= start_time_of_day:
execute()
next_run_time = start_time_of_day + timedelta(minutes=interval)
break
while True:
if datetime.now() >= end_time_of_day:
break
elif datetime.now() >= next_run_time:
execute()
next_run_time = next_run_time + timedelta(minutes=interval)
t.sleep(sleep_secs)

Why is time conversion between epoch seconds and string changing time by 1 calendar year?

I am using the time module of python3 to convert time between seconds and formatted string. Python functions used to generate string are localtime and strftime. To generate the time in seconds, I use string splicing followed by mktime. As I call these repeatedly on each result, only the year changes, always incrementing the seconds by a full year.
Code used is as below:
import time
def time_string(t):
#t is second obtained by time.mktime((yr, mn, dy, hr, mn, sec, 0, 0, 0))
time_struct = time.localtime(t)
time_string = time.strftime("%Y-%m-%d %H:%M:%S", time_struct)
return time_string
def string_time(t_string):
#t_string has format '2020-01-31 08:23:35'
yr = int(t_string[:4])
mn = int(t_string[5:7])
dy = int(t_string[8:10])
hr = int(t_string[11:13])
mn = int(t_string[14:16])
se = int(t_string[17:])
t=int(time.mktime((yr, mn, dy, hr, mn, se, 0, 0, 0)))
return t
t = int(time.mktime((2020, 3, 19, 18, 15, 20, 0, 0, 0)))
print (t)
for x in range(5):
t_st = time_string(t)
print(t_st)
t = string_time(t_st)
print(t)
sys.exit("stopping..")
The results I get from above code execution is as follows:
1584621920
2020-03-19 18:15:20
1616157920
2021-03-19 18:15:20
1647693920
2022-03-19 18:15:20
1679229920
2023-03-19 18:15:20
1710852320
2024-03-19 18:15:20
1742388320
SystemExit: stopping..
What am I doing wrong? Why does this happen?
What is a better way of converting time-string to seconds?
I do not get the purpose of the question, so what you're actually trying to do, however if you have a string of a time, and you want to have the seconds of it, try using datetime.timestamp() instead of a time-string-splicing...
Your code is increasing in the year by one beacuse in your method string_time(t_string) you set the variable mn twice! One time at mn = int(t_string[5:7]) and once at mn = int(t_string[14:16]) which will result in a month of 15 which will adapt the year by 1 year and 3 month which will result in the one year for you
Found time.strptime to solve the problem of converting back from string using the right formatters. The following code eliminated the need to do string splicing
def string_time(t_string):
#t_string has format '2020-01-31 08:23:35'
t_struct = time.strptime(t_string,"%Y-%m-%d %H:%M:%S")
t = int(time.mktime(t_struct))
return t
Korbinian had already found the error in my code. Is there a reason why I should use the datetime module instead of the date module?

Remove certain dates in list. Python 3.4

I have a list that has several days in it. Each day have several timestamps. What I want to do is to make a new list that only takes the start time and the end time in the list for each date.
I also want to delete the Character between the date and the time on each one, the char is always the same type of letter.
the time stamps can vary in how many they are on each date.
Since I'm new to python it would be preferred to use a lot of simple to understand codes. I've been using a lot of regex so pleas if there is a way with this one.
the list has been sorted with the command list.sort() so it's in the correct order.
code used to extract the information was the following.
file1 = open("test.txt", "r")
for f in file1:
list1 += re.findall('20\d\d-\d\d-\d\dA\d\d\:\d\d', f)
listX = (len(list1))
list2 = list1[0:listX - 2]
list2.sort()
here is a list of how it looks:
2015-12-28A09:30
2015-12-28A09:30
2015-12-28A09:35
2015-12-28A09:35
2015-12-28A12:00
2015-12-28A12:00
2015-12-28A12:15
2015-12-28A12:15
2015-12-28A14:30
2015-12-28A14:30
2015-12-28A15:15
2015-12-28A15:15
2015-12-28A16:45
2015-12-28A16:45
2015-12-28A17:00
2015-12-28A17:00
2015-12-28A18:15
2015-12-28A18:15
2015-12-29A08:30
2015-12-29A08:30
2015-12-29A08:35
2015-12-29A08:35
2015-12-29A10:45
2015-12-29A10:45
2015-12-29A11:00
2015-12-29A11:00
2015-12-29A13:15
2015-12-29A13:15
2015-12-29A14:00
2015-12-29A14:00
2015-12-29A15:30
2015-12-29A15:30
2015-12-29A15:45
2015-12-29A15:45
2015-12-29A17:15
2015-12-29A17:15
2015-12-30A08:30
2015-12-30A08:30
2015-12-30A08:35
2015-12-30A08:35
2015-12-30A10:45
2015-12-30A10:45
2015-12-30A11:00
2015-12-30A11:00
2015-12-30A13:00
2015-12-30A13:00
2015-12-30A13:45
2015-12-30A13:45
2015-12-30A15:15
2015-12-30A15:15
2015-12-30A15:30
2015-12-30A15:30
2015-12-30A17:15
2015-12-30A17:15
And this is how I want it to look like:
2015-12-28 09:30
2015-12-28 18:15
2015-12-29 08:30
2015-12-29 17:15
2015-12-30 08:30
2015-12-30 17:15
First of all, you should convert all your strings into proper dates, Python can work with. That way, you have a lot more control on it, also to change the formatting later. So let’s parse your dates using datetime.strptime in list2:
from datetime import datetime
dates = [datetime.strptime(item, '%Y-%m-%dA%H:%M') for item in list2]
This creates a new list dates that contains all your dates from list2 but as parsed datetime object.
Now, since you want to get the first and the last date of each day, we somehow have to group your dates by the date component. There are various ways to do that. I’ll be using itertools.groupby for it, with a key function that just looks at the date component of each entry:
from itertools import groupby
for day, times in groupby(dates, lambda x: x.date()):
first, *mid, last = times
print(first)
print(last)
If we run this, we already get your output (without date formatting):
2015-12-28 09:30:00
2015-12-28 18:15:00
2015-12-29 08:30:00
2015-12-29 17:15:00
2015-12-30 08:30:00
2015-12-30 17:15:00
Of course, you can also collect that first and last date in a list first to process the dates later:
filteredDates = []
for day, times in groupby(dates, lambda x: x.date()):
first, *mid, last = times
filteredDates.append(first)
filteredDates.append(last)
And you can also output your dates with a different format using datetime.strftime:
for date in filteredDates:
print(date.strftime('%Y-%m-%d %H:%M'))
That would give us the following output:
2015-12-28 09:30
2015-12-28 18:15
2015-12-29 08:30
2015-12-29 17:15
2015-12-30 08:30
2015-12-30 17:15
If you don’t want to go the route through parsing those dates, of course you could also do this simply by working on the strings. Since they are nicely formatted (i.e. they can be easily compared), you can do that as well. It would look like this then:
for day, times in groupby(list2, lambda x: x[:10]):
first, *mid, last = times
print(first)
print(last)
Producing the following output:
2015-12-28A09:30
2015-12-28A18:15
2015-12-29A08:30
2015-12-29A17:15
2015-12-30A08:30
2015-12-30A17:15
Because your data is ordered you just need to pull the first and last value from each group, you can use re.sub to remove the single letter replacing it with a space then split each date string just comparing the dates:
from re import sub
def grp(l):
it = iter(l)
prev = start = next(it).replace("A"," ")
for dte in it:
dte = dte.replace("A"," ")
# if we have a new date, yield that start and end
if dte.split(None, 1)[0] != prev.split(None,1)[0]:
yield start
yield prev
start = dte
prev = dte
yield start, prev
l=["2015-12-28A09:30", "2015-12-28A09:30", .....................
l[:] = grp(l)
This could also certainly be done as your process the file without sorting by using a dict to group:
from re import findall
from collections import OrderedDict
with open("dates.txt") as f:
od = defaultdict(lambda: {"min": "null", "max": ""})
for line in f:
for dte in findall('20\d\d-\d\d-\d\dA\d\d\:\d\d', line):
dte, tme = dte.split("A")
_dte = "{} {}".format(dte, tme)
if od[dte]["min"] > _dte:
od[dte]["min"] = _dte
if od[dte]["max"] < _dte:
od[dte]["max"] = _dt
print(list(od.values()))
Which will give you the start and end time for each date.
[{'min': '2016-01-03 23:59', 'max': '2016-01-03 23:59'},
{'min': '2015-12-28 00:00', 'max': '2015-12-28 18:15'},
{'min': '2015-12-30 08:30', 'max': '2015-12-30 17:15'},
{'min': '2015-12-29 08:30', 'max': '2015-12-29 17:15'},
{'min': '2015-12-15 08:41', 'max': '2015-12-15 08:41'}]
The start for 2015-12-28 is also 00:00 not 9:30.
if you dates are actually as posted one per line you don't need a regex either:
from collections import defaultdict
with open("dates.txt") as f:
od = defaultdict(lambda: {"min": "null", "max": ""})
for line in f:
dte, tme = line.rstrip().split("A")
_dte = "{} {}".format(dte, tme)
if od[dte]["min"] > _dte:
od[dte]["min"] = _dte
if od[dte]["max"] < _dte:
od[dte]["max"] = _dte
print(list(od.values()
Which would give you the same output.

Resources