Related
hoping someone helps me out. I have a nested json file and I'm trying to calculate the age difference between two lines of the file, the start_date and end_date with date format of mm/yyyy only. So I'm trying to split it so I can calculate the year difference between end_date and start_date, if over 10 years, I add to another list.
This is my code below, but it prints an empty list and I don't know how to fix it. Any tips or directions will be appreciated
Oh...I have to use default python libraries so even though pandas will be easier, I can't use it.
remove_card=[]
def datebreakdown(data_file):
expr1 = data_file['Credit Card']['start_date']
expr2 = data_file['Credit Card']['end_date']
breakdown1 = expr1.split('/')
breakdown2 = expr2.split('/')
card_month = int(breakdown1[0]) - int(breakdown2[0])
card_year= int(breakdown1[1]) - int(breakdown2[1])
if card_year >= 10:
return True
elif card_year == 10 and card_year > 0:
return True
else:
return False
for line in data_json: #data_json is name of the json file.
if datebreakdown(data_file) == True:
remove_card.append(data_file)
I think these are the conditions you want:
if card_year > 10:
return True
elif card_year == 10 and card_month > 0:
return True
else:
return False
The first condition should be strictly >, not >=. The second condition should compare the months when the year difference is exactly 10.
Another problem is that you're subtracting the dates in the wrong order. You're subtracting the end from the start, so it will always be negative. So those subtractions should be:
card_month = int(breakdown2[1]) - int(breakdown1[0])
card_year= int(breakdown2[1]) - int(breakdown1[1])
def datebreakdown(data_file):
expr1 = data_file['Credit Card']['start_date']
expr2 = data_file['Credit Card']['end_date']
year1, month1 = expr1.split('/')
year2, month2 = expr2.split('/')
start_date = int(year1) + int(month1)/12
end_date = int(year2) + int(month2)/12
return end_date - start_date > 10
DEMO
I am trying to learn Python and I am stuck in a date/time routine I need to find data that occurred between 05:00 and 11:30, but no matter how I go about this I get an error. I would think that I need decode the time, do the math and then encode the time. I am sure it is a simple thing to do but I cannot seem to get it done.
Example:
riders = [
["rider_2391", 37_775370, -122.417571, 37_808601, -122.409807, "17:02:35", "$17.23", "UberX"],
["rider_1879", 37.775222, 122.47109, 37.808080, -122.410002, "06:25:08", "$22.25", "UberX"],
["rider_98233", 37.784125, -122.471891, 37.763025, 122.478749, "11:48:55", "$6.28", "Pool"]
]
def getRiderDate(riders):
ans = []
for rider in riders:
if rider[5] >= "05:00:00" and rider[5] <= "11:30:00":
ans.insert(len(ans), rider)
return ans
print(getRiderDate(riders)
Your problem is faulty loop control:
for rider in riders:
if rider[5] >= "05:00:00" and rider[5] <= "11:30:00":
ans.insert(len(ans), rider)
return ans;
You check one rider and return, regardless off the match. Try this:
def getRiderDate(riders):
ans = []
for rider in riders:
if rider[5] >= "05:00:00" and rider[5] <= "11:30:00":
ans.append(rider)
return ans;
return goes after the loop, not inside.
Output:
[['rider_1879', 37.775222, 122.47109, 37.80808, -122.410002, '06:25:08', '$22.25', 'UberX']]
I would use datetime module, mainly time. Also what previous answers are pointing out (return of ans inside of the loop, etc.)
import datetime
riders = [["rider_2391", 37_775370, -122.417571, 37_808601, -122_409807, "17:02:35", "$17.23", "UberX"],
["rider_1879", 37.775222, 122.47109, 37.808080, -122.410002, "06:25:08", "$22.25", "UberX"],
["rider_98233", 37.784125, -122.471891, 37.763025, 122.478749, "11:48:55", "$6.28", "Pool"]]
def getRiderDate(riders):
ans = []
t1 = datetime.time(hour=5)
t2 = datetime.time(hour=11, minute=30)
for rider in riders:
t = datetime.datetime.strptime(rider[5], '%H:%M:%S').time()
if t1 <= t <= t2: # using chained comparison
ans.insert(len(ans), rider)
return ans
print(getRiderDate(riders))
I'm trying to add n (integer) working days to a given date, the date addition has to avoid the holidays and weekends (it's not included in the working days)
Skipping weekends would be pretty easy doing something like this:
import datetime
def date_by_adding_business_days(from_date, add_days):
business_days_to_add = add_days
current_date = from_date
while business_days_to_add > 0:
current_date += datetime.timedelta(days=1)
weekday = current_date.weekday()
if weekday >= 5: # sunday = 6
continue
business_days_to_add -= 1
return current_date
#demo:
print '10 business days from today:'
print date_by_adding_business_days(datetime.date.today(), 10)
The problem with holidays is that they vary a lot by country or even by region, religion, etc. You would need a list/set of holidays for your use case and then skip them in a similar way. A starting point may be the calendar feed that Apple publishes for iCal (in the ics format), the one for the US would be http://files.apple.com/calendars/US32Holidays.ics
You could use the icalendar module to parse this.
If you don't mind using a 3rd party library then dateutil is handy
from dateutil.rrule import *
print "In 4 business days, it's", rrule(DAILY, byweekday=(MO,TU,WE,TH,FR))[4]
You can also look at rruleset and using .exdate() to provide the holidays to skip those in the calculation, and optionally there's a cache option to avoid re-calculating that might be worth looking in to.
There is no real shortcut to do this. Try this approach:
Create a class which has a method skip(self, d) which returns True for dates that should be skipped.
Create a dictionary in the class which contains all holidays as date objects. Don't use datetime or similar because the fractions of a day will kill you.
Return True for any date that is in the dictionary or d.weekday() >= 5
To add N days, use this method:
def advance(d, days):
delta = datetime.timedelta(1)
for x in range(days):
d = d + delta
while holidayHelper.skip(d):
d = d + delta
return d
Thanks based on omz code i made some little changes ...it maybe helpful for other users:
import datetime
def date_by_adding_business_days(from_date, add_days,holidays):
business_days_to_add = add_days
current_date = from_date
while business_days_to_add > 0:
current_date += datetime.timedelta(days=1)
weekday = current_date.weekday()
if weekday >= 5: # sunday = 6
continue
if current_date in holidays:
continue
business_days_to_add -= 1
return current_date
#demo:
Holidays =[datetime.datetime(2012,10,3),datetime.datetime(2012,10,4)]
print date_by_adding_business_days(datetime.datetime(2012,10,2), 10,Holidays)
I wanted a solution that wasn't O(N) and it looked like a fun bit of code golf. Here's what I banged out in case anyone's interested. Works for positive and negative numbers. Let me know if I missed anything.
def add_business_days(d, business_days_to_add):
num_whole_weeks = business_days_to_add / 5
extra_days = num_whole_weeks * 2
first_weekday = d.weekday()
remainder_days = business_days_to_add % 5
natural_day = first_weekday + remainder_days
if natural_day > 4:
if first_weekday == 5:
extra_days += 1
elif first_weekday != 6:
extra_days += 2
return d + timedelta(business_days_to_add + extra_days)
I know it does not handle holidays, but I found this solution more helpful because it is constant in time. It consists of counting the number of whole weeks, adding holidays is a little more complex. I hope it can help somebody :)
def add_days(days):
today = datetime.date.today()
weekday = today.weekday() + ceil(days)
complete_weeks = weekday // 7
added_days = weekday + complete_weeks * 2
return today + datetime.timedelta(days=added_days)
This will take some work since there isn't any defined construct for holidays in any library (by my knowledge at least). You will need to create your own enumeration of those.
Checking for weekend days is done easily by calling .weekday() < 6 on your datetime object.
Refactoring omz code, and using holidays package, this is what I use to add business days taking into account the country's holidays
import datetime
import holidays
def today_is_holiday(date):
isHoliday = date.date() in [key for key in holidays.EN(years = date.year).keys()]
isWeekend = date.weekday() >= 5
return isWeekend or isHoliday
def date_by_adding_business_days(from_date, add_days):
business_days_to_add = add_days
current_date = from_date
while business_days_to_add > 0:
current_date += datetime.timedelta(days=1)
if today_is_holiday(current_date):
continue
business_days_to_add -= 1
return current_date
Hope this helps. It's not O(N) but O(holidays). Also, holidays only works when the offset is positive.
def add_working_days(start, working_days, holidays=()):
"""
Add working_days to start start date , skipping weekends and holidays.
:param start: the date to start from
:type start: datetime.datetime|datetime.date
:param working_days: offset in working days you want to add (can be negative)
:type working_days: int
:param holidays: iterator of datetime.datetime of datetime.date instances
:type holidays: iter(datetime.date|datetime.datetime)
:return: the new date wroking_days date from now
:rtype: datetime.datetime
:raise:
ValueError if working_days < 0 and holidays
"""
assert isinstance(start, (datetime.date, datetime.datetime)), 'start should be a datetime instance'
assert isinstance(working_days, int)
if working_days < 0 and holidays:
raise ValueError('Holidays and a negative offset is not implemented. ')
if working_days == 0:
return start
# first just add the days
new_date = start + datetime.timedelta(working_days)
# now compensate for the weekends.
# the days is 2 times plus the amount of weeks are included in the offset added to the day of the week
# from the start. This compensates for adding 1 to a friday because 4+1 // 5 = 1
new_date += datetime.timedelta(2 * ((working_days + start.weekday()) // 5))
# now compensate for the holidays
# process only the relevant dates so order the list and abort the handling when the holiday is no longer
# relevant. Check each holiday not being in a weekend, otherwise we don't mind because we skip them anyway
# next, if a holiday is found, just add 1 to the date, using the add_working_days function to compensate for
# weekends. Don't pass the holiday to avoid recursion more then 1 call deep.
for hday in sorted(holidays):
if hday < start:
# ignore holidays before start, we don't care
continue
if hday.weekday() > 4:
# skip holidays in weekends
continue
if hday <= new_date:
# only work with holidays up to and including the current new_date.
# increment using recursion to compensate for weekends
new_date = add_working_days(new_date, 1)
else:
break
return new_date
If someone needs to add/substract days, extending #omz's answer:
def add_business_days(from_date, ndays):
business_days_to_add = abs(ndays)
current_date = from_date
sign = ndays/abs(ndays)
while business_days_to_add > 0:
current_date += datetime.timedelta(sign * 1)
weekday = current_date.weekday()
if weekday >= 5: # sunday = 6
continue
business_days_to_add -= 1
return current_date
similar to #omz solution but recursively:
def add_days_skipping_weekends(start_date, days):
if not days:
return start_date
start_date += timedelta(days=1)
if start_date.weekday() < 5:
days -= 1
return add_days_skipping_weekends(start_date, days)
If you are interested in using NumPy, then you can follow the solution below:
import numpy as np
from datetime import datetime, timedelta
def get_future_date_excluding_weekends(date,no_of_days):
"""This methods return future date by adding given number of days excluding
weekends"""
future_date = date + timedelta(no_of_days)
no_of_busy_days = int(np.busday_count(date.date(),future_date.date()))
if no_of_busy_days != no_of_days:
extend_future_date_by = no_of_days - no_of_busy_days
future_date = future_date + timedelta(extend_future_date_by)
return future_date
This is the best solution because it has O(1) complexity (no loop) and no 3-rd party, but it does not take into account the holidays:
def add_working_days_to_date(self, start_date, days_to_add):
from datetime import timedelta
start_weekday = start_date.weekday()
# first week
total_days = start_weekday + days_to_add
if total_days < 5:
return start_date + timedelta(days=total_days)
else:
# first week
total_days = 7 - start_weekday
days_to_add -= 5 - start_weekday
# middle whole weeks
whole_weeks = days_to_add // 5
remaining_days = days_to_add % 5
total_days += whole_weeks * 7
days_to_add -= whole_weeks * 5
# last week
total_days += remaining_days
return start_date + timedelta(days=total_days)
Even though this does not fully solves your problem, I wanted to let it here because the solutions found on the internet for adding working days to dates, all of them have O(n) complexity.
Keep in mind that, if you want to add 500 days to a date, you will go through a loop and make the same set of computations 500 times. The above approach operates in the same amount of time, no matter how many days you have.
This was heavily tested.
Use numpy (you can skip holidays too):
np.busday_offset(
np.datetime64('2022-12-08'),
offsets=range(12),
roll='following',
weekmask="1111100",
holidays=[])
Result:
array(['2022-12-08', '2022-12-09', '2022-12-12', '2022-12-13',
'2022-12-14', '2022-12-15', '2022-12-16', '2022-12-19',
'2022-12-20', '2022-12-21', '2022-12-22', '2022-12-23'],
dtype='datetime64[D]')
I am using following code to handle business date delta. For holidays, you need to create your own list to skip.
today = datetime.now()
t_1 = today - BDay(1)
t_5 = today - BDay(5)
t_1_str = datetime.strftime(t_1,"%Y%m%d")
Welp, I'm a noob when it comes to Python. No doubt about it. Have done some VBS and VB so I have a bit of understanding.
What I am tasked to do using Python seems easy: run an action only during these times:
Mon: between 1:30 am and 7:30 am
Tues – Fri: between 3:00 am 7:30 am
Sat: between 1:00 am and 9:00 am and 5:00 pm to Midnight
Sun: Midnight to 8:30 am
Trouble is, all I've been able to come up with is this (and I'm not even sure this is working properly):
import time
def IsOffHour(time):
if (time.tm_wday > 4):
return True
elif (time.tm_hour >= 17):
return True
elif (time.tm_hour < 8):
return True
else:
return False
now = time.localtime()
if IsOffHour(now):
print 'hello cruel world !'
I'm not sure how to handle the times that start at :30. It's been a bit hard to test, maybe I can change the system date and dime to test it out.
It seems like I'm close, open to ideas.
Thanks!
Instead of using the time module you should try the datetime module. It's much easier for tasks like these.
If you use a fictional date (or replace the date in your checks) than you can do it like this:
>>> x = datetime.datetime(1, 1, 1, 13, 37, 40)
>>> a = datetime.datetime(1, 1, 1, 1, 30, 0)
>>> b = datetime.datetime(1, 1, 1, 7, 30, 0)
>>> a < x < b
False
>>> x = datetime.datetime(1, 1, 1, 5, 0, 0)
>>> a < x < b
True
My ideas:
do check for each day separately (if time_wday == ... or if time_wday in [...])
for checking hours convert them to 24h based string (there is strftime()) and then compare as strings, so instead of time.tm_hour >= .. this will look as hrstr > '13:30' and hrstr < '19:30'
this gives code like:
def IsOffHour(dt):
hrstr = '%02d:%02d' % (dt.tm_hour, dt.tm_min)
if dt.tm_wday == 0:
return '01:30' <= hrstr <= '07:30'
if dt.tm_wday in [1, 2, 3, 4]:
return '03:00' <= hrstr <= '07:30'
if dt.tm_wday == 5:
return '01:00' <= hrstr <= '09:00' or hrstr >= '17:00'
if dt.tm_wday == 6:
return hrstr <= '08:30'
return False
What you should be doing is comparing time objects to time objects rather than extracting the hours and minutes and doing this by hand.
So define the acceptable time windows in your script using time objects and then just see if the current time falls in any of the those windows.
from datetime import datetime,time
# Set our allowed time windows in a dictionay indexed by day, with 0 =
# Monday, 1 = Tuesday etc. Each value is list of tuples, the tuple
# containing the start and end time of each window in that day
off_windows = {
0: [(time(1,30),time(7,30))],
1: [(time(3,0),time(7,30))],
2: [(time(3,0),time(7,30))],
3: [(time(3,0),time(7,30))],
4: [(time(3,0),time(7,30))],
5: [(time(1,0),time(9,0)),(time(16,0),time.max)], #time.max is just before midnight
6: [(time(0,0),time(8,30))]
}
def is_off_hours():
# Get current datetime
current = datetime.now()
# get day of week and time
current_time = current.time()
current_day = current.weekday()
# see if the time falls in any of the windows for today
return any(start <= current_time <= end for (start,end) in off_windows[current_day])
if is_off_hours():
print 'Hello cruel world!'
Above we use the any function which returns True if any value of an iterable is True. So he code loops through the off windows which we have defined for a day, returning true if the current time falls within any of them.
A nice this about python is we can say:
start <= current_time <= end
instead of
start <= current_time and current_time <= end
at the start and end of my program, I have
from time import strftime
print int(strftime("%Y-%m-%d %H:%M:%S")
Y1=int(strftime("%Y"))
m1=int(strftime("%m"))
d1=int(strftime("%d"))
H1=int(strftime("%H"))
M1=int(strftime("%M"))
S1=int(strftime("%S"))
Y2=int(strftime("%Y"))
m2=int(strftime("%m"))
d2=int(strftime("%d"))
H2=int(strftime("%H"))
M2=int(strftime("%M"))
S2=int(strftime("%S"))
print "Difference is:"+str(Y2-Y1)+":"+str(m2-m1)+":"+str(d2-d1)\
+" "+str(H2-H1)+":"+str(M2-M1)+":"+str(S2-S1)
But when I tried to get the difference, I get syntax errors.... I am doing a few things wrong, but I'm not sure what is going on...
Basically, I just want to store a time in a variable at the start of my program, then store a 2nd time in a second variable near the end, then at the last bit of the program, compute the difference and display it. I am not trying to time a function speed. I am trying to log how long it took for a user to progress through some menus. What is the best way to do this?
The datetime module will do all the work for you:
>>> import datetime
>>> a = datetime.datetime.now()
>>> # ...wait a while...
>>> b = datetime.datetime.now()
>>> print(b-a)
0:03:43.984000
If you don't want to display the microseconds, just use (as gnibbler suggested):
>>> a = datetime.datetime.now().replace(microsecond=0)
>>> b = datetime.datetime.now().replace(microsecond=0)
>>> print(b-a)
0:03:43
from time import time
start_time = time()
...
end_time = time()
seconds_elapsed = end_time - start_time
hours, rest = divmod(seconds_elapsed, 3600)
minutes, seconds = divmod(rest, 60)
You cannot calculate the differences separately ... what difference would that yield for 7:59 and 8:00 o'clock? Try
import time
time.time()
which gives you the seconds since the start of the epoch.
You can then get the intermediate time with something like
timestamp1 = time.time()
# Your code here
timestamp2 = time.time()
print "This took %.2f seconds" % (timestamp2 - timestamp1)
Both time.monotonic() and time.monotonic_ns() are correct. Correct as in monotonic.
>>> import time
>>>
>>> time.monotonic()
452782.067158593
>>>
>>> t0 = time.monotonic()
>>> time.sleep(1)
>>> t1 = time.monotonic()
>>> print(t1 - t0)
1.001658110995777
Regardless of language, monotonic time is the right answer, and real time is the wrong answer. The difference is that monotonic time is supposed to give a consistent answer when measuring durations, while real time isn't, as real time may be adjusted – indeed needs to be adjusted – to keep up with reality. Monotonic time is usually the computer's uptime.
As such, time.time() and datetime.now() are wrong ways to do this.
Python also has time.perf_counter() and time.perf_counter_ns(), which are specified to have the highest available resolution, but aren't guarranteed to be monotonic. On PC hardware, though, both typically have nanosecond resolution.
Here is a piece of code to do so:
def(StringChallenge(str1)):
#str1 = str1[1:-1]
h1 = 0
h2 = 0
m1 = 0
m2 = 0
def time_dif(h1,m1,h2,m2):
if(h1 == h2):
return m2-m1
else:
return ((h2-h1-1)*60 + (60-m1) + m2)
count_min = 0
if str1[1] == ':':
h1=int(str1[:1])
m1=int(str1[2:4])
else:
h1=int(str1[:2])
m1=int(str1[3:5])
if str1[-7] == '-':
h2=int(str1[-6])
m2=int(str1[-4:-2])
else:
h2=int(str1[-7:-5])
m2=int(str1[-4:-2])
if h1 == 12:
h1 = 0
if h2 == 12:
h2 = 0
if "am" in str1[:8]:
flag1 = 0
else:
flag1= 1
if "am" in str1[7:]:
flag2 = 0
else:
flag2 = 1
if flag1 == flag2:
if h2 > h1 or (h2 == h1 and m2 >= m1):
count_min += time_dif(h1,m1,h2,m2)
else:
count_min += 1440 - time_dif(h2,m2,h1,m1)
else:
count_min += (12-h1-1)*60
count_min += (60 - m1)
count_min += (h2*60)+m2
return count_min