The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/local/bin/perl -w

package cal;
use strict;

$cal::num_args = scalar( @ARGV );

if( $cal::num_args == 0 ) {
	( $cal::month, $cal::year ) = (localtime( time ))[ 4, 5 ];
	$cal::month++;
	$cal::year += 1900;
} elsif( $cal::num_args == 1 ) {
	if( $ARGV[ 0 ] !~ /^\d.*$/o ) {
		( $cal::jd, $cal::yr ) = &get_flags( $ARGV[ 0 ] );
		if( $cal::jd && !($cal::yr) ) {
			( $cal::month, $cal::year ) = (localtime( time ))[ 4, 5 ];
			$cal::month++;
			$cal::year += 1900;
		}
		if( $cal::yr ) {
			$cal::year = (localtime( time ))[ 5 ];
			$cal::year += 1900;
		}
	} else {
		$cal::year = &get_year( $ARGV[ 0 ] );
	}
} elsif( $cal::num_args == 2 ) {
	if( $ARGV[ 0 ] !~ /^\d.*$/o ) {
		( $cal::jd, $cal::yr ) = &get_flags( $ARGV[ 0 ] );
		if( $cal::yr ) {
			print "INVALID FLAG.  Can not use -y with year.\n";
			&display_help();
		}
		$cal::year = &get_year( $ARGV[ 1 ] );
	} else {
		$cal::month = &get_month( $ARGV[ 0 ] );
		$cal::year = &get_year( $ARGV[ 1 ] );
	}
} elsif( $cal::num_args == 3 ) {
	( $cal::jd, $cal::yr ) = &get_flags( $ARGV[ 0 ] );
	if( $cal::yr ) {
		print "INVALID FLAG.  Can not use -y with month and year.\n";
		&display_help();
	}
	$cal::month = &get_month( $ARGV[ 1 ] );
	$cal::year = &get_year( $ARGV[ 2 ] );
} else {
	print "TOO MANY ARGUMENTS.\n";
	&display_help();
}

if( $cal::year && $cal::month ) {
	&print_month( $cal::year, $cal::month );
} else {
	my( $i );
	for( $i = 1; $i < 13; $i++ ) {
		&print_month( $cal::year, $i );
		print "\n";
	}
}

sub print_month {
	my( $year, $month ) = @_;
	my( @month ) = &make_month_array( $year, $month );
	my( $title, $length, $diff, $left, $right ) = ();
	my( $day, $end, $x, $size ) = ();
	my( @months ) = ( '', 'January', 'February', 'March', 'April', 'May',
					  'June', 'July', 'August', 'September', 'October',
					  'November', 'December' );
	my( @days ) = ( '  Su', '   M', '  Tu', '   W', '  Th', '   F', '  Sa' );
	$size = scalar( @month );
	$title = "$months[ $month ] $year";
	$length = length( $title );
	$diff = 28 - $length;
	$right = int($diff / 2);
	$left = $diff - $right;
	$title = ' ' x $left."$title".' ' x $right;
	print "$title\n";
	foreach $day (@days) {
		print "$day";
	}
	$end = 0;
	for( $x = 0; $x < $size; $x++ ) {
		if( $end == 0 ) { print "\n"; }
		print "$month[ $x ]";
		$end++;
		if( $end > 6 ) {
			$end = 0;
		}
	}
	print "\n";
}

sub make_month_array {
	my( $year, $month ) = @_;
	my( @month_array, $numdays, $remain, $x, $y ) = ();
	my( $firstweekday ) = &day_of_week_num( $year, $month, 1 );
	$numdays = &days_in_month( $year, $month );
	$y = 1;
	if( $cal::jd ) {
		$y = &day_of_year( $year, $month, 1 );
	}
	for( $x = 0; $x < $firstweekday; $x++ ) { $month_array[$x] = '    '; }
	if( !(($year == 1752) && ($month == 9)) ) {
		for( $x = 1; $x <= $numdays; $x++, $y++ ) { 
			$month_array[$x + $firstweekday - 1] = sprintf( "%4d", $y);
		}
	} else {
		for( $x = 1; $x <= $numdays; $x++, $y++ ) { 
			$month_array[$x + $firstweekday - 1] = sprintf( "%4d", $y);
			if( $y == 2 ) {
				$y = 13;
			}
		}
	}
	return( @month_array );
}

sub day_of_week_num {
	my( $year, $month, $day ) = @_;
	my( $a, $y, $m, $d ) = ();
	$a = int( (14 - $month)/12 );
	$y = $year - $a;
	$m = $month + (12 * $a) - 2;
	if( &is_julian( $year, $month ) ) {
		$d = (5 + $day + $y + int($y/4) + int(31*$m/12)) % 7;
	} else {
		$d = ($day + $y + int($y/4) - int($y/100) + int($y/400) + int(31*$m/12)) % 7;
	}
	return( $d );
}

sub days_in_month {
	my( $year, $month ) = @_;
	my( @month_days ) = ( 0,31,28,31,30,31,30,31,31,30,31,30,31 );
	if( ($month == 2) && (&is_leap_year( $year )) ) {
		$month_days[ 2 ] = 29;
	} elsif ( ($year == 1752) && ($month == 9) ) {
		$month_days[ 9 ] = 19;
	}
	return( $month_days[ $month ] );
}

sub day_of_year {
	my( $year, $month, $day ) = @_;
	my( @days ) = ( 0,31,59,90,120,151,181,212,243,273,304,334,365 );
	my ( $ly, $i ) = ();
	$ly = 0;
	if( (&is_leap_year( $year )) && ($month > 2) ) {
		$ly = 1;
	}
	if( $year == 1752 ) {
		for( $i = 9; $i < 13; $i++ ) {
			$days[ $i ] = $days[ $i ] - 11;
		}
	}
	return( $days[ $month - 1 ] + $day + $ly )
}

