Example 12.7.1. A Top-Down Design.
We want to calculate the number of days between two dates. We will ignore leap years so we can focus on the process and not on special case logic for handling those.
As we identify tasks, we will think of them in terms of precondition (information required to start that task) and postcondition (information produced by that task).
(a) Getting Started.
A top-down design for our date program would start by identifying the high-level tasks:
-
Get dates from the console.
-
Precondition: none. There is no data we need before we can do this job.
-
Postcondition: two strings representing the dates entered by the user like
"3/4/2023".
-
-
Calculate the number of days between two dates.
-
Precondition: two strings like
"3/4/2023"and"4/5/2024" -
Postcondition: an integer representing the number of days between the two dates.
-
-
Print result
-
Precondition: number of days.
-
Postcondition: none.
-
The first and third tasks sound pretty simple - a few lines of code each. The second task sounds too complicated to implement in a few lines of code. So letβs turn it into a function. We will call the function
int daysBetween(string date1, string date2). It will take the two strings and produce the integer.
(b) Refining daysBetween.
We need to think through the
daysBetween function.
If we can convert both dates into a number of days since the start of year 0000, we can then find the difference between those two numbers. That is how we will do the work for
daysBetween .
Note 12.7.2.
We will use β
to mark new chunks of code at each step of the process. Each time you see a list of function ideas, look for that symbol to find the new functions. Anything without that symbol is a function we already identified in a previous step.
We will also use ... to indicate details left out as we repeat earlier descriptions.
So our design now looks like:
-
Get dates from the console...
-
Calculate the number of days between two dates.
-
Precondition: two strings like
"3/4/2023"and"4/5/2024" -
Postcondition: an integer representing the number of days between the two dates.
-
β How:
int daysBetween(string date1, string date2)
-
-
Print result...
β
A function:
int daysBetween(string date1, string date2)
-
β Turn date1 into a number of days.
-
Precondition: date1 is a string like
"3/4/2023". -
Postcondition: an integer representing the number of days since 0/0/0.
-
-
β Turn date2 into a number of days.
-
Precondition: date2 is a string like
"3/4/2023". -
Postcondition: an integer representing the number of days since 0/0/0.
-
-
β Find the difference between the two numbers.
-
Precondition: two integers representing the number of days since 0/0/0 for each date.
-
Postcondition: an integer representing the number of days between the two dates.
-
The last new step, finding the difference, sounds easy. But turning a date into an integer number of days sounds complex. So letβs give that a name:
dateToDays. We will make it into a function that takes a string and produces an integer: int dateToDays(string date). We can use the same function to handle both dates.
(c) Refining dateToDays.
We now need to design the function
dateToDays. If we take "3/4/2023" and break it up into 3 (month), 4 (day), and 2023 (year), we can use these values to calculate the total number of days.
So our design now looks like:
-
Get dates from the console....
-
Calculate the number of days between two dates... (uses
daysBetween) -
Print result...
A function:
int daysBetween(string date1, string date2). It will:-
Turn date1 into a number of days.
-
Precondition: date1 is a string like
"3/4/2023". -
Postcondition: an integer representing the number of days since 0/0/0.
-
β How:
int dateToDays(string date)
-
-
Turn date2 into a number of days.
-
Precondition: date2 is a string like
"3/4/2023". -
Postcondition: an integer representing the number of days since 0/0/0.
-
β How:
int dateToDays(string date)
-
-
Find the difference between the two numbers...
β
The new function:
int dateToDays(string date). It will:-
β Get the month
-
Precondition: date is a string like
"3/4/2023". -
Postcondition: an integer representing the month part.
-
-
β Get the day
-
Precondition: date is a string like
"3/4/2023". -
Postcondition: an integer representing the day part.
-
-
β Get the year
-
Precondition: date is a string like
"3/4/2023". -
Postcondition: an integer representing the year part.
-
-
β Calculate total days
-
Precondition: integers for month, day, and year.
-
Postcondition: an integer representing the total number of days since 0/0/0.
-
How hard is it to take
"3/4/2023" and get the day part, or the month part as an integer? Not too hard. We can probably write that in a few lines of code (find the right part of the string, then use stoi on that part). But if we make those tasks into functions, it will be easier to test that they are working correctly. So letβs do that.
(d) Adding dateToDays helpers.
This version adds in some functions to help turn parts of the date string into functions. It looks like:
-
Get dates from the console....
-
Calculate the number of days between two dates... (uses
daysBetween) -
Print result...
A function
int daysBetween(string date1, string date2). It will:-
Turn date1 into a number of days using
dateToDays... -
Turn date2 into a number of days using
dateToDays... -
Find the difference between the two numbers...
The function
int dateToDays(string date). It will:-
β Get the month using
int getMonth(string date)... -
β Get the day using
int getDay(string date)... -
β Get the year using
int getYear(string date)... -
Calculate total days
-
Precondition: integers for month, day, and year.
-
Postcondition: an integer representing the total number of days since 0/0/0.
-
β
Three new functions for date manipulation:
-
β
int getMonth(string date)It will get up to the first / character, then convert the substring to an integer. -
β
int getDay(string date)It will get the second part (between the first and second / characters), then convert the substring to an integer. -
β
int getYear(string date)It will get the third part (after the second / character), then convert the substring to an integer.
We wonβt bother to break those down further. We are confident we can implement those in a few lines of code each.
(e) Finish dateToDays.
Reviewing our design, the only part that we havenβt thought through is βCalculate the total number of daysβ.
Using the days and years should be easy. We know each year is 365 days (recall that we are ignoring leap years) and each day is 1 day. But the months will be more complicated.
What would make the job easier? Well, what if I could ask someone βhow many days are there before month X starts?β. If I could ask βhow many days were there before the start of Marchβ and get the answer 59 (31 for Jan + 28 for Feb), it would be easy to do the rest of
dateToDays. My math could just look like: totalDays = daysBeforeMonth(month) + (day) + (year * 365)
So letβs add a function
daysBeforeMonth that answers that question.
We will update
int dateToDays(string date) to look like this:-
Get the month using
int getMonth(string date)... -
Get the day using
int getDay(string date)... -
Get the year using
int getYear(string date)... -
β Get the number of days before the given month using
int daysBeforeMonth(int month). -
Calculate total days
-
β Precondition: integers for day, year, and number of days before the month.
-
Postcondition: an integer representing the total number of days since 0/0/0.
-
Note that we adjusted the precondition for the last task in
dateToDays to assume that it will use the value produced by our new daysBeforeMonth function.
How hard is it to implement
daysBeforeMonth? It just needs to return a fixed value for each month. We could implement that with a big if/else chain (if the month is 0, return 0; if the month is 1, return 31; if the month is 2, return 59; etc...).
At this point, each of the listed functions seems simple enough to implement on its own. So now we can start implementation!
