I am trying to implement a function to calculate the next and 3rd business day from a given date (ideally taking into account some given holidays)
def day_of_week(year, month, day):
t = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4]
year -= month < 3
return (year + int(year/4) - int(year/100) + int(year/400) + t[month-1] + day) % 7
The input is in the format YYYYMMDD with 21th of March 2018 written as 20180321 and the output date should be in the same format.
I`m trying to do something like this but I realised this is not the best practice
def leap_year(year):
if(year%4==0 and year%100!=0):
return True
elif (year%4==0 and year%100==0 and year%400==0):
return True
else:
return False
def business_day(year, month, day):
if (month==12):
if(day_of_week(year, month, day)<5 and day_of_week(year, month, day)>0 and day==31):
return str(year+1)+str(0)+str(1)+str(0)+str(1)
elif (day_of_week(year, month, day)<5 and day_of_week(year, month, day)>0 and day!=31):
newDay="0"
if(day<10):
newDay = newDay + str(day+1)
else:
newDay = str(day+1)
return str(year) + str(month) + newDay
elif (day_of_week(year, month, day)>=5 and day==31):
if(day_of_week(year, month, day)==5):
return str(year+1)+"01"+"03"
if (day_of_week(year, month, day) == 6):
return str(year + 1) + "01" + "02"
if (day_of_week(year, month, day) == 0):
return str(year + 1) + "01" + "01"
elif (day_of_week(year, month, day)>=5 and day==30):
if((day_of_week(year, month, day)==5)):
return str(year + 1) + "01" + "02"
if ((day_of_week(year, month, day) == 6)):
return str(year + 1) + "01" + "01"
if ((day_of_week(year, month, day) == 0)):
return str(year + 1) + str(month) + str(day+1)
Can`t use any libraries in the solution. Thanks for the help
No libraries! I had some fun learning Python. Did you? :-)
def day_of_week(year, month, day):
t = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4]
year -= month < 3
dw = (year + year // 4 - year // 100 + year // 400 + t[month-1] + day) % 7
return [6, 0, 1, 2, 3, 4, 5][dw] # To be consistent with 'datetime' library
def leap_year(year):
leap = year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
return True if leap else False
def valid_day(year, month, day):
month_list = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if year < 1 or year > 9999 or month < 1 or month > 12:
return False
m = month_list[month-1] if month != 2 or not leap_year(year) else 29
return True if 1 <= day <= m else False
class Career(Exception):
def __str__(self): return 'So I became a waiter...'
MAX_DATE_AND_DAYS_INT = 365 * 100
class Date:
# raise ValueError
def __init__(self, year, month, day):
if not valid_day(year, month, day):
raise Career()
self.y, self.m, self.d = year, month, day
#classmethod
def fromstring(cls, s):
s1, s2, s3 = int(s[0:4]), int(s[4:6]), int(s[6:8])
return cls(s1, s2, s3)
def __repr__(self) -> str:
return '%04d%02d%02d' % (self.y, self.m, self.d)
def weekday_date(self) -> str:
names = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
return names[self.weekday()] + ' ' + str(self)
def next_day(self):
if valid_day(self.y, self.m, self.d + 1):
return Date(self.y, self.m, self.d + 1)
elif valid_day(self.y, self.m + 1, 1):
return Date(self.y, self.m + 1, 1)
elif valid_day(self.y + 1, 1, 1):
return Date(self.y + 1, 1, 1)
else:
raise Career
def weekday(self):
return day_of_week(self.y, self.m, self.d)
def __add__(self, other):
"Add a Date to an int."
if isinstance(other, int):
if other < 1 or other > MAX_DATE_AND_DAYS_INT:
raise OverflowError("int > MAX_DATE_AND_DAYS_INT")
new_date = Date(self.y, self.m, self.d)
while other >= 1:
new_date = new_date.next_day()
other -= 1
return new_date
return NotImplemented
def next_working_day(self):
day = self.next_day()
while True:
while day.weekday() >= 5:
day = day.next_day()
holidays_list = year_holidays(day.y)
for str_day in holidays_list:
s2 = str(day)
if str_day == s2:
day = day.next_day()
break # for
if day.weekday() < 5:
break # while True
return day
def year_holidays(year):
holidays = [
["New Year's Day", 1, 1], # Fixed: January 1
["Birthday of Martin Luther King, Jr.", 1, 0, 0, 3], # Floating
["Washington's Birthday", 2, 0, 0, 3], # Third Monday in February
["Memorial Day", 5, 0, 0, 5], # Last Monday
["Independence Day", 7, 4],
["Labor Day", 9, 0, 0, 1],
["Columbus Day", 10, 0, 0, 2],
["Veterans Day", 11, 11],
["Thanksgiving Day", 11, 0, 3, 4],
["Christmas Day", 12, 25]
]
year_list = []
for h in holidays:
if h[2] > 0:
day = Date(year, h[1], h[2]) # Fixed day
else:
day = Date(year, h[1], 1) # Floating day
while h[3] != day.weekday(): # Advance to match the weekday
day = day.next_day()
count = 1
while count != h[4]: # Match the repetition of this day
next_week = day + 7
if next_week.m == day.m:
day = next_week
count += 1
year_list.append(str(day))
return year_list # return the holidays as list of strings
if __name__ == '__main__':
dates = [
['20190308', '20190311', '20190313'],
['20190309', '20190311', '20190313'],
['20190310', '20190311', '20190313'],
['20190311', '20190312', '20190314'],
['20190329', '20190401', '20190403'],
['20181231', '20190102', '20190104'],
['20190118', '20190122', '20190124'],
['20190216', '20190219', '20190221'],
['20190526', '20190528', '20190530'],
['20190703', '20190705', '20190709'],
['20190828', '20190829', '20190903'],
['20191010', '20191011', '20191016'],
['20191108', '20191112', '20191114'],
['20191125', '20191126', '20191129'],
['20191224', '20191226', '20191230'],
['20191227', '20191230', '20200102']]
print('\n Today Next and 3rd business day')
for days in dates:
today = Date.fromstring(days[0])
next_day = today.next_working_day()
third_day = next_day.next_working_day().next_working_day()
if str(next_day) != days[1] or str(third_day) != days[2]:
print('*** ERROR *** ', end='')
else:
print(' ', end='')
print(today.weekday_date(), next_day.weekday_date(), third_day.weekday_date())
Output:
Today Next and 3rd business day
Fri 20190308 Mon 20190311 Wed 20190313
Sat 20190309 Mon 20190311 Wed 20190313
Sun 20190310 Mon 20190311 Wed 20190313
Mon 20190311 Tue 20190312 Thu 20190314
Fri 20190329 Mon 20190401 Wed 20190403
Mon 20181231 Wed 20190102 Fri 20190104
Fri 20190118 Tue 20190122 Thu 20190124
Sat 20190216 Tue 20190219 Thu 20190221
Sun 20190526 Tue 20190528 Thu 20190530
Wed 20190703 Fri 20190705 Tue 20190709
Wed 20190828 Thu 20190829 Tue 20190903
Thu 20191010 Fri 20191011 Wed 20191016
Fri 20191108 Tue 20191112 Thu 20191114
Mon 20191125 Tue 20191126 Fri 20191129
Tue 20191224 Thu 20191226 Mon 20191230
Fri 20191227 Mon 20191230 Thu 20200102
import datetime
example = '20180321'
# you can parse the time string directly to a datetime object
next_buisness_day = datetime.datetime.strptime(example, '%Y%m%d')
# specify the increment based on the day of the week or any
#other condition
increment = 1
print('day day is', next_buisness_day.weekday())
# if friday
if next_buisness_day.weekday() == 4:
increment = 3
# if saturday
elif next_buisness_day.weekday() == 5:
increment = 2
next_buisness_day += datetime.timedelta(days=increment)
# and convert back to whatever format you like
print('{:%Y%m%d}'.format(next_buisness_day))
Have a look at the datetime module you can do all sorts of things with it.
https://docs.python.org/3/library/datetime.html
I use few functions from 'datetime' library. You can have fun to write them: date(y, m, d), timedelta(days=7), day,weekday(), '{:%Y%m%d}'.format(day), strptime(input, '%Y%m%d'), strftime(datetime, '%a %x'). Good idea is to create a class for date and get rid from all format conversions. So, only date(y, m, d), timedelta(days=7), day, weekday() will be left for exercise.
import datetime
from datetime import date, timedelta
def day2string(day):
return '{:%Y%m%d}'.format(day)
def year_holidays(year):
holidays = [
["New Year's Day", 1, 1], # Fixed: January 1
["Birthday of Martin Luther King, Jr.", 1, 0, 0, 3], # Floating
["Washington's Birthday", 2, 0, 0, 3], # Third Monday in February
["Memorial Day", 5, 0, 0, 5], # Last Monday
["Independence Day", 7, 4],
["Labor Day", 9, 0, 0, 1],
["Columbus Day", 10, 0, 0, 2],
["Veterans Day", 11, 11],
["Thanksgiving Day", 11, 0, 3, 4],
["Christmas Day", 12, 25]
]
year_list = []
for h in holidays:
if h[2] > 0:
day = date(year, h[1], h[2]) # Fixed day
else:
day = date(year, h[1], 1) # Floating day
while h[3] != day.weekday(): # Advance to match the weekday
day += timedelta(days=1)
count = 1
while count != h[4]: # Match the repetition of this day
next_week = day + timedelta(days=7)
if next_week.month == day.month:
day = next_week
count += 1
year_list.append(day2string(day))
return year_list # return the holidays as list of strings
def str2datetime(string):
return datetime.datetime.strptime(string, '%Y%m%d')
def next_working_day(string):
day = str2datetime(string)
day += timedelta(days=1)
while True:
while day.weekday() >= 5:
day += timedelta(days=1)
holidays_list = year_holidays(day.year)
for str_day in holidays_list:
s2 = day2string(day)
if str_day == s2:
day += timedelta(days=1)
break # for
if day.weekday() < 5:
break # while True
return day2string(day)
if __name__ == '__main__':
dates = [
['20190308', '20190311', '20190313'],
['20190309', '20190311', '20190313'],
['20190310', '20190311', '20190313'],
['20190311', '20190312', '20190314'],
['20190329', '20190401', '20190403'],
['20181231', '20190102', '20190104'],
['20190118', '20190122', '20190124'],
['20190216', '20190219', '20190221'],
['20190526', '20190528', '20190530'],
['20190703', '20190705', '20190709'],
['20190828', '20190829', '20190903'],
['20191010', '20191011', '20191016'],
['20191108', '20191112', '20191114'],
['20191125', '20191126', '20191129'],
['20191224', '20191226', '20191230'],
['20191227', '20191230', '20200102']]
print('\n Today Next and 3rd business day')
for days in dates:
next_day = next_working_day(days[0])
third_day = next_working_day(next_working_day(next_day))
if next_day != days[1] or third_day != days[2]:
print('*** ERROR *** ', end='')
else:
print(' ', end='')
def f(x): return datetime.datetime.strftime(str2datetime(x), '%a %x')
print(f(days[0]), f(next_day), f(third_day))
It should create the next output:
Today Next and 3rd business day
Fri 03/08/19 Mon 03/11/19 Wed 03/13/19
Sat 03/09/19 Mon 03/11/19 Wed 03/13/19
Sun 03/10/19 Mon 03/11/19 Wed 03/13/19
Mon 03/11/19 Tue 03/12/19 Thu 03/14/19
Fri 03/29/19 Mon 04/01/19 Wed 04/03/19
Mon 12/31/18 Wed 01/02/19 Fri 01/04/19
Fri 01/18/19 Tue 01/22/19 Thu 01/24/19
Sat 02/16/19 Tue 02/19/19 Thu 02/21/19
Sun 05/26/19 Tue 05/28/19 Thu 05/30/19
Wed 07/03/19 Fri 07/05/19 Tue 07/09/19
Wed 08/28/19 Thu 08/29/19 Tue 09/03/19
Thu 10/10/19 Fri 10/11/19 Wed 10/16/19
Fri 11/08/19 Tue 11/12/19 Thu 11/14/19
Mon 11/25/19 Tue 11/26/19 Fri 11/29/19
Tue 12/24/19 Thu 12/26/19 Mon 12/30/19
Fri 12/27/19 Mon 12/30/19 Thu 01/02/20
I need to print month using the month and day. But I cannot seem to move the numbers after '1' to the next line using Python.
# This program shows example of "November" as month and "Sunday" as day.
month = input("Enter the month('January', ...,'December'): ")
day = input("Enter the start day ('Monday', ..., 'Sunday'): ")
n = 1
if month == "January" or month == "March" or month == "May" or month == "July" or month == "August" or month == "October" or month == "December":
x = 31
elif month == "February":
x = 28
else:
x = 30
print(month)
print("Mo Tu We Th Fr Sa Su")
if (day == "Sunday"):
print(" ", end='')
for i in range (1, 7):
for j in range (1, 8):
while n != x+1:
print('%2s' % n, end=' ')
n = n + 1
break
print()
Output looks like this:
November
Mo Tu We Th Fr Sa Su
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Some changes.
Instead of having a nested loop, just have a single loop that prints all the dates. Then, inside that loop, make the decision about whether to end the line (if the date you just printed corresponded to a Sunday).
Also, the # of days in month look-up is a bit cleaner, and you now handle more "days" than just Sunday:
day = "Monday"
month = "March"
# Get the number of days in the months
if month in ["January", "March", "May", "July", "August", "October", "December"]:
x = 31
elif month in ["February"]:
x = 28
else:
x = 30
# Get the number of "blank spaces" we need to skip for the first week, and when to break
DAY_OFF = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
off = DAY_OFF.index(day)
print(month)
print("Mo Tu We Th Fr Sa Su")
# Print empty "cells" when the first day starts after Monday
for i in range(off):
print(" ", end=' ')
# Print days of the month
for i in range(x):
print("%2d" % (i+1), end=' ')
# If we just printed the last day of the week, print a newline
if (i + off) % 7 == 6: print()
March/Monday
March
Mo Tu We Th Fr Sa Su
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
March/Sunday
March
Mo Tu We Th Fr Sa Su
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
February/Sunday
February
Mo Tu We Th Fr Sa Su
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
First problem I see in your code, is: why are you using an while and a break just after start it?
It seems that you only need an if statement, not a while.
Second, you're using the same logic for any line of your calendar, that means: They start on Monday and end on Sunday.
You should change the start point of your inner for loop for your first line, depending on the day that it starts.
A simple dictionary can hold the number associated with each day of the week and for the first week you use it as the start point of the for instead of 1.
And your code will work only for Monday and Sunday as the first day of the month.
To make it works for any first day you should change the way you print spaces, changing it depending on the first day.
The code with the changes:
month = 'November'
day = 'Sunday'
x = 30
n = 1
days = { 'Mo': 1, 'Tu': 2, 'We': 3, 'Th': 4, 'Fr': 5, 'Sa': 6, 'Su': 7 }
print(" "*(days[day[:2]]-1), end='') # print 3 spaces for each day that isn't the first day of the month
start = days[day[:2]] # Set the start of the inner loop to the first day of the month
for i in range (1, 7):
for j in range (start, 8):
start = 1
if n < x+1:
print('%2s' % n, end=' ')
n = n + 1
print()
Re-phrasing my Question, as suggested by moderator.
I need to create a calendar with Python & CSS for a web page, I tried the following in Python:
#!/usr/bin/env python
import os, re, sys, calendar
from datetime import datetime
myCal = calendar.monthcalendar(2011,9)
html += str(myCal)
mycal = myCal[:1]
cal1 = myCal[1:2]
cal2 = myCal[2:3]
cal3 = myCal[3:4]
cal4 = myCal[4:5]
html += str(mycal)+'<br>'
html += str(cal1)+'<br>'
html += str(cal2)+'<br>'
html += str(cal3)+'<br>'
html += str(cal4)+'<br>'
html += "<br>"
This is the following output on the web page:
[[0, 0, 0, 1, 2, 3, 4]]<br>
[[5, 6, 7, 8, 9, 10, 11]]<br>
[[12, 13, 14, 15, 16, 17, 18]]<br>
[[19, 20, 21, 22, 23, 24, 25]]<br>
[[26, 27, 28, 29, 30, 0, 0]]<br>
How can I arrange the above in the following format below?
(
This is a SAMPLE format, I have not done the actual Day / Date match.
The format needs to be in TWO rows.
eg.DayDateimgNDayDateimgN next month
)
Dec , 2011
----------------------------------------------------------------------------
sun | mon | tue | wed thu fri sat sun mon tue wed thu fri sat sun
-------|-------|-------|----------------------------------------------------
.... 1 |.. 2 ..|.. 3 ..| 4 5 6 7 8 9 10 11 12 13 14 15<br>
------ |-------|-------|----------------------------------------------------
img1 | img2 | img3 | ....
Jan , 2012
----------------------------------------------------------------------------
sun | mon | tue | wed thu fri sat sun mon tue wed thu fri sat sun
-------|-------|-------|----------------------------------------------------
.... 1 |.. 2 ..|.. 3 ..| 4 5 6 7 8 9 10 11 12 13 14 15<br>
------ |-------|-------|----------------------------------------------------
img1 | img2 | img3 | ....
Feb , 2012
----------------------------------------------------------------------------
sun | mon | tue | wed thu fri sat sun mon tue wed thu fri sat sun
-------|-------|-------|----------------------------------------------------
.... 1 |.. 2 ..|.. 3 ..| 4 5 6 7 8 9 10 11 12 13 14 15<br>
------ |-------|-------|----------------------------------------------------
img1 | img2 | img3 | ....
I'm not sure exactly what you are looking for, this produces a table with the 3 rows you describe, but for the entire month (not just the first 15 days). I may be starting point
import calendar
import itertools
blank = " "
myCal = calendar.monthcalendar(2011,9)
day_names = itertools.cycle(['mon','tue','wed','thu','fri','sat','sun']) # endless list
cal = [day for week in myCal for day in week] # flatten list of lists
# empty lists to hold the data
headers = []
numbers = []
imgs = []
# fill lists
for d in cal:
if d != 0:
headers.append(day_names.next())
numbers.append(d)
imgs.append("image"+str(d))
else:
headers.append(day_names.next())
numbers.append(blank)
imgs.append(blank)
# format data
html = "<table><tr></tr>{0}<tr>{1}</tr><tr>{2}</tr></table>".format(
"".join(["<td>%s</td>"% h for h in headers]),
"".join(["<td>%s</td>"% n for n in numbers]),
"".join(["<td>%s</td>"% i for i in imgs]))
I'm not quiet sure I understand the exact format your wanting but you can put the calender into a table of 3 rows.
Try this for each of your months
import calendar
myCal = calendar.monthcalendar(2011,9)
#make multi rowed month into a single row list
days = list()
for x in myCal:
days.extend(x)
#match this up with the week names with enough weeks to cover the month
weeks = len(myCal) * ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
#some images
images = ['image1', 'image2', '...']
#make sure there is at least a zero at the end of the list
days.append(0)
#find start and end indexes where the actual day numbers lie
start_index = days.index(1)
#if there are no zeros at the end of the list this will fail
end_index = days[start_index:].index(0) + len(days) - len(days[start_index:])
header = 'Dec, 2011'
#Create the table rows of just the items between start_index and end_index.
weekday_row = '<tr>%s</tr>'%(''.join(['<td>%s</td>'%(x)
for x in weeks[start_index:end_index]]))
day_row = '<tr>%s</tr>'%(''.join(['<td>%d</td>'%(x)
for x in days[start_index:end_index]]))
image_row = '<tr>%s</tr>'%(''.join(['<td>%s</td>' % (x) for x in images]))
#finally put them all together to form your month block
html = '<div>%s</div><table>\n\n%s\n\n%s\n\n%s</table>' % (header,
weekday_row,
day_row,
image_row)
You can repeat that as many times as you need