so here is my problem, I am trying to do a little program that gives the user the next date when he will have to pay rent.
Here is my code:
curdate = datetime.date(2015, 01, 01)
rent_date = datetime.date(curdate.year, curdate.month+1, 01)
one_day = datetime.timedelta(days = 1)
one_week = datetime.timedelta(weeks = 1)
one_month = datetime.timedelta(weeks = 4)
def rent_date_calc(cd, rd):
if cd.month == 12:
rd.replace(cd.year+1, 01, 01)
else:
rd.replace(cd.year, cd.month+1, 01)
def time_pass(rd, cd, a, al):
if rd < cd:
for a in al:
a.finances -= a.rent
move_fwd = raw_input("Would you like to move forward one day(1), one week (2) or one month (3)?")
if move_fwd == "1":
curdate += one_day
elif move_fwd == "2":
curdate += one_week
else:
curdate += one_month
time_pass(rent_date, curdate, prodcomp, prodcomps)
rent_date_calc(curdate, rent_date)
print "Rent date: " + str(rent_date)
The problem I have is that rent_date always stays the same (2015-02-01)
Any idea why?
Your code is not altering anything because datetime is an immutable object, and when you call replace on it, it returns a new datetime, instead of modifying the first one.
You should return the new object from the function and assign it back to rent_date:
def rent_date_calc(cd, rd):
if cd.month == 12:
return rd.replace(cd.year+1, 01, 01)
else:
return rd.replace(cd.year, cd.month+1, 01)
...
rent_date = rent_date_calc(curdate, rent_date)
Your functions have to return a new rent date. You just need to add the following lines in your code:
return cd
new_rent_date = rent_date_calc(curdate, rent_date)
====================================================================
curdate = datetime.date(2015, 1, 1)
rent_date = datetime.date(curdate.year, curdate.month+1, 1)
one_day = datetime.timedelta(days = 1)
one_week = datetime.timedelta(weeks = 1)
one_month = datetime.timedelta(weeks = 4)
def rent_date_calc(cd, rd):
if cd.month == 12:
new_date = rd.replace(cd.year+1, 1, 1)
else:
new_date = rd.replace(cd.year, cd.month+1, 1)
return new_date
def time_pass(rd, cd, a, al):
if rd < cd:
for a in al:
a.finances -= a.rent
# Not sure what this function should return...
move_fwd = raw_input("Would you like to move forward one day(1), one week (2) or one month (3)?")
if move_fwd == "1":
curdate += one_day
elif move_fwd == "2":
curdate += one_week
else:
curdate += one_month
# time_pass(rent_date, curdate, prodcomp, prodcomps)
new_rent_date = rent_date_calc(curdate, rent_date)
print "Rent date: " + str(new_rent_date)
Related
I am learning python and going through some interactive exercises. Specifically, I'm working on Friday the 13th.
I have rewritten several iterations of this but can never seem to lock it down. With this version, it seems to get hung up when run with the simulated start date of 2025-06-12 which means there's a problem with the "this month" section. Since it returns an accurate Friday the 13th except not 2025-06-13, I suspect it's a problem with the elif statement, particularly the
and date.fromisoformat(current_year + '-' + current_month + '-13').weekday == 4:
Here's the most recent iteration of this.
def friday_the_13th():
from datetime import date
current_year = str(date.today().year)
current_month = str(date.today().month)
if len(current_month) == 1: current_month = '0' + current_month
#Function to increment to the 13th of next month
def NextMonth13(startdate):
lst_date = str(startdate)
lst_date = lst_date.split('-')
month = int(lst_date[1])
if month == 12:
year = str(int(lst_date[0]) + 1)
month = '01'
return str(year + '-' + month + '-' + '13')
else:
year = lst_date[0]
month = str(month + 1)
if len(month) == 1: month = '0' + month
return str(year + '-' + month + '-' + '13')
# Return today if today is Friday the 13th
if date.today().weekday() == 4 and date.today().day == 13:
return date.today()
# Check if this month's 13th is in the future and if it's a Friday
elif date.today().day < 13 and date.fromisoformat(current_year + '-' + current_month + '-13').weekday == 4:
return str(date.fromisoformat(current_year + '-' + current_month + '-13'))
#Check next month and return result if Friday 13
else:
result = NextMonth13(date.today())
while not (date.fromisoformat(result).weekday() == 4):
result = NextMonth13(result)
if date.fromisoformat(result).weekday() == 4:
return result
Would someone mind giving me some guidance on what I might be doing wrong?
First, your error is that you forgot the parenthesis after the weekday method call: date.fromisoformat(current_year + '-' + current_month + '-13').weekday() == 4 (FYI, date.fromisoformat(current_year + '-' + current_month + '-13').weekday returns the memory address of the method, something like this <built-in method weekday of datetime.date object at 0x7fa4e36058f0>. As you can see, it is nowhere near the result you were expecting, so it was normal for your program to behave this way.)
Second, you are needlessly complicating yourself by doing str conversions all the time:
def friday_the_13th():
from datetime import datetime, timedelta
days_passed = 0
today = datetime.today()
while True:
curr = today + timedelta(days=days_passed)
if curr.day == 13 and datetime.weekday(curr) == 4:
return str(datetime.date(curr))
days += 1
This is more readable and less prone to error as you only convert to string at the end, after you've handled all your calculations.
Not sure if it helps but to calculate the future Friday 13th you can do something like:
import datetime
def get_13th_future(startdate, months):
result=[]
year=startdate.year
month=startdate.month
checkdate = datetime.date(year=year, month=month, day=13)
for i in range(month):
if checkdate.weekday()==4:
result.append(checkdate.isoformat())
month+=1
if month==13:
year+=1
month=1
checkdate=datetime.date(year=year,month=month,day=13)
return result
startdate=datetime.datetime.now()
print(get_13th_future(startdate,1000))
If you like to search for a specific date you might construct a set instead of the list.
I'm using tkinter with threading and for a while now, I've had a problem with my threads continuing even though all my windows are closed. The only way I can close the window is in the task manager.
This is my code:
def rootOnClose():
save()
main.deiconify()
pygame.mixer.quit()
root.destroy()
def advancetime():
global datelabelstringvar
global date
global month
global year
global calendarthread
calendarthread = threading.Timer(3.0, advancetime)
calendarthread.setDaemon=(True)
print(calendarthread.isDaemon())
calendarthread.start()
stockchange()
if month == "09" or month == "04" or month == "06" or month == "11":
date = int(date) + 1
if date > 30:
date = 1
month = int(month) + 1
if month > 12:
month = 1
year = int(year) + 1
fixdate()
fixmonth()
datelabelstringvar.set(str(date) + "/" + str(month) + "/" + str(year))
return
if month == "01" or month == "03" or month == "05" or month == "07" or month == "08" or month == "10" or month == "12":
date = int(date) + 1
if date > 31:
date = 1
month = int(month) + 1
if month > 12:
month = 1
year = int(year) + 1
fixdate()
fixmonth()
datelabelstringvar.set(str(date) + "/" + str(month) + "/" + str(year))
return
if month == "02":
date = int(date) + 1
if date > 31:
date = 1
month = int(month) + 1
if month > 12:
month = 1
year = int(year) + 1
fixdate()
fixmonth()
datelabelstringvar.set(str(date) + "/" + str(month) + "/" + str(year))
def root():
root = Toplevel()
root.resizable(width=False, height=False)
main.withdraw()
root.title("Fruit Clicker")
root.geometry("400x350+300+100")
Ive tried adding Daemons but that doesn't seem to work. Ive also tried calendarthread.join() but that freezes my program. Am I doing something wrong?
#tdelaney answered this in the comments thank you so much
I needed to add calendarthread.cancel() in the rootOnClose(): function
This program is intended to ask for the date as dd/mm/yyyy. It should then check to see if the user inputted the date in the correct format (dd/mm/yyyy). My program is not able to recognize the format correctly. This is my program:
date = (input("enter the date as dd/mm/yyyy: "))
date = day, month, year = date.split("/")
if date == (day + '/' + month + '/' + year):
print (date)
if len(day) == 1 or len(day) == 2:
print("1")
if len(month) == 1 or len(month) == 2:
print("2")
if len(year) == 4:
print ("3")
else:
if len(day) == 1 or len(day) == 2:
print("4")
if len(month) == 1 or len(month) == 2:
print("5")
if len(year) == 4:
print ("6")
The numbers being printed currently have no other purpose than to just check the validity of the date. So far, only 4,5, and 6 are being printed, meaning my program is not recognizing the formatting of the date.
Your solution doesn't work because date=day, month, year = date.split("/") sets date to a list, then you're comparing it to a string (day + '/' + month + '/' + year). However, your solution is a solved problem, do instead:
import datetime
date = (input("enter the date as dd/mm/yyyy: "))
try: datetime.datetime.strptime(date,"%d/%m/%Y")
except ValueError: # incorrect format
In addition, you probably are turning this into a datetime object later on anyway, so you can do so in the try block!
As a further optimization, be aware that many users won't WANT to enter their dates using / as a datesep! Do some introspection on your input, and adjust your datesep appropriately.
date = input("enter the date: ")
if "-" in date: datesep = "-"
elif "/" in date: datesep = "/"
elif "." in date: datesep = "."
else: datesep = ""
if len(date) < 6: yeartype = "%y"
elif date[-4:-2] not in ("19","20"): yeartype = "%y"
else: yeartype = "%Y"
try: date = datetime.datetime.strptime(date,"%d{0}%m{0}{1}".format(datesep,yeartype))
except ValueError: # invalid date
Now your code will end up with a valid datetime object of Feb 2nd 2014 for:
02022014
222014
0222014
222014
020214
02214
2214
02-02-2014
02/02/2014
2-2-14
2/2/2014
2/2/14
etc etc etc
You can use the datetime module:
import datetime
def checkdate(date):
try:
datelist = date.split('/')
datetime.datetime(year=int(datelist[2]), month=int(datelist[1]),day=int(datelist[0]))
return True
except:
return False
I'm new to programming and I was trying out this question on Pyschools.
Can anyone help me?
Write a function that converts the time to 24hr format.
Examples
>>> time24hr('12:34am')
'0034hr'
>>> time24hr('12:15pm')
'1215hr'
This question is under conditionals.
Remove the "am" or "pm" from the end and save it off somewhere
Split on the ":", so you have hours and minutes separate
If the time is "am", print the hour unless the hour is "12", in which case print "00"
Otherwise (if the time is "pm"), print the hour + 12 unless the hour is "12", in which case print "12"
Print the minutes
def time24hr(tstr):
time_list = tstr[:-2].split(':')
time_int = [int(x) for x in time_list]
am_pm = tstr[-2:]
hours = time_int[0]
minutes = time_int[1]
if am_pm == 'am':
if hours < 12:
hours = hours + 12
return "%02d%02dhr" % (hours,minutes)
else:
return "00%2dhr" % (minutes)
else:
if hours < 12:
hours = hours + 12
return "%02d%02dhr" % (hours,minutes)
else:
return "%02d%02dhr" % (hours,minutes)
def time24hr(tstr):
newTime = ""
if "am" in tstr:
for i in tstr:
if i not in ":am":
newTime += i
if "12" in newTime[:2]:
newTime = newTime.replace("12", "00")
return newTime + "hr"
elif len(newTime) == 3:
newTime = "0" + newTime + "hr"
return newTime
elif "pm" in tstr:
for i in tstr:
if i not in "pm":
newTime += i
if "10" in newTime[:2]:
newTime = newTime.replace("10", "22")
newTime = newTime.replace(":", "")
elif "11" in newTime[:2]:
newTime = newTime.replace("11", "23")
newTime = newTime.replace(":", "")
elif "12" in newTime[:2]:
newTime = newTime.replace(":", "")
elif "1" in newTime[:1]:
newTime = newTime.replace("1", "13")
newTime = newTime.replace(":", "")
elif "2" in newTime[:1]:
newTime = newTime.replace("2", "14")
newTime = newTime.replace(":", "")
elif "3" in newTime[:1]:
newTime = newTime.replace("3", "15")
newTime = newTime.replace(":", "")
elif "4" in newTime[:1]:
newTime = newTime.replace("4", "16")
newTime = newTime.replace(":", "")
elif "5" in newTime[:1]:
newTime = newTime.replace("5", "17")
newTime = newTime.replace(":", "")
elif "6" in newTime[:1]:
newTime = newTime.replace("6", "18")
newTime = newTime.replace(":", "")
elif "7" in newTime[:1]:
newTime = newTime.replace("7", "19")
newTime = newTime.replace(":", "")
elif "8" in newTime[:1]:
newTime = newTime.replace("8", "20")
newTime = newTime.replace(":", "")
elif "9" in newTime[:1]:
newTime = newTime.replace("9", "21")
newTime = newTime.replace(":", "")
return newTime + "hr"
Comments inline
def time24hr(s):
c = s.split(':') # Split string to list ["hh", "mmAP"] AP is "am" or "pm"
h = int(c[0]) # convert hours string to integer
m = int(c[1][0:-2]) # remove "AP" from "mmAP" and convert to integer. This will handle "mAP" input correctly as well.
h = h % 12 if s[-2:] != 'pm' else 12 if h == 12 else h + 12 # convert hours to 24hr format, handling edge cases.
return "%02d%02dhr" % (h, m) # Print hours and minutes as %02d - two digits, potentially filling with zeroes
This worked for me better. I improved this based on the accepted answer.
Instead of using a loop to convert the list to integers, I call the values from the actual list and declare the variable as int type. Simple!
I still see no reason to add hours + 12 when the time is am and less than 12 hours–12hr and 24hr time format have the same format for am time, an exception of 12 am. Therefore, I just return the initial values from the function for the morning, and when it is exactly 12, I return hours %12 to have 00 am.
def time24hr(tstr):
time_list = tstr[:-2].split(':')
t_list_int = []
for e in time_list:
t_list_int.append(e)
minutes = int(time_list[1])
hours = int(time_list[0])
am_pm = tstr[-2:]
if am_pm == 'am':
if hours == 12:
return "%02d%02dhr" % (hours%12,minutes)
elif hours <12:
return "%02d%02dhr" %(hours, minutes)
else:
if hours == 12:
return "%02d%02dhr" %(hours, minutes)
elif hours <12:
return "%02d%02dhr" %(hours+12, minutes)
print(time24hr('9:34pm'))
print(time24hr('12:00am'))
def time24hr(tstr):
time=tstr.replace(':','')
if 'am' in time:
tim=int(time.replace('am',''))
elif 'pm' in time:
tim=int(time.replace('pm',''))
mini=tim%100
hour=tim//100
if mini<10:
mini='0'+str(mini)
else:
mini=str(mini)
if 'am' in time:
if hour<10:
hour='0'+str(hour)
elif hour==12:
hour='0'+str(hour-12)
time1=time.replace('am','')
time1=str(hour)+str(mini)+'hr'
elif 'pm' in time:
if hour<12:
hour=12+hour
elif hour==12:
hour=hour
time1=time.replace('pm','')
time1=str(hour)+str(mini)+'hr'
return time1
def time24hr(tstr):
a = tstr
b = int(a[:2])+12
if a[-2:] =='am' and a[:2]=='12':
return '\'00'+a[3:-2]+'hr\''
elif a[-2:]=='pm' and a[:2]=='12':
return '\'12'+a[3:-2]+'hr\''
else:
if a[-2:] =='pm':
return "\'"+str(b)+a[3:-2]+'hr\''
else:
return "'"+a[:2]+a[3:-2]+'hr\''
print time24hr('12:15pm')
# '1215hr'
print time24hr('12:15pm')
print time24hr('12:34am')
# '0034hr'
print time24hr('08:34am')
# '0834hr'
print time24hr('08:34pm')
'2034hr'
For me it works (but it isn't too pretty :'C):
def time24hr(tstr):
a=list(tstr)
if len(a)==6:
a.insert(0,'0')
if a[5]=='a' and a[0]=='1' and a[1]=='2':
del a[2]
del a[4:6]
a.append('h')
a.append('r')
a[0]='0'
a[1]='0'
w=''.join(a)
return w
elif a[5]=='p':
del a[2]
del a[4:6]
a.append('h')
a.append('r')
x=a[0]+a[1]
print x
y=int(x)
print y
if y<12:
z=y+12
g=str(z)
a[0:2]=g
w=''.join(a)
return w
else:
w=''.join(a)
return w
else:
del a[2]
del a[4:6]
a.append('h')
a.append('r')
w=''.join(a)
return w
I want to calculate a bonus based on the two consecutive months where sales where the most. So I can iterate a total for every two consecutive months to find the Max value ie get
value = Max[total_between_firstdayMonth1_and_lastDayMonth2, total_between_firstdayMonth2_and_lastDayMonth3, ... , total_between_firstdaySecondToLastMonth_andlastDayLastMonth]
So I might need a list of pairs of datetime objects or something similar.
start= model.Order.order('created').get().created # get the oldest order
end = model.Order.order('-created').get().created # get the newest order
So inbetween start and end I must partition the time in overlapping pairs of consecutive 2 months eg. if first order was in december 2008 and the last order was in november 2011 then the list from where to pick the max should be [total_december2008 + total_january2009, total_january2009 + total_february2009, ... , total_october2011 + total_november2011]
But then how do I get the last day of the second month if I know the start like above? How can I create the list of times and totals?
I might not be able to create the list of totals right away but if I can create the list of starts and ends then I can call a helper function we can assume eg.
total(start_datetime, end_datetime)
Thanks for any help
Update
I think I found how to calculate the time for an example interval where the timeline is from any date to last day next month:
>>> d = date(2007,12,18)
>>> print d
2007-12-18
>>> d + relativedelta(months=2) - timedelta(days=d.day)
datetime.date(2008, 1, 31)
Update 2
I can calculate upto the first level the first duration. Now I only have to generalize it to loop through all the durations and check which was the highest level:
def level(self):
startdate = model.Order.all().filter('status =', 'PAID').filter('distributor_id =' , self._key.id()).get().created.date()
last_day_nextmonth =startdate + relativedelta(months=2) - timedelta(days=1)
if self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth) < 25:
maxlevel = _('New distributor')
elif self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth) > 25:
maxlevel = _('Assistant Teamleader')
return maxlevel
Update 3
Closer to what I mean is taking the max of some function values from beginning up to now. Basecase can be that last day next month is is the future and the helper function can be recursive but I didn't have time or help to make it recursive to it only works for the first 2 periods now ie 4 months from start:
def level(self):
startdate = model.Order.all().filter('status =', 'PAID'
).filter('distributor_id =',
self._key.id()).get().created.date()
last_day_nextmonth = startdate + relativedelta(months=2) \
- timedelta(days=1)
total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)
if total >= 125:
level = 5
elif total >= 75:
level = 4
elif total >= 25:
level = 3
elif total >= 2:
level = 2
else:
level = 1
return self.levelHelp(level, last_day_nextmonth + timedelta(days=1))
def levelHelp(self, level, startdate):
#if startdate in future return level
last_day_nextmonth = startdate + relativedelta(months=2) \
- timedelta(days=1)
total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)
if total >= 125:
newlevel = 5
elif total >= 75:
newlevel = 4
elif total >= 25:
newlevel = 3
elif total >= 2:
newlevel = 2
else:
newlevel = 1
return level if level > newlevel else newlevel
Update 4
I added the recursion where base case is that next step is in the future, if so it will return the max level:
def level(self):
startdate = model.Order.all().filter('status =', 'PAID'
).filter('distributor_id =',
self._key.id()).get().created.date()
last_day_nextmonth = startdate + relativedelta(months=2) \
- timedelta(days=1)
total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)
if total >= 125:
level = 5
elif total >= 75:
level = 4
elif total >= 25:
level = 3
elif total >= 2:
level = 2
else:
level = 1
return self.levelHelp(level, last_day_nextmonth + timedelta(days=1))
def levelHelp(self, level, startdate):
last_day_nextmonth = startdate + relativedelta(months=2) \
- timedelta(days=1)
total = self.personal_silver(startdate, last_day_nextmonth) + self.non_manager_silver(startdate, last_day_nextmonth)
if total >= 125:
newlevel = 5
elif total >= 75:
newlevel = 4
elif total >= 25:
newlevel = 3
elif total >= 2:
newlevel = 2
else:
newlevel = 1
maxlevel = level if level > newlevel else newlevel
nextstart = last_day_nextmonth + timedelta(days=1)
now = datetime.now().date()
if nextstart > now: #next start in is the future
return maxlevel
else: return self.levelHelp(maxlevel, nextstart)
This sounds like a fine job for functional approach. At the end there is a full working example, but I just want to emphasize the elegance and simplicity of the core function, written in FP style:
def find_best_two_months(orders):
first = lambda x: x[0]
second = lambda x: x[1]
orders_by_year_and_month = [
("%04d-%02d" % (date.year, date.month), amount)
for date, amount in orders]
sorted_orders = sorted(orders_by_year_and_month, key=first)
totals_by_month = [
(ym, sum(map(second, groupped_orders)))
for ym, groupped_orders in groupby(sorted_orders, key=first)]
totals_two_months = [
( "%s - %s" % (m1[0], m2[0]), m1[1]+m2[1] )
for m1, m2 in zip(totals_by_month, totals_by_month[1:]) ]
return max(totals_two_months, key=second)
Here is a full working example with comments:
#!/usr/bin/python
from random import randint
from datetime import date, timedelta
from itertools import groupby
""" finding best two months the functional way """
def find_best_two_months(orders):
"""
Expect a list of tuples of form (date_of_order, amount):
[ (date1, amount1), (date2, amount2), ...]
"""
" helper functions for extracting first or second from tuple "
first = lambda x: x[0]
second = lambda x: x[1]
" converts [(date, amount)] -> [(YYYY-MM, amount)] "
orders_by_year_and_month = [ ("%04d-%02d" % (date.year, date.month), amount) for date, amount in orders]
" Sorts by YYYY-MM. This step can be omitted if orders were already sorted by date"
sorted_orders = sorted(orders_by_year_and_month, key=first)
" Compresses orders from the same month, so ve get [(YYYY-MM), total_amount_of_orders]"
totals_by_month = [ (ym, sum(map(lambda x:x[1], groupped_orders)))
for ym, groupped_orders in groupby(sorted_orders, key=first)]
" Zips orders to two month periods"
totals_two_months = [ ("%s - %s" % (m1[0], m2[0]), m1[1]+m2[1]) for m1, m2 in zip(totals_by_month, totals_by_month[1:]) ]
" Returns two-month period with maximum total amount. If there were many periods with max amount, only the first is returned "
return max(totals_two_months, key=second)
"""
this code is for generating random list of orders
and is not a part of the solution
"""
MIN_AMOUNT=70
MAX_AMOUNT=500
MAX_DAY_SPREAD=5
def gen_order(last_date):
""" returns (order_date, amount) """
days = timedelta()
return (
last_date+timedelta(days=randint(0, MAX_DAY_SPREAD)), # new date
randint(MIN_AMOUNT, MAX_AMOUNT)) # amount
def gen_orders(total, start_date):
orders = []
last_date = start_date
for i in range(total):
order = gen_order(last_date)
orders.append(order)
last_date = order[0]
return orders
if __name__ == "__main__":
orders = gen_orders(300, date(2010,1,1))
print find_best_two_months(orders)