Tag Archives: date

Problem Nineteen

24 Oct

Here’s problem nineteen:

Question: How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?

This could’ve been solved trivially by using the Python date module, but where’s the fun in that? I decided to create my own date-incrementer by simply the date in a list, as [month, day, year, weekday], and incrementing by day using a handful of functions.

The code is long, but we’ll look at it in chunks:

def leapyear(date): # checks if year is leap year
	global leapyr 
	if str(date[2])[-2:] != '00' and date[2] % 4 == 0:
		leapyr = True
	elif str(date[2])[-2:] == '00' and date[2] % 400 == 0:
		leapyr = True
	else:
		leapyr = False

def weekday(date): # increments weekday (m, t, w..)
	if date[3] == 7:
		date[3] = 1
	else:
		date[3] += 1
	
	return date

def day(date): # increments day (1, 2, 3, 4..31)
	if any(date[0] == i for i in [4, 6, 9, 11]): # 30 days
		if date[1] == 30:
			date[1] = 1
		else:
			date[1] += 1

	elif date[0] == 2: # february
		if leapyr:
			if date[1] == 29:
				date[1] = 1
			else:
				date[1] += 1
		else:
			if date[1] == 28:
				date[1] = 1
			else:
				date[1] += 1

	else: # 31 days
		if date[1] == 31:
			date[1] = 1
		else:
			date[1] += 1

	return date 

def month(date): # increments month
	if date[1] == 1: # since we increment the day first, if the day is 1, meaning the first day of a new month
		if date[0] != 12: # increment month
			date[0] += 1
		else:
			date[0] = 1

	return date

def year(date): # increments year
	if date[1] == 1 and date[0] == 1: # we increment day and month first, so if it is jan. 1st
		date[2] += 1 # increment year

	return date

date = [1, 1, 1901, 2] # start date, is tuesday
result = 0 # holds number of sundays on first of month

while date[0:3] != [12, 31, 2000]: # up to this date
	leapyear(date)
	date = weekday(date)
	date = day(date)
	date = month(date)	
	date = year(date)

	if date[1] == 1 and date[3] == 7:
		result += 1
	
print(result)

So let’s look at the run code first, at the very bottom.

date = [1, 1, 1901, 2] # start date, is tuesday
result = 0 # holds number of sundays on first of month

while date[0:3] != [12, 31, 2000]: # up to this date
	leapyear(date)
	date = weekday(date)
	date = day(date)
	date = month(date)	
	date = year(date)

	if date[1] == 1 and date[3] == 7:
		result += 1
	
print(result)

The starting date is stored as a list in date, and is a Tuesday (found by WolframAlpha), hence the 2. While the date is not the ending date (Dec 31, 2000), we run this chunk of code. leapyear(date) checks to see if the year is a leap year, and the next four functions each increment the date according to its name. Then we check if the current date is a Sunday on the first of a month, and if it is, we add one to result.

def leapyear(date): # checks if year is leap year
	global leapyr 
	if str(date[2])[-2:] != '00' and date[2] % 4 == 0:
		leapyr = True
	elif str(date[2])[-2:] == '00' and date[2] % 400 == 0:
		leapyr = True
	else:
		leapyr = False

Now let’s look at leapyear(). It calculate leap years based on what the question told us: “A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400”. We check if the last two digits of the year are “00” to determine if a year is a century. This is pretty simple.

def weekday(date): # increments weekday (m, t, w..)
	if date[3] == 7:
		date[3] = 1
	else:
		date[3] += 1
	
	return date

Incrementing the weekday is also pretty simple, and we use Monday as 1 up to Sunday as 7. If it’s Sunday, the next day is Monday, so we make sure to reset the weekday to 1.

def day(date): # increments day (1, 2, 3, 4..31)
	if any(date[0] == i for i in [4, 6, 9, 11]): # 30 days
		if date[1] == 30:
			date[1] = 1
		else:
			date[1] += 1

	elif date[0] == 2: # february
		if leapyr:
			if date[1] == 29:
				date[1] = 1
			else:
				date[1] += 1
		else:
			if date[1] == 28:
				date[1] = 1
			else:
				date[1] += 1

	else: # 31 days
		if date[1] == 31:
			date[1] = 1
		else:
			date[1] += 1

	return date 

Here it gets a bit complicated. We have to know what month it is to calculate the number of days in that month, and also if it’s a leap year. The code is divided into three chunks, with the first and last dedicated to months with 30 and 31 days, respectively. This is more or less straightforward, using the same logic for incrementing as in weekday(). For February, we have to consider if it’s a leap year or not. This is found by the global variable leapyr, which we calculate at every date by the function leapyear(). If it is a leap year, we increment the day until it hits 29, and if it isn’t, we increment until 28.

def month(date): # increments month
	if date[1] == 1: # since we increment the day first, if the day is 1, meaning the first day of a new month
		if date[0] != 12: # increment month
			date[0] += 1
		else:
			date[0] = 1

	return date

Moving on to the month. Remember that in the run code that we increment the day before we increment the month. So, that means if the day is 1, it must be the first day of a new month, and so we increment the month by one. Same logic as before, incrementing up to 12.

def year(date): # increments year
	if date[1] == 1 and date[0] == 1: # we increment day and month first, so if it is jan 1st
		date[2] += 1 # increment year

	return date

The last part is incrementing the year. We increment the day and month before incrementing the year, so if the day and month are 1/1 (Jan 1st), that means it must be a new year, so we increment year by 1. There is no limit on how high the year can go.

When the date reaches Dec 31, 2000, the loop ends and we print the result.

Answer: 171