aboutsummaryrefslogblamecommitdiff
path: root/wcal.c
blob: b555be8779df5b28715b165ef4f8872a3761dfed (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
















                                                                           
                  
                  

                   

                   
                                             
 





                                                 




                                                


























                                                            

                                                             






                                                             
                                                             




                                            
                                                                                  
                                  
                                                                          
                             

                                 

 
    
                                                   
 
                    





                                               
                                  




                                                                      
                                            

                                                                       
                                        
                                                    
















                                                                  

                 

 






                                                                      


                            

                                        
 




                                   
              

               
               

              
                                                          



                                           
                                           

                                           
                                                                   
                                  

                 


                          
                                     
                                                   






                              
                                           
                           


                                                              
                                       
                                                     

         
                                  
 
                                             
                                                     
                                             
 
                                       
 





                                













                                           
 


                                                                                 
                                                

                                        
                                                

                                         
 


                                                               


                                                     
                                 

                                                                 


                                       
 
                    
                                          

                                                                  
                                                                    
 
                             
                                                             
                                 



                             
/*
 * wcal - ISO weekly calendar
 *
 * To the extent possible under law, Leah Neukirchen <leah@vuxu.org>
 * has waived all copyright and related or neighboring rights to this work.
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

/*
 * -1 show current month (default)
 * -3 show three months
 * -y show whole year
 * -c show current week only
 * -i print infinite calendar
 * -d YYYY-MM-DD different value for today
 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>

int flag1, flag3, flagc, flagC, flagi, flagy;

const char monthname[13][4] = {
	"   ",
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

const char wdayname[8][3] = {
	"  ",
	"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"
};

/* 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 *isoyear)
{
	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 = *isoyear = 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, isoyear);
	else if (*isoweek == 53 &&
	    ymd2jd(y+1, 1, 1) + (7 - (y+y/4-y/100+y/400+3)%7 - 4) <= jd) {
		*isoweek = 1;
		*isoyear = y + 1;
	}
}

void
parse_isodate(char *optarg, int *y, int *m, int *d)
{
	*m = *d = 1;

	if (isdigit(optarg[0]) &&
	    isdigit(optarg[1]) &&
	    isdigit(optarg[2]) &&
	    isdigit(optarg[3]) &&
	    (!optarg[4] || optarg[4] == '-')) {
		*y = atoi(optarg);

		if (!optarg[4]) {
			flagy = 1;
		} else if (isdigit(optarg[5]) && isdigit(optarg[6]) &&
		    (!optarg[7] || optarg[7] == '-')) {
			*m = atoi(optarg+5);

			if (isdigit(optarg[8]) && isdigit(optarg[9]) &&
			    !optarg[10])
				*d = atoi(optarg+8);
		} else if (optarg[5] == 'W' &&
		    isdigit(optarg[6]) && isdigit(optarg[7]) &&
		    (!optarg[8] || optarg[8] == '-')) {
			int w = atoi(optarg+6);
			int wd = 1;

			if (isdigit(optarg[9]) && !optarg[10])
				wd = atoi(optarg+9);

			int _;
			int corr;
			long jan4 = ymd2jd(*y, 1, 4);
			jd2ymdwi(jan4, &_, &_, &_, &corr, &_, &_);
			corr += 3;

			jd2ymdwi(jan4 - 4 + 7*w + wd - corr,
			    y, m, d, &_, &_, &_);
		}
	}
}

void
usage()
{
	fprintf(stderr, "Usage: wcal [-13yci] [-d YYYY[-MM[-DD]]]\n");
	exit(1);
}

int
main(int argc, char *argv[])
{
	time_t now = time(0);
	struct tm *tm = localtime(&now);

	int y = tm->tm_year + 1900;
	int m = tm->tm_mon + 1;
	int d = tm->tm_mday;
	int w;
	int i;
	int g;
	int cw;
	int ci;
	int cg;

	int c;
	while ((c = getopt(argc, argv, "13cCid:y")) != -1)
		switch (c) {
		case '1': flag1 = 1; break;
		case '3': flag3 = 1; break;
		case 'c': flagc = 1; break;
		case 'C': flagC = 1; break;
		case 'y': flagy = 1; break;
		case 'i': flagi = 1; break;
		case 'd': parse_isodate(optarg, &y, &m, &d); break;
		case '?': usage();
		}

	if (argc > optind)
		usage();

	long today = ymd2jd(y, m, d);
	jd2ymdwi(today, &y, &m, &d, &cw, &ci, &cg);

	int max_weeks = 1;
	int max_months = 0;

	if (flagc) {
		max_weeks = 1;
	} else if (flag3) {
		d = 1; m--; max_weeks = 14;
	} else if (flagy) {
		d = 1; m = 1; max_months = 12; max_weeks = 53;
	} else if (flagi) {
		d = 1;
	} else { /* flag1 is default */
		d = 1; max_months = 1; max_weeks = 6;
	}

	long jd = ymd2jd(y, m, d);

	jd2ymdwi(jd, &y, &m, &d, &w, &i, &g);
	jd -= (w - 1);   // skip back to last monday.
	jd2ymdwi(jd, &y, &m, &d, &w, &i, &g);

	int color = isatty(1) || flagC;

	if (color)
		printf("\e[4m");
	printf("Wk");
	if (color)
		printf("\e[0m");
	printf("      ");
	if (color)
		printf("\e[4m");
	for (int wd = 1; wd <= 7; wd++) {
		if (color && wd == cw)
			printf("\e[7m");
		printf("%s", wdayname[wd]);
		if (color && wd == cw)
			printf("\e[27m");
		if (wd < 7)
			printf(" ");
	}
	if (color)
		printf("\e[0m");
	printf("\n");

	for (int weeks = 0, months = 0;
	     flagi || (max_months && months < max_months) || (weeks < max_weeks);
	     weeks++) {
		if (color && i == ci && g == cg)
			printf("\e[7m");
		printf("%02d", i);
		if (color && i == ci && g == cg)
			printf("\e[27m");
		printf(" ");

		int ey, em, ed, ew, ei, eg;
		/* end of week */
		jd2ymdwi(jd + 6, &ey, &em, &ed, &ew, &ei, &eg);

		if (ed <= 7 || weeks == 0) {
			printf("%s ", monthname[em]);
			months++;
		} else if ((em == 1 && ed <= 14) || weeks == 1) {
			printf("%04d", ey);
		} else {
			printf("    ");
		}

		do {
			printf(" %s%2d%s",
			    color && (jd == today) ? "\e[7m" : "",
			    d,
			    color && (jd == today) ? "\e[27m" : "");

			jd++;
			jd2ymdwi(jd, &y, &m, &d, &w, &i, &g);
		} while (w != 1);

		printf("\n");
	}
}