My program intends to achieve this
(A) Write a C function named larger()
that returns the later date of any two dates passed to it. For
As half-promised, a revision of Rüppell's Vulture's answer. This code struggles to avoid the repetition in the other answer — which is already reduced by comparison with the code in the question.
There are multiple changes. Since the type only stores a single date, it is renamed Date
. There is a general purpose function read_validate_number()
which is used to handle the entry of each component of the date (with a separate prompt). The function returns an error/non-error status and returns the value via a pointer argument. It puts an upper bound on the number of times the user is prompted for a number. It avoids insulting the user but does report the erroneous value. The code uses a while
loop rather than a do ... while
loop; generally, the latter should be avoided. It would be possible to read to a newline after a failure to read a number instead of simply returning an error.
With this function on hand, writing read_date()
becomes trivial. And with read_date()
on hand, the main()
function simplifies.
I'm still not keen on the interface to the function larger()
; in general, I prefer the interface shown in later_date()
. However, the code shows the one advantage of the larger()
interface; it can identify the larger of the two dates by position in the array, whereas later_date()
does that by value.
The functions other than main()
are static since they aren't used outside this file; the compiler options I use require either static
functions or an extern
declaration.
On the whole, I'd prefer a less verbose interface for entering dates. I also prefer the ISO 8601 format for dates since they are unambiguous; however, that would be a user preference item in fully internationalized (and localized) code.
#include
#include
#include
#define NUM 2
typedef struct Date
{
int month;
int day;
int year;
} Date;
enum { MAX_ERRORS = 3 };
static int read_validate_number(const char *tag, int min, int max, int *value)
{
int errors = 0;
assert(min <= max);
while (errors++ < MAX_ERRORS)
{
printf("Please enter the %s number (%d-%d): ", tag, min, max);
if (scanf("%d", value) != 1)
{
printf("Failed to read number\n");
return EOF;
}
if (*value >= min && *value <= max)
return 0;
printf("The value entered (%d) is outside the range %d-%d\n", *value, min, max);
}
printf("Too many errors entering %s\n", tag);
return EOF;
}
static int read_date(Date *date)
{
if (read_validate_number("month", 1, 12, &date->month) != 0 ||
read_validate_number("day", 1, 31, &date->day ) != 0 ||
read_validate_number("year", 1, 9999, &date->year ) != 0)
return EOF;
return 0;
}
static Date *larger(Date *more)
{
int days0 = (more[0].month*31)+(more[0].day)+(more[0].year*365);
int days1 = (more[1].month*31)+(more[1].day)+(more[1].year*365);
// Resist the temptation to write: return more + (days1 > days0);
if (days1 > days0)
return more+1;
else
return more+0;
}
static Date later_date(Date d1, Date d2)
{
int days1 = d1.day + d1.month * 31 + d1.year * 365;
int days2 = d2.day + d2.month * 31 + d2.year * 365;
if (days1 > days2)
return d1;
else
return d2;
}
int main(void)
{
Date user[NUM];
printf("Enter two dates, and the program will return the larger.\n");
if (read_date(&user[0]) == 0 && read_date(&user[1]) == 0)
{
putchar('\n');
printf("Date 1: %.4d-%.2d-%.2d\n", user[0].year, user[0].month, user[0].day);
printf("Date 2: %.4d-%.2d-%.2d\n", user[1].year, user[1].month, user[1].day);
Date *p_later = larger(user);
Date v_later = later_date(user[0], user[1]);
printf("\nThe later date is the %s (%d/%d/%d)\n",
(p_later == &user[0]) ? "first" : "second",
p_later->month, p_later->day, p_later->year);
printf("Later Date: %.4d-%.2d-%.2d\n", v_later.year, v_later.month, v_later.day);
}
return 0;
}
Sample output:
Enter two dates, and the program will return the larger.
Please enter the month number (1-12): 12
Please enter the day number (1-31): 25
Please enter the year number (1-9999): 2013
Please enter the month number (1-12): 1
Please enter the day number (1-31): 1
Please enter the year number (1-9999): 2012
Date 1: 2013-12-25
Date 2: 2012-01-01
The later date is the first (12/25/2013)
Later Date: 2013-12-25
I note that you could reduce the size of the Date
structure by using unsigned char
(or uint8_t
) for the day
and month
components, and an unsigned short
(or uint16_t
) for the year
component. However, you'd have to modify the read_date()
function a bit to do so:
#include
typedef struct Date
{
uint8_t month;
uint8_t day;
uint16_t year;
} Date;
static int read_date(Date *date)
{
int mm, dd, yyyy;
if (read_validate_number("month", 1, 12, &mm ) != 0 ||
read_validate_number("day", 1, 31, &dd ) != 0 ||
read_validate_number("year", 1, 9999, &yyyy) != 0)
return EOF;
date->month = mm;
date->day = dd;
date->year = yyyy;
return 0;
}
At some point, you might want to prevent someone entering the 31st of February (but remember that there was once a 30th of February — in Sweden, in 1712; or there again, maybe you don't need to remember that).