#!/usr/bin/perl
=begin metadata
Name: cal
Description: displays a calendar and the date of Easter
Author: Michael E. Schechter, mschechter@earthlink.net
License: gpl
=end metadata
=cut
package
cal; # hide from PAUSE
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( $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";
for my $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;
}
=head1 NAME
cal - displays a calendar and the date of easter
=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 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