sub is_leap_year {
	my( $year ) = @_;
	my( $bool ) = 0;
	if( &is_julian( $year, 1 ) ) {
		if( $year % 4 == 0 ) {
			$bool = 1;
		}
	} else {
		if( (($year % 4 == 0) && ($year % 100 != 0)) || ($year % 400 == 0) ) {
			$bool = 1;
		}
	}
	return( $bool );
}

sub is_julian {
	my( $year, $month ) = @_;
	my( $bool ) = 0;
	if( ($year < 1752) || ($year == 1752 && $month <= 9) ) {
		$bool = 1;
	}
	return( $bool );
}

sub get_month {
	my( $month ) = @_;
	if( $month < 1 || $month > 12 || $month !~ /^\d+$/o ) {
		print "INVALID MONTH ENTERED.\n";
		&display_help();
	}
	return( $month );
}

sub get_year {
	my( $year ) = @_;
	if( $year < 1 || $year > 9999 || $year !~ /^\d+$/o ) {
		print "INVALID YEAR ENTERED.\n";
		&display_help();
	}
	return( $year );
}

sub get_flags {
	my( $flags ) = @_;
	my( $jd, $yr ) = ();
	if( ($flags =~ /.[^jy\?]/o) || ($flags !~ /^-\w+/o) ) {
		print "INVALID FLAGS ENTERED.\n";
		&display_help();
	}
	if( $flags =~ /j/o ) { $jd = 1;	}
	if( $flags =~ /y/o ) { $yr = 1; }
	if( $flags =~ /\?/o ) { &display_help(); }
	return( $jd, $yr );
}

sub display_help {
	print <<END_HELP;
cal.pl - part of the Perl Power Tools
cal.pl [-jy?] [[month] year]

    -j : Display calendar using julian days, where each day
         number is the day of the year.
    -y : Display entire year calendar for the current year.
    -? : Display this help information.
 month : The month of the entered year for which to display
         a calendar.  Valid months are 1 through 12.
  year : The year for which to display the calendar.  If no
         month is entered, will display the calendar for the
         entire entered year.  Valid years are 1 through 9999.

If no arguments are passed, prints the calendar for the current
month and year.
END_HELP
	exit;
}

# POD SECTION #

=head1 NAME

cal.pl - member of the Perl Power Tools.

=head1 SYNOPSIS

cal.pl [-jy?] [[month] year]

If no arguments are supplied, the current month will be displayed.  If only the
-j flag is passed as an argument, the current month will be displayed with
julian day numbering (see below).

=head2 FLAGS

Below is a list of the flags that can be passed to cal.pl, and their meanings.

=over 4

=item -j

displays the requested date using julian days for the numbers.  That is, each
day number is the day of the year for that day.  Thus, in non leap-years,
December 31 is day 365.

=item -y

displays the calendar for the current year.  Note this is mutually
exclusive with
the -n flag above.

=item -?

displays information and usage instructions for the program.  If this flag
is included
in any flag combination, it overrides all other flags.

=back

=head2 MONTH/YEAR

If only one number is supplied, this number will be used as the year.  If
two numbers
are supplied, the first will be the month, and the second will be the year.
 Valid
months are 1 through 12, and valid years are 1 through 9999.

B<NOTE:> dates must be fully defined.  Entering a year of 99 means 99 AD,
not 1999 AD.

=head1 DESCRIPTION

cal.pl generates calendars from 1 AD through 9999 AD, in the following format.

S<         March 1999         
  Su   M  Tu   W  Th   F  Sa
       1   2   3   4   5   6
   7   8   9  10  11  12  13
  14  15  16  17  18  19  20
  21  22  23  24  25  26  27
  28  29  30  31
>

It takes into account the conversion from the Julian Calendar to the
Gregorian Calendar
in September, 1752.  Note that this was the date used in the UK and all of
her colonies
(including the U.S.).  Thus, in September, 1752, there were only 19 days.
Eleven days
had to be removed from the calendar to make up for inaccuracies in the
Julian Calendar.
So, September 2, 1752 was followed by September 14, 1752 in the UK and all
colonies,
as shown below.

S<       September 1752       
  Su   M  Tu   W  Th   F  Sa
           1   2  14  15  16
  17  18  19  20  21  22  23
  24  25  26  27  28  29  30
>

The Julian calendar has a leap year every 4 years.  The Gregorian calendar has
a leap year based on the following:  If the year is evenly divisible by
400, it is a leap
year, else, if the year is evenly divisible by 4, and not evenly divisible
by 100, it is
a leap year.  All other years are not leap years.  There is some contention
over whether
4 AD was a leap year or not.  Not all the experts agree, as there was a
counting error
in the beginning of the Julian calendar with regards to leap years that had
to be
corrected, and thus some years that would normally have been leap years
were not.  This
calendar assumes that 4 AD was indeed a leap year.

=head1 FURTHER READING

The calculations used in this calendar, as well as information on when the
calendar was
switched over, and the implications thereof, were obtained from information
found at
the following URL.  If you would like to find out more about calendars, I
highly
suggest taking a look.

F<URL: http://www.pip.dknet.dk/~c-t/calendar.html>

=head1 AUTHOR

Michael E. Schechter
mschechter@earthlink.net

=head1 COPYRIGHT INFORMATION

This application is distributed as part of the Perl Power Tools.  Feel free
to copy,
modify, delete, or whatever you would like with this file, under the
information
contained in the GNU GPL.

=cut