#!/usr/local/bin/perl -w

# 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
#
#/

my ($given_time,@inp,$year,$daynumber,$day,$x,$week);

use POSIX;
use integer;

if ($#ARGV == 0) {
	@inp=localtime($ARGV[0]);
} elsif ($#ARGV == 2) {
	$inp[3] = $ARGV[0];
	$inp[4] = $ARGV[1]-1;

	# Process the year number
	# If xx < 50, assume 20xx
	# If 50 <= xx < 100, assume 19xx
	# If xx >= 100, assume xxxx

	if ($ARGV[2] < 50) {
		$inp[5] = $ARGV[2]+100;
	} elsif ($ARGV[2] < 100) {
		$inp[5] = $ARGV[2];
	} else {
		$inp[5] = $ARGV[2]-1900;
	}
	$inp[2]=12;
	$inp[1]=0;
	$inp[0]=0;
	$inp[6]=0;
	$inp[7]=0;
	$inp[8]=-1;
} elsif ($#ARGV == -1) {
	@inp=localtime(time);
} else {
	printf STDERR "Syntax: %s [day month year] | [seconds_since_epoch]\nReturns week number according to ISO8601:1988\nWritten by ADS Benham\n",$ARGV;
	exit 1;
}

# POSIX::mktime seems loathe to fill in the weekday, so do a localtime()
# to force the weekday to be right.

$given_time=POSIX::mktime(@inp);
@inp=localtime($given_time);

$year=$inp[5]+1900;
$daynumber=$inp[7] + 1;	# day number in year ( range 1-366)
$day=$inp[6];		# 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) and ($x%7 == 5)) or ($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

	if ($x%7 == 1) {
		$week = 52;
	} elsif ($x%7 == 3) {
		$week = 53;
	} elsif ($x%7 == 2) {
		if (&is_leap_year($year)) {
			# Current year is a leap year
			$week = 52;
		} else {
			if (&is_leap_year($year-1)) {
				# Previous year is a leap year
				$week = 53;
			} else {
				$week = 52;
			}
		}
	} else {
		print STDERR "Logic warning - shouldn't get here\n";
		$week=-999;
	}
	$year--;
}

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


sub is_leap_year
# Return 1 if leap year, 0 if not
# Note: year is 4 digit number
{
	if ($_[0] % 400 == 0) {
		return 1;
	}
	if ($_[0] % 100 == 0) {
		return 0;
	}
	if ($_[0] % 4 == 0) {
		return 1;
	}
	return 0;
}

