I have already defined a function to check if a given date is a valid date or not. Now I must find out
a) difference between 2 dates
b) date 2 should not be less than date 1
c) date 2 and date 1 must be valid dates. I have to incorporate the first function (i.e., valid date function) into the second function.
import datetime
def is_valid_date (year, month, day):
"""
check if the input date is a valid date or not.
"""
if month == 1 or month == 3 or month == 5 or month == 7 or month == 8 or month == 10 or month == 12:
max_days = 31
elif month == 4 or month == 6 or month == 9 or month == 11:
max_days = 30
elif year % 4 == 0 and year % 100 != 0 or year % 400 == 0:
max_days = 29
else:
max_days = 28
if (month < 1 or month > 12) or (day < 1 or day > max_days) or (year < 1 or year > 9999):
return "Invalid Date"
else:
return "Valid Date"
def days_between(year, month, day, year1, month1, day1):
result = is_valid_date (year, month, day)
if datetime.date (year1, month1, day1) != result:
return 0
elif datetime.date (year,month,day) != result:
return 0
elif datetime.date (year1, month1, day1) < datetime.date (year, month, day):
return 0
else:
date1= (year, month, day)
date2= (year1, month1, day1)
difference = datetime.date (date2) - datetime.date (date1)
return difference
print (is_valid_date(2000, 12,1))
print (days_between(2000,12,1,2000,12,20))
Code is returning the following answers:
Valid Date
0
What I would like to get:
Valid Date
19
You'd have to change your second function to something like this, which checks for incorrect conditions as well as subtracts dates if everything is correct
def days_between(year, month, day, year1, month1, day1):
result1 = is_valid_date (year, month, day)
result2 = is_valid_date (year1, month1, day1)
if (result1=="Invalid Date")|(result2=="Invalid Date"):
return 0
elif datetime.date (year1, month1, day1) < datetime.date (year, month, day):
return 0
else:
difference = datetime.date (year1, month1, day1) - datetime.date (year, month, day)
return difference
print (is_valid_date(2000, 12,1))
print (days_between(2000,12,1,2000,12,20))
>>
Valid Date
19 days, 0:00:00
I was working on Project Euler problem 19 and my initial approach at the problem involved finding a function that would just tell me the day of the week given a date in the form yyy-mm-dd by generating the number of days that had passed since 1900-01-01 (which we are told is a Monday).
I later wrote a loop that would consider the 1st day of every month in the range 1901-01-01 and 2000-12-31 and count the number of Sundays.
As with some of the other project Euler problems, it is quite hard to resist playing around with it and trying to produce a couple of other results. The code that I have before correctly tells me the result for the number of Sundays in the given range but I also asked it to tell me (a) the number of occurences of any given day in the given range (both specified by the user) and (b) the day of the week for the first of each month in the year 2000. I think part (a) seems to work correctly also but looking at the result of part (b), all of these days are off by a single day from what the year 2000 calendar actually was. Playing around with other years, there seem to be similar discrepencies appearing.
Whilst I have solved what the question asks me, this is annoying me greatly and I can't happily move on to the next problem before I figure out what I did wrong here! Can anyone help me? Thanks!
My code is below (I'm fairly new to Python so it's probably very far from optimal but it runs in 1 or 2 seconds so I'm not too upset!)
import time
date = input("Enter a date after 1900-01-01 in the form yyyy-mm-dd: ")
which_day = input("Which day of the week would you like to count? ")
start_time = time.time() #Start the clock after the input
date_year = date[0:4]
date_month = date[5:7]
date_day = date[8:10]
def day_difference_month(date_month): #Find the number of days accumulated until we reach the month in question
if date_month=='01':
return 0
elif date_month=='02':
return 31
elif date_month=='03':
return 59
elif date_month=='04':
return 90
elif date_month=='05':
return 120
elif date_month=='06':
return 151
elif date_month=='07':
return 181
elif date_month=='08':
return 212
elif date_month=='09':
return 243
elif date_month=='10':
return 273
elif date_month=='11':
return 304
elif date_month=='12':
return 334
day_difference_day = int(date_day) #Find the number of days accumulated between first of month and day in question
def day_difference_year(date_year): #Find the number of days accumulated between 1900 and the year in question
days = abs(int(date_year) - 1900)*365
return days
def leap_days(date_year,x,date_month):
total=0
total = (x // 1460) - (x // 36500) + (x // 146000) #This takes account of leap years. We find the floor wrt 1460 days (4 year period) and thus add a day for each 4 year period. We subtract a day for each 100 year period e.g. in 100th year no day is added since we get +1 from mod 4 and -1 from mod 100. We then add a third contribution for mod 400 meaning a leap year takes place in this situation.
if (int(date_month)==1 or int(date_month)==2) and (int(date_year) % 100 != 0 or int(date_year) % 400 == 0): #This accounts for the fact that the extra day won't contribute until March or later so we better not add yet it if the date in question is January or February
total -=1
return total
total_days = leap_days(date_year,day_difference_year(date_year),date_month) + day_difference_year(date_year) + day_difference_month(date_month) + day_difference_day
def day_finder(total_days):
if total_days % 7 == 1:
return 'Monday'
elif total_days % 7 == 2:
return 'Tuesday'
elif total_days % 7 == 3:
return 'Wednesday'
elif total_days % 7 == 4:
return 'Thursday'
elif total_days % 7 == 5:
return 'Friday'
elif total_days % 7 == 6:
return 'Saturday'
elif total_days % 7 == 0:
return 'Sunday'
def no_particular_day(which_day,total_days):
if which_day == 'Monday':
if total_days >6 and total_days %7 ==0: #if the end day is same as day we're counting, we add the number of weeks (total_days //7) + 1 (initial day)
return total_days // 7 + 1
elif total_days >7: #if end day is different to day we are counting we simply count the number of weeks
return total_days // 7
else:
return 1 #if end date is within one week of start date then we count 1 for 1900-01-01
elif which_day == 'Tuesday':
if total_days >7 and total_days %7 ==1: #logic same as above - we need total_days %7 ==1 for end date to be Tuesday
return total_days // 7 + 1
elif total_days >7:
return total_days //7
elif total_days >0: #if we're within one week of the start date and more than one day has passed, then we count 1 Tuesday (first day was a Monday)
return 1
else:
return 0 #if less than one day has passed i.e. zero days then we are stuck on zero Tuesdays
elif which_day == 'Wednesday':
if total_days >8 and total_days %7 ==2:
return total_days // 7 + 1
elif total_days >7:
return total_days // 7
elif total_days >1:
return 1
else:
return 0
elif which_day == 'Thursday':
if total_days >9 and total_days %7 ==3:
return total_days // 7 + 1
elif total_days >7:
return total_days // 7
elif total_days >2:
return 1
else:
return 0
elif which_day == 'Friday':
if total_days >10 and total_days %7 ==4:
return total_days // 7 + 1
elif total_days >7:
return total_days // 7
elif total_days >3:
return 1
else:
return 0
elif which_day == 'Saturday':
if total_days >11 and total_days %7 ==5:
return total_days // 7 + 1
elif total_days >7:
return total_days // 7
elif total_days >4:
return 1
else:
return 0
elif which_day == 'Sunday':
if total_days >12 and total_days %7 ==6:
return total_days // 7 + 1
elif total_days >7:
return total_days //7
elif total_days >5:
return 1
else:
return 0
def sunday_counter_from_1900(date_year):
increment = 0
for i in range(1900,int(date_year)+1):
string_year=str(i)
for month in ['01','02','03','04','05','06','07','08','09','10','11','12']:
day_count = day_difference_year(string_year) + day_difference_month(month) + 1 #we're considering 1st of each month so day_difference(date_day)=1 always)
if day_count %7 ==0:
increment +=1
return increment
def sunday_counter_1900_to_1901():
increment = 0
string_year=str(1900)
for month in ['01','02','03','04','05','06','07','08','09','10','11','12']:
day_count = day_difference_year(string_year) + day_difference_month(month) +1 #we're considering 1st of each month so day_difference(date_day)=1 always)
if day_count %7 ==0:
increment +=1
return increment
def sunday_counter_from_1901(date_year):
x= sunday_counter_from_1900(date_year) - sunday_counter_1900_to_1901()
return x
for month in ['01','02','03','04','05','06','07','08','09','10','11','12']:
print(day_finder(leap_days(2000,day_difference_year(2000),month)+day_difference_year(2000)+day_difference_month(month)+1))
print("%s was a %s." %(date,day_finder(total_days)))
if int(date_year) >= 1900:
print("There were %s %s between 1900-01-01 and %s." %(no_particular_day(which_day,total_days),which_day+'s',date))
else:
print("You entered a date in the past of the start date. This formula only counts day occurences for end dates in the future. Try again please.")
print("Between 1901-01-01 and the end of the year %s, there were %s months with a Sunday falling on the first day." %(date_year, sunday_counter_from_1901(date_year)))
print("Runtime: %.2f seconds" % (time.time() - start_time))
And the output that I get is:
Enter a date after 1900-01-01 in the form yyyy-mm-dd: 2000-12-31
Which day of the week would you like to count? Sunday
Friday
Monday
Tuesday
Friday
Sunday
Wednesday
Friday
Monday
Thursday
Saturday
Tuesday
Thursday
2000-12-31 was a Saturday.
There were 5270 Sundays between 1900-01-01 and 2000-12-31.
Between 1901-01-01 and the end of the year 2000, there were 171 months with a Sunday falling on the first day.
Runtime: 0.07 seconds
I was solving Project Euler #19:
How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?
And here is the code :
months = { "January": 31,
"February" : 28,
"March" : 31,
"April" : 30,
"May" : 31,
"June" : 30,
"July" : 31,
"August" : 31,
"September" : 30,
"October" : 31,
"November" : 30,
"December" : 31}
def countingSundays():
day = 1
sunday_count = 0
for year in xrange(1901,2001):
for m in months:
day += months[m]
if year % 4 == 0 and m == "February":
day += 1
if day % 7 == 0:
sunday_count += 1
print "Sundays:", sunday_count
The output of the program is 172 which is incorrect.
I searched the answer to be 171.
So I wanted to know why am I getting the extra 1 Sunday ?
You're iterating over the months dict, expecting it to iterate in the order of the months, but dicts aren't ordered, so you can get the months in the wrong order.
Since you don't actually need the month names, you can just make months a list of the month lengths instead.
You should use the datetime library, which will handled all the leap year information automatically:
from datetime import date
from collections import Counter
counter = Counter()
for year in xrange(1901, 2001):
for month in xrange(1, 13):
day = date(year, month, 1)
counter[day.weekday()] += 1
print counter[6]
import time
from math import floor
"""
Gaussian algorithm to determine day of week
"""
def day_of_week(year, month, day):
"""
w = (d+floor(2.6*m-0.2)+y+floor(y/4)+floor(c/4)-2*c) mod 7
Y = year - 1 for January or February
Y = year for other months
d = day (1 to 31)
m = shifted month (March = 1, February = 12)
y = last two digits of Y
c = first two digits of Y
w = day of week (Sunday = 0, Saturday = 6)
"""
d = day
m = (month - 3) % 12 + 1
if m > 10: Y = year - 1
else: Y = year
y = Y % 100
c = (Y - (Y % 100)) / 100
w = (d + floor(2.6 * m - 0.2) + y + floor(y/4) + floor(c/4) - 2*c) % 7
return int(w)
"""
Compute the number of months starting on a given day of the week in a century
"""
def months_start_range(day,year_start,year_end):
total = 0
for year in range(year_start, year_end + 1):
for month in range(1,13):
if day_of_week(year, month, 1) == day: total += 1
return total
start = time.time()
total = months_start_range(0,1901,2000)
elapsed = time.time() - start
print("%s found in %s seconds") % (total,elapsed)
This might you solve the problem.
It took around 0.068 seconds to solve it.
Here is a different approach to tackle this question
public static void main(String[] args) {
int k = 0;
// String months[] = { "January", "February", "March", "April", "May", "June",
// "July", "August", "September",
// "October", "November", "December" };
String Days[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
int MonthsDdays[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int counter = 0;
for (int t = 1900; t <= 2000; t++) {
MonthsDdays[1]=28;
if (t % 4 == 0) {
if (t % 100 == 0)
{
if (t % 400 == 0)
MonthsDdays[1] = 29;
} else if (t % 100 != 0)
MonthsDdays[1] = 29;
}
int p = 0;
while (p < 12) {
for (int j = 0; j < MonthsDdays[p]; k++, j++) {
if (k == 7)
k = 0;
if (Days[k].equalsIgnoreCase("Sunday") && j == 0 && t > 1900) {
counter++;
}
}
p++;
}
}
System.out.println(counter);
}
I tried the Mathematical approach although we could use the calendar functions.
I first calculated the math of the months to determine the relationships between the first dates of the months using the other months. Also, for simplicity in calculating leap years, I calculated the year from March to Feb. If you want to calculate for the Jan and Feb of 1901, you can write a separate condition, and do the same to remove Jan and Feb of 2001. However, in this case, they do not really matter as they are not Sundays, so you could remove the last if condition for this specific case.
# Zero is Sunday and the rest of the days are according to mod7
# li stores the first days of the months in the year in every iteration
# The year in initial li is 1900 but the contents are Mar-1900 to Feb-1901
# At the end, we can check if Jan or Feb of 2001 contain a Sunday and remove if it does
li, cnt = [4,0,2,5,0,3,6,1,4,6,2,5], 0
# Could also initialize li from by the same method as below, but I had already calculated those
# cnt adds number of zeros in every iteration (the number of Sundays in every year) to its value
# As we don't count for the year 1900 cnt=0, else initialize cnt=li.count(0)
for year in range(1901,2001):
if year%4==0:
li[0]=li[8]=(li[11]+1)%7 #Set March and November to +1 value than last Feb
else:
li[0]=li[8]=li[11] #Set March and November to same value as last Feb
# The following values of other months will depend solely on their own March value
# You can check the Math if you want to
li[3]=li[11]=(li[0]+1)%7;li[6]=li[9]=(li[0]+2)%7;li[1]=li[4]=(li[0]+3)%7;li[2]=li[10]=(li[0]-2)%7;li[5]=(li[0]-1)%7;li[7]=(li[0]-3)%7
cnt = cnt + li.count(0)
# This is to remove the extra two months of the year 2001 if they bother the answer
if li[10] == 0 or li[11] == 0:
cnt = cnt-1
print(cnt)
This was my first answer on StackOverflow, I hope I wrote well. ;-)
The mistakes you have:
The way you calculate leap years
Dictionary does not keep the order necessarily
You assume January 1st is Sunday
The correct program would be:
from collections import OrderedDict
months = OrderedDict( [("January",31),("February", 28),("March",31),
("April", 30), ("May", 31), ("June", 30),
("July", 31), ("August", 31), ("September", 30),
("October", 31), ("November", 30), ("December", 31)] )
days = ['Tuesday','Wednesday', 'Thursday','Friday','Saturday', 'Sunday', 'Monday']
day = 0
sunday_count = 0
def isLeap(year): #https://en.wikipedia.org/wiki/Leap_year#Algorithm
leap = True
if year % 4 != 0:
leap = False
elif year % 100 != 0:
leap = True
elif year % 400 != 0:
leap = False
return leap
for year in xrange(1901,2001):
leap = isLeap(year)
for m in months:
dayName = days[day%7]
if dayName == "Sunday":
sunday_count += 1
#print year, m, dayName
day += months[m]
if leap == True and m == "February":
day += 1
print sunday_count
# print 171
Also, some days:
1901 January Tuesday
1901 February Friday
1901 March Friday
1901 April Monday
1901 May Wednesday
1901 June Saturday
1901 July Monday
1901 August Thursday
1901 September Sunday
...
import pandas as pd
from datetime import date
start = date(1901, 1, 1)
end = date(2000, 12, 31)
d = pd.date_range(start, end, freq='MS').strftime('%A')
s = pd.Series(d)
print(s.value_counts())
So I approached this problem from not a date perspective but of a counting days.
Here's my solution:.
days_1st = list()
day_counter = 1
for year in range(1900, 2001):
for month in range(1,13):
#Skip for year 1900 as count starts from 1901, but this still
#adds the days hence keeping the cycle in sync!
if year != 1900:
days_1st.append(day_counter)
if month == 4 or month == 6 or month == 9 or month == 11:
day_counter+=30
elif month == 2 and ((year % 100 == 0 and year % 400 == 0) or (year % 100 != 0 and year % 4 == 0)):
day_counter+=29
elif month == 2:
day_counter+=28
else:
day_counter+=31
# mod 7 because since the day the counting started (1 Jan 1900 -
# Monday) Every 7th day is a sunday!
days_sunday = list(filter(lambda x: x % 7 == 0, days_1st))
print(len(days_sunday))
A = [31,28,31,30,31,30,31,31,30,31,30,31]
sunday =0
gK = 1
for y in range(1901,2001):
if(y %4 ==0):
A[1] = 29
else:
A[1] = 28
for m in range(len(A)):
for d in range(1,A[m]+1):
if(gK ==6):
if(d==1):
sunday +=1
gK =0
else:
gK =gK+1
print(sunday)
==>Solution in python
euler19.py
normal_year = [31,28,31,30,31,30,31,31,30,31,30,31]
leap_year = [31,29,31,30,31,30,31,31,30,31,30,31]
years = [ normal_year ] * 100
for i in range(3, len(years), 4) :
years[i] = leap_year
current_day = (0+365) % 7
sundays = 0
for y in years :
for m in y :
if current_day % 7 == 6:
sundays += 1
current_day += m%7
print (sundays)
I think I got the answer. I am not sure though.. your logic was right. But needed a little improvement. We need to start off by counting the number of Tuesdays first as we clearly know that it was Monday on Jan 1, 1900.
months = { "January": 31,
"February" : 28,
"March" : 31,
"April" : 30,
"May" : 31,
"June" : 30,
"July" : 31,
"August" : 31,
"September" : 30,
"October" : 31,
"November" : 30,
"December" : 31}
for month in months:
print(months[month])
tuesday_count = 0
day = 0
extra_days = 0
for year in range(1901, 2001):
days_in_the_year = 0
for month in months:
day += months[month]
days_in_the_year += months[month]
if( year % 4 == 0 and month == 'February'):
if (year % 100 != 0):
extra_days += 1
days_in_the_year += 1
day += 1
elif(year % 100 ==0 and year % 400 ==0):
extra_days += 1
days_in_the_year += 1
day += 1
if( (day) % 7 == 0):
tuesday_count += 1
print('No. of days in the year',year,'are',days_in_the_year)
print('No. of Tuesdays counted so far is =', tuesday_count)
print('The number of extra_days because of the leap years are:',extra_days)
# print('extra_days % 7 =', '25 % 7 =', extra_days % 7)
print('So, there were', extra_days // 7, 'extra_no_of_weeks left that we haven\'t considered. After that, it\'s followed by --wed, thu, fri and sat (we don\'t need to consider that).\n So, the total number of Tuesdays are', tuesday_count+3 )
tuesday_count += 3
print('This means only 2 Sundays that have followed')
sunday_count = tuesday_count - 1
print('Since, 1901 Jan1 would be a Tuesday, we need to subract one from the total number of Sundays\n So, the total number of sundays are:', )
sunday_count=sunday_count-1
print(sunday_count)
months=[31,28,31,30,31,30,31,31,30,31,30,31]
leap=[31,29,31,30,31,30,31,31,30,31,30,31]
sundays=0
start=2
for y in range(25):
for nonleap in range (3):
for j in months:
start=(start+j)%7
if start == 0:
sundays+=1
for m in leap:
start=(start+m)%7
if start == 0:
sundays+=1
print sundays
Note that the problem defines the first day of 1900 as Monday and you define the first day of 1901 as Monday.
months = [31,28,31,30,31,30,31,31,30,31,30,31]
def countingSundays():
day = 1
sunday_count = 0
for year in range(1900,1901):
for m in months:
day += m
if (year % 4 == 0 and m == 28):
day += 1
for year in range(1901,2001):
for m in months:
day += m
if (year % 4 == 0 and m == 28):
day += 1
if day % 7 == 0:
sunday_count += 1
return sunday_count
print ("Sundays:", countingSundays())
you have initialized the day variable to 1 but the 1st Jan 1901 is a Tuesday. I made the same error ;-)