about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--wcal.c151
1 files changed, 90 insertions, 61 deletions
diff --git a/wcal.c b/wcal.c
index 294002b..8ee635d 100644
--- a/wcal.c
+++ b/wcal.c
@@ -15,7 +15,6 @@
  * -d YYYY-MM-DD different value for today
  */
 
-#define _XOPEN_SOURCE 700
 #include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -24,33 +23,82 @@
 
 int flag1, flag3, flagc, flagC, flagi, flagy;
 
+const char monthname[13][4] = {
+	"   ",
+	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* all args are 1-indexed */
+long
+ymd2jd(int year, int month, int day)
+{
+	month--;
+	if (month >= 12 || month < 0) {
+		int adj = month / 12;
+		month %= 12;
+		if (month < 0) {
+			adj--;
+			month += 12;
+		}
+		year += adj;
+	}
+	month++;
+
+	if (month < 3) {
+		year--;
+		month += 12;
+	}
+	year += 4800;
+	return day + (153*(month-3)+2)/5 +
+	    365*year + year/4 - year/100 + year/400 - 32045;
+}
+
+/* Mon = 1, Sun = 7 */
+void
+jd2ymdwi(long jd, int *year, int *month, int *day, int *wday, int *isoweek)
+{
+	int _;
+	long e = 4*(jd+1401+(4*jd+274277)/146097*3/4-38) + 3;
+	long h = e%1461/4*5 + 2;
+
+	*day = h%153/5 + 1;
+	int m = *month = (h/153+2)%12 + 1;
+	int y = *year = e/1461 - 4716 + (14-m)/12;
+	int w = *wday = jd%7 + 1;
+
+	int yday = jd - ymd2jd(y, 1, 1) + 1;
+	*isoweek = (yday - w + 10) / 7;
+	if (*isoweek < 1)
+		jd2ymdwi(ymd2jd(y - 1, 12, 31), &_, &_, &_, &_, isoweek);
+	else if (*isoweek == 53 &&
+	    ymd2jd(y+1, 1, 1) + (7 - (y+y/4-y/100+y/400+3)%7 - 4) <= jd)
+		*isoweek = 1;
+}
+
 void
-parse_isodate(char *optarg, struct tm *tm)
+parse_isodate(char *optarg, int *y, int *m, int *d)
 {
-	tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
-	tm->tm_mday = 1;
-	tm->tm_mon = 0;
+	*m = *d = 1;
 
 	if (isdigit(optarg[0]) &&
 	    isdigit(optarg[1]) &&
 	    isdigit(optarg[2]) &&
 	    isdigit(optarg[3]) &&
 	    (!optarg[4] || optarg[4] == '-')) {
-		tm->tm_year = atoi(optarg) - 1900;
+		*y = atoi(optarg);
 
 		if (!optarg[4]) {
 			flagy = 1;
 		} else if (isdigit(optarg[5]) && isdigit(optarg[6]) &&
 		    (!optarg[7] || optarg[7] == '-')) {
-			tm->tm_mon = atoi(optarg+5) - 1;
+			*m = atoi(optarg+5);
 
 			if (isdigit(optarg[8]) && isdigit(optarg[9]) &&
 			    (!optarg[10] || optarg[10] == '-'))
-				tm->tm_mday = atoi(optarg+8);
+				*d = atoi(optarg+8);
 		}
 	}
-
-	mktime(tm);
 }
 
 int
@@ -58,17 +106,12 @@ main(int argc, char *argv[])
 {
 	time_t now = time(0);
 	struct tm *tm = localtime(&now);
-	struct tm tm2 = {
-		.tm_year = tm->tm_year,
-		.tm_mon = tm->tm_mon,
-		.tm_mday = tm->tm_mday
-	};
 
-	setenv("TZ", "", 1);
-	tzset();
-
-	now = mktime(&tm2);
-	tm = gmtime(&now);
+	int y = tm->tm_year + 1900;
+	int m = tm->tm_mon + 1;
+	int d = tm->tm_mday;
+	int w;
+	int i;
 
 	int c;
 	while ((c = getopt(argc, argv, "13cCid:y")) != -1)
@@ -79,14 +122,10 @@ main(int argc, char *argv[])
 		case 'C': flagC = 1; break;
 		case 'y': flagy = 1; break;
 		case 'i': flagi = 1; break;
-		case 'd': parse_isodate(optarg, tm); break;
+		case 'd': parse_isodate(optarg, &y, &m, &d); break;
 		}
 
-	tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
-
-	int today_mday = tm->tm_mday;
-	int today_mon = tm->tm_mon;
-	int today_year = tm->tm_year;
+	long today = ymd2jd(y, m, d);
 
 	int max_weeks = 1;
 	int max_months = 0;
@@ -94,21 +133,20 @@ main(int argc, char *argv[])
 	if (flagc) {
 		max_weeks = 1;
 	} else if (flag3) {
-		tm->tm_mday = 1; tm->tm_mon--; max_weeks = 14;
+		d = 1; m--; max_weeks = 14;
 	} else if (flagy) {
-	        tm->tm_mday = 1; tm->tm_mon = 0; max_months = 12; max_weeks = 53;
-        } else if (flagi) {
-		tm->tm_mday = 1;
+		d = 1; m = 1; max_months = 12; max_weeks = 53;
+	} else if (flagi) {
+		d = 1;
 	} else { /* flag1 is default */
-	        tm->tm_mday = 1; max_months = 1; max_weeks = 6;
+		d = 1; max_months = 1; max_weeks = 6;
 	}
 
-	mktime(tm);
+	long jd = ymd2jd(y, m, d);
 
-	while (tm->tm_wday != 1) {
-		tm->tm_mday--;
-		mktime(tm);
-	}
+	jd2ymdwi(jd, &y, &m, &d, &w, &i);
+	jd -= (w - 1);   // skip back to last monday.
+	jd2ymdwi(jd, &y, &m, &d, &w, &i);
 
 	int color = isatty(1) || flagC;
 
@@ -119,38 +157,29 @@ main(int argc, char *argv[])
 	for (int weeks = 0, months = 0;
 	     flagi || (max_months && months < max_months) || (weeks < max_weeks);
 	     weeks++) {
-		char buf[8];
-		strftime(buf, sizeof buf, "%V", tm);
-		printf("%s ", buf);
-
-		tm->tm_mday += 6;
-		mktime(tm);
-		if (tm->tm_mday <= 7 || weeks == 0) {
-			strftime(buf, sizeof buf, "%b", tm);
-			printf("%s ", buf);
+		printf("%02d ", i);
+
+		int ey, em, ed, ew, ei;
+		jd2ymdwi(jd + 6, &ey, &em, &ed, &ew, &ei);   /* end of week */
+
+		if (ed <= 7 || weeks == 0) {
+			printf("%s ", monthname[em]);
 			months++;
-		} else if ((tm->tm_mon == 0 && tm->tm_yday < 14) || weeks == 1) {
-			printf("%04d", tm->tm_year + 1900);
+		} else if ((em == 1 && ed <= 14) || weeks == 1) {
+			printf("%04d", ey);
 		} else {
 			printf("    ");
 		}
-		tm->tm_mday -= 6;
-		mktime(tm);
-
-		for (int i = 0; i < 7; i++) {
-			int today =
-			    tm->tm_mday == today_mday &&
-			    tm->tm_mon == today_mon &&
-			    tm->tm_year == today_year;
 
+		do {
 			printf(" %s%2d%s",
-			    color && today ? "\e[7m" : "",
-			    tm->tm_mday,
-			    color && today ? "\e[0m" : "");
+			    color && (jd == today) ? "\e[7m" : "",
+			    d,
+			    color && (jd == today) ? "\e[0m" : "");
 
-			tm->tm_mday++;
-			mktime(tm);
-		}
+			jd++;
+			jd2ymdwi(jd, &y, &m, &d, &w, &i);
+		} while (w != 1);
 
 		printf("\n");
 	}