Code:
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <locale.h>
#include <errno.h>
int diffdays(const struct tm *const after, const struct tm *const before)
{
struct tm temp, target, source;
time_t ttarget, tsource;
int days;
if (!after || !before) {
errno = EINVAL;
return 0;
}
/* Source date is before, at noon, DST unknown. */
source.tm_year = before->tm_year;
source.tm_mon = before->tm_mon;
source.tm_mday = before->tm_mday;
source.tm_hour = 12;
source.tm_min = 0;
source.tm_sec = 0;
source.tm_isdst = -1;
tsource = mktime(&source);
/* Target date is after, at noon, DST unknown. */
target.tm_year = after->tm_year;
target.tm_mon = after->tm_mon;
target.tm_mday = after->tm_mday;
target.tm_hour = 12;
target.tm_min = 0;
target.tm_sec = 0;
target.tm_isdst = -1;
ttarget = mktime(&target);
/* We estimate the day count initially by 24-hour, 60-minute, 60-second days. */
days = difftime(ttarget, tsource) / 86400.0;
while (1) {
temp.tm_year = source.tm_year;
temp.tm_mon = source.tm_mon;
temp.tm_mday = source.tm_mday + days;
temp.tm_hour = 12;
temp.tm_min = 0;
temp.tm_sec = 0;
temp.tm_isdst = -1;
mktime(&temp);
/* The day count should not be off by more than a few days
* (leap days and so on). If efficiency is desird, we could do
* better calculations (see between() for example), but
* single day increments an decrements is usually fine. */
if (temp.tm_year > target.tm_year)
days--;
else
if (temp.tm_year < target.tm_year)
days++;
else
if (temp.tm_mon > target.tm_mon)
days--;
else
if (temp.tm_mon < target.tm_mon)
days++;
else
if (temp.tm_mday > target.tm_mday)
days--;
else
if (temp.tm_mday < target.tm_mday)
days++;
else
break;
}
errno = 0;
return days;
}
int between(const struct tm *const after, const struct tm *const before,
int *const yearsptr, int *const monthsptr, int *const daysptr)
{
struct tm temp;
int years, months, days;
if (!after || !before)
return -1;
years = after->tm_year - before->tm_year;
months = after->tm_mon - before->tm_mon;
days = after->tm_mday - before->tm_mday;
while (1) {
/* Keep days to exact count if known, otherwise within 0 and 30, inclusive. */
if (after->tm_mday >= before->tm_mday) {
days = after->tm_mday - before->tm_mday;
} else
if (days < 1) {
months--;
days += 31;
} else
if (days > 31) {
months++;
days -= 31;
}
/* Keep months within 0 and 11, inclusive. */
if (months < 0) {
years--;
months += 12;
} else
if (months > 11) {
years++;
months -= 12;
}
/* Calculate prior date based on elapsed years, months, days. */
temp.tm_year = after->tm_year - years;
temp.tm_mon = after->tm_mon - months;
temp.tm_mday = after->tm_mday - days;
/* At noon. DST unknown. */
temp.tm_hour = 12;
temp.tm_min = 0;
temp.tm_sec = 0;
temp.tm_isdst = -1;
mktime(&temp);
/* Adjust days, if incorrect. */
if (temp.tm_mday != before->tm_mday) {
days += temp.tm_mday - before->tm_mday;
continue;
}
/* Adjust months, if incorrect. */
if (temp.tm_mon != before->tm_mon) {
months += temp.tm_mon - before->tm_mon;
continue;
}
/* Adjust years, if incorrect. */
if (temp.tm_year != before->tm_year) {
years += temp.tm_year - before->tm_year;
continue;
}
/* Found! */
if (yearsptr) *yearsptr = years;
if (monthsptr) *monthsptr = months;
if (daysptr) *daysptr = days;
return 0;
}
}
int main(int argc, char *argv[])
{
struct tm birth, now, then;
time_t nowtime;
int y, m, d, value;
char dummy, suffix[10];
setlocale(LC_ALL, "");
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s YYYY-MM-DD INTERVAL\n", argv[0]);
fprintf(stderr, " %s MM/DD/YYYY INTERVAL\n", argv[0]);
fprintf(stderr, "Where INTERVAL is\n");
fprintf(stderr, " NUMBER s seconds\n");
fprintf(stderr, " NUMBER mi minutes\n");
fprintf(stderr, " NUMBER h hours\n");
fprintf(stderr, " NUMBER d days\n");
fprintf(stderr, " NUMBER w weeks\n");
fprintf(stderr, " NUMBER mo months\n");
fprintf(stderr, " NUMBER y years\n");
fprintf(stderr, "\n");
return 1;
}
birth.tm_hour = 12; /* Noon */
birth.tm_min = 0;
birth.tm_sec = 0;
birth.tm_isdst = -1; /* Unknown whether DST or not. */
birth.tm_wday = -1; /* Irrelevant */
birth.tm_yday = -1; /* Irrelevant */
if (sscanf(argv[1], "%d-%d-%d %c", &y, &m, &d, &dummy) == 3 ||
sscanf(argv[1], "%d/%d/%d %c", &m, &d, &y, &dummy) == 3) {
if (y < 1900) {
fprintf(stderr, "%s: Invalid year.\n", argv[1]);
return 1;
}
if (m < 1 || m > 12) {
fprintf(stderr, "%s: Invalid month.\n", argv[1]);
return 1;
}
if (d < 1 || d > 31) {
fprintf(stderr, "%s: Invalid day.\n", argv[1]);
return 1;
}
birth.tm_year = y - 1900;
birth.tm_mon = m - 1;
birth.tm_mday = d;
} else {
fprintf(stderr, "%s: Cannot parse birth date.\n", argv[1]);
return 1;
}
mktime(&birth);
printf("Birth date: %04d-%02d-%02d\n", birth.tm_year + 1900, birth.tm_mon + 1, birth.tm_mday);
if (sscanf(argv[2], "%d %9s", &value, suffix) != 2) {
fprintf(stderr, "%s: Invalid interval.\n", argv[2]);
return 1;
}
then = birth;
if (suffix[0] == 'Y' || suffix[0] == 'y')
then.tm_year += value;
else
if ((suffix[0] == 'M' || suffix[0] == 'm') && (suffix[1] == 'O' || suffix[1] == 'o'))
then.tm_mon += value;
else
if (suffix[0] == 'W' || suffix[0] == 'w')
then.tm_mday += value * 7;
else
if (suffix[0] == 'D' || suffix[0] == 'd')
then.tm_mday += value;
else
if (suffix[0] == 'H' || suffix[0] == 'h')
then.tm_hour += value;
else
if ((suffix[0] == 'M' || suffix[0] == 'm') && (suffix[1] == 'I' || suffix[1] == 'i'))
then.tm_min += value;
else
if (suffix[0] == 'S' || suffix[0] == 's')
then.tm_sec += value;
else {
fprintf(stderr, "%s: Invalid interval; unknown suffix '%s'.\n", argv[2], suffix);
return 1;
}
mktime(&then);
nowtime = time(NULL);
now = *(localtime(&nowtime));
printf("Todays date: %04d-%02d-%02d\n", now.tm_year + 1900, now.tm_mon + 1, now.tm_mday);
value = diffdays(&now, &birth);
if (value < 0)
printf("You will be born in %d days.\n", value);
else
if (value > 0)
printf("You are %d days old today.\n", value);
else
printf("Today is your birthday.\n");
if ((then.tm_year > now.tm_year) ||
(then.tm_year == now.tm_year && then.tm_mon > now.tm_mon) ||
(then.tm_year == now.tm_year && then.tm_mon == now.tm_mon && then.tm_mday > now.tm_mday)) {
if (between(&then, &now, &y, &m, &d)) {
fprintf(stderr, "BUG in between().\n");
return 1;
}
printf("Target date: %04d-%02d-%02d, in ", then.tm_year + 1900, then.tm_mon + 1, then.tm_mday);
if (y > 0)
printf("%d years, %d months, %d weeks, and %d days.\n", y, m, d / 7, d % 7);
else
if (m > 0)
printf("%d months, %d weeks, and %d days.\n", m, d / 7, d % 7);
else
if (d > 0)
printf("%d weeks and %d days.\n", d / 7, d % 7);
else
printf("%d days.\n", d);
fflush(stdout);
} else
if (then.tm_year == now.tm_year &&
then.tm_mon == now.tm_mon &&
then.tm_mday == now.tm_mday) {
printf("Target date: %04d-%02d-%02d, today!\n", then.tm_year + 1900, then.tm_mon + 1, then.tm_mday);
fflush(stdout);
} else {
if (between(&now, &then, &y, &m, &d)) {
fprintf(stderr, "BUG in between().\n");
return 1;
}
printf("Target date: %04d-%02d-%02d, was ", then.tm_year + 1900, then.tm_mon + 1, then.tm_mday);
if (y > 0)
printf("%d years, %d months, %d weeks, and %d days ago.\n", y, m, d / 7, d % 7);
else
if (m > 0)
printf("%d months, %d weeks, and %d days ago.\n", m, d / 7, d % 7);
else
if (d > 7)
printf("%d weeks and %d days ago.\n", d / 7, d % 7);
else
printf("%d days ago.\n", d);
fflush(stdout);
}
return 0;
}
If you compile and run it without parameters, it outputs usage to standard error.