C Program to find day of week given date
Solution 1:
As reported also by Wikipedia, in 1990 Michael Keith and Tom Craver published an expression to minimise the number of keystrokes needed to enter a self-contained function for converting a Gregorian date into a numerical day of the week.
The expression does preserve neither y
nor d
, and returns a zero-based index representing the day, starting with Sunday, i.e. if the day is Monday the expression returns 1
.
A code example which uses the expression follows:
int d = 15 ; //Day 1-31
int m = 5 ; //Month 1-12`
int y = 2013 ; //Year 2013`
int weekday = (d += m < 3 ? y-- : y - 2, 23*m/9 + d + 4 + y/4- y/100 + y/400)%7;
The expression uses the comma operator, as discussed in this answer.
Enjoy! ;-)
Solution 2:
A one-liner is unlikely, but the strptime function can be used to parse your date format and the struct tm
argument can be queried for its tm_wday
member on systems that modify those fields automatically (e.g. some glibc implementations).
int get_weekday(char * str) {
struct tm tm;
memset((void *) &tm, 0, sizeof(tm));
if (strptime(str, "%d-%m-%Y", &tm) != NULL) {
time_t t = mktime(&tm);
if (t >= 0) {
return localtime(&t)->tm_wday; // Sunday=0, Monday=1, etc.
}
}
return -1;
}
Or you could encode these rules to do some arithmetic in a really long single line:
- 1 Jan 1900 was a Monday.
- Thirty days has September, April, June and November; all the rest have thirty-one, saving February alone, which has twenty-eight, rain or shine, and on leap years, twenty-nine.
- A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.
EDIT: note that this solution only works for dates after the UNIX epoch (1970-01-01T00:00:00Z).
Solution 3:
Here's a C99 version based on wikipedia's article about Julian Day
#include <stdio.h>
const char *wd(int year, int month, int day) {
/* using C99 compound literals in a single line: notice the splicing */
return ((const char *[]) \
{"Monday", "Tuesday", "Wednesday", \
"Thursday", "Friday", "Saturday", "Sunday"})[ \
( \
day \
+ ((153 * (month + 12 * ((14 - month) / 12) - 3) + 2) / 5) \
+ (365 * (year + 4800 - ((14 - month) / 12))) \
+ ((year + 4800 - ((14 - month) / 12)) / 4) \
- ((year + 4800 - ((14 - month) / 12)) / 100) \
+ ((year + 4800 - ((14 - month) / 12)) / 400) \
- 32045 \
) % 7];
}
int main(void) {
printf("%d-%02d-%02d: %s\n", 2011, 5, 19, wd(2011, 5, 19));
printf("%d-%02d-%02d: %s\n", 2038, 1, 19, wd(2038, 1, 19));
return 0;
}
By removing the splicing and spaces from the return
line in the wd() function, it can be compacted to a 286 character single line :)
Solution 4:
This is my implementation. It's very short and includes error checking. If you want dates before 01-01-1900, you could easily change the anchor to the starting date of the Gregorian calendar.
#include <stdio.h>
int main(int argv, char** arv) {
int month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char* day[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
int d, m, y, i;
printf("Fill in a date after 01-01-1900 as dd-mm-yyyy: ");
scanf("%d-%d-%d", &d, &m, &y);
// correction for leap year
if (y % 4 == 0 && (y % 100 != 0 || y % 400 == 0))
month[1] = 29;
if (y < 1900 || m < 1 || m > 12 || d < 1 || d > month[m - 1]) {
printf("This is an invalid date.\n");
return 1;
}
for (i = 1900; i < y; i++)
if (i % 4 == 0 && (i % 100 != 0 || i % 400 == 0))
d += 366;
else
d += 365;
for (i = 0; i < m - 1; i++)
d += month[i];
printf("This is a %s.\n", day[d % 7]);
return 0;
}