/* Code to print ISO 8601:1988 week number for 'any' date.
 * Uses Unix time functions, so works for 1970 - 2038.
 *
 * A.D.S.Benham, June 1998
 *
 */

/* If called with no arguments, prints week number of current date/time
 *
 * Can call with one argument, the seconds since the epoch
 *
 * Can call with three arguments - day month year
 *
 */

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

int is_leap_year(int);

int main(int argc,char **argv)
{
	time_t given_time;
	struct tm in;
	struct tm *inp = &in;
	int year,daynumber,day,mont,mday,x,week,lastyear;

	if (argc == 2) {
		given_time=atol(argv[1]);
		inp=localtime(&given_time);
	} else if (argc == 4) {
		inp->tm_mday=atoi(argv[1]);
		inp->tm_mon=atoi(argv[2])-1;

		/* Process the year number
		 * If xx < 50, assume 20xx
		 * If 50 <= xx < 100, assume 19xx
		 * If xx >= 100, assume xxxx
		 */
		if (atoi(argv[3]) < 50) 
			inp->tm_year=atoi(argv[3])+100;
		else if (atoi(argv[3]) < 100)
			inp->tm_year=atoi(argv[3]);
		else 
			inp->tm_year=atoi(argv[3])-1900;
		inp->tm_hour=0;
		inp->tm_min=0;
		inp->tm_sec=0;
		inp->tm_isdst=-1;
	} else if (argc == 1) {
		time(&given_time);
		inp=localtime(&given_time);
	} else {
		fprintf(stderr,"Syntax: %s [day month year] | [seconds_since_epoch]\nReturns week number according to ISO8601:1988\nWritten by ADS Benham\n",argv[0]);
		exit(1);
	}

	given_time=mktime(inp);

	year=inp->tm_year+1900;
	daynumber=inp->tm_yday + 1;	/* day number in year ( range 1-366) */
	day=inp->tm_wday;		/* weekday, sunday=0 */
	
	if (day == 0) day=7;		/* Make Sunday day 7 (day range 1-7) */

	x=daynumber - day;
	week=x/7+1;			/* Get 'raw' week number */

	/* The raw week number now needs to be worked on. ISO8601:1988 tells
	 * us that week 1 of a year is the one which includes the first Thursday
	 * of the year, or equivalently the one which includes 4 January.
	 * ISO8601:1988 also tells us that the week begins on Monday.
	 */

	if (x<0) week=0;

	if (x%7 < 0) x+=7;		/* x%7 is the date of the 1st Sunday in the year */

	if (x%7 > 3) week++;

	if (week==53) {			/* Week 53 is usually bogus result of calc */
		if ((is_leap_year(year) && x%7 == 5) || x%7 == 4) {
			/* Really is a week 53 under these conditions */
		} else {
			week=1;
			year++;
		}
	}
	if (week < 1) {
		/* Need to return the last week of the previous year. This is either 52 or 53.
		 * To get here, x%7 is either 1, 2, or 3 
		 * If it's 1, then the last week of the previous year is guaranteed to be week 52
		 * If it's 3, then the last week of the previous year is guaranteed to be week 53
		 * If it's 2, then it depends on:
		 *    1. whether this year is a leap year: if so, then the last week of the
		 *       previous year was week 52
		 *    2. if this year isn't a leap year, was the previous year was a leap year ?
		 *       if it was, then the last week of the previous year was week 53
		 *       if it wasn't, then the last week of the previous year was week 52
		 */
		switch (x%7) {
			case 1:
				week = 52;
				break;
			case 3:
				week = 53;
				break;
			case 2:
				if (is_leap_year(year)) {
					/* Current year is a leap year */
					week = 52;
				} else {
					lastyear = year-1;
					if (is_leap_year(lastyear)) {
						/* Previous year is a leap year */
						week = 53;
					} else {
						week = 52;
					}
				}
				break;
			default:
				fprintf(stderr,"Logic warning - shouldn't get here\n");
				week=-999;
				break;
		}
		year--;
	}

	printf("%dW%.2d\n",year,week);

	return 0;
}

int is_leap_year(int year)
/* Return 1 if leap year, 0 if not
 * Note: year is a 4 digit number (i.e. includes century)
 */
{
	if (year % 400 == 0)
		return 1;
	if (year % 100 == 0)
		return 0;
	if (year % 4 == 0)
		return 1;
	return 0;
}

