package TripodDate;

#################
# TripodDate.pm #
##############################################################################
# TripodDate is a module for dealing with dates. You can fetch the current #
# date (or part of it), modify the format of a date, see which of two dates #
# is more recent, and so on. #
# #
# The functions you may want to use are: #
# * currentDate(); * convertMonthNameToInt(); #
# * currentDay(); * convertIntToMonthName(); #
# * currentMonth(); * dateIsPast(); #
# * currentYear(); * addNMonthsToDate(); #
# * massageDate(); * addNDaysToDate(); #
# * convertYearToYYYY(); * isValidDate(); #
# #
# The first four functions, currentDate(), currentDay(), currentMonth(), and #
# currentYear(), are very similar. None of them take any arguments, and #
# each returns a single string, like this: #
# #
# $todays_date = $DATE->currentDate(); #
# #
# Note that all of the above functions return their information in #
# mm/dd/yyyy format - so if today is July 16th, 1999, currentDate() would #
# return '08/16/1999', and currentMonth() would return '08'. #
# #
# massageDate takes a date which may not be in mm/dd/yyyy format, and #
# returns it in mm/dd/yyyy format. So, you could use it like this: #
# #
# $unformatted_date = '9/6/99'; #
# $formatted_date = $DATE->massageDate($unformatted_date); #
# #
# $formatted_date would then be equal to '09/06/1999'. Note that #
# massageDate() can handle dates using dashes or slashes, but the date needs #
# to be numerical, and it needs to be in the standard U.S. ordering of #
# month/day/year. #
# #
# convertYearToYYYY() is another date formatter. It just takes a year and #
# converts it into a four-digit year. It should work well for years between #
# 1931 and 2030. Just say this to convert $two_digit_year to a #
# $four_digit_year: #
# #
# $two_digit_year = '78'; #
# $four_digit_year = $DATE->convertYearToYYYY(); #
# #
# This will make $four_digit_year equal '1978'. #
# #
# convertMonthNameToInt() and convertIntToMonthName() are sister functions, #
# used to convert back and forth between a month name (like 'January' or #
# 'jan') and that month's number (in this case, '1'). Use them like this: #
# #
# $month_name = 'February'; #
# $month_number = $DATE->convertMonthNameToInt($month_name); #
# $month_name_again = $DATE->convertIntToMonthName($month_number); #
# #
# dateIsPast() checks to see if a given date has already occurred. The date #
# should be in month/day/year format. It returns 1 if the date is past, 0 #
# otherwise. Use it like this: #
# #
# $date_in_question = '1/1/2000'; #
# if ($DATE->dateIsPast($date_in_question)) { #
# print "Y2K has already occurred.\n"; #
# } else { #
# print "Y2K hasn't happened yet - there's still time to prepare!\n"; #
# } #
# #
# Our last functions are another pair: addNMonthsToDate() and #
# addNDaysToDate(). Both require a date and a number; that number is added #
# to the date to produce a new date. Note that the number can be negative, #
# so you can subtract as well. For example: #
# #
# $moon_walk_date = '07/20/69'; #
# $five_days_later = $DATE->addNMonthsToDate($moon_walk_date, '3'); #
# $ten_months_earlier = $DATE->addNMonthsToDate($moon_walk_date, '-10'); #
##############################################################################

($CMONTH,$CDAY,$CYEAR) = _getCurrentDate();

%MONTH_LENGTH = ('1' => 31,
'2' => 28,
'3' => 31,
'4' => 30,
'5' => 31,
'6' => 30,
'7' => 31,
'8' => 31,
'9' => 30,
'10' => 31,
'11' => 30,
'12' => 31);

sub new {
my $class = shift;
my $self = {};
bless $self, $class;
return $self;
}

# Preferred over todaysDate()
sub currentDate {
return "$CMONTH/$CDAY/$CYEAR";
}

sub todaysDate {
return "$CMONTH/$CDAY/$CYEAR";
}

sub currentMonth {
return $CMONTH;
}
sub currentDay {
return $CDAY;
}
sub currentYear {
return $CYEAR;
}

sub massageDate {
my $self = shift;
my $date = shift;
my($ref,$month,$day,$year);

$ref = ref($date);

if ($ref eq 'ARRAY') {
# We're passed a reference to an array.
($month,$day,$year) = @$date;
} else {
if ($date =~ /\//) {
($month,$day,$year) = split('/',$date);
} else {
($month,$day,$year) = split('-',$date);
}
$year = $CYEAR
if (! defined($year));
}

# Put single digits in 01 format.
for ($month,$day) {
s/^(\d)$/0$1/;
}
$year = convertYearToYYYY(undef,$year);

if ($ref eq 'ARRAY') {
return ($month,$day,$year);
} else {
return "$month/$day/$year";
}
}

sub convertYearToYYYY {
my $self = shift;
my $year = shift;

if (! defined($year)) {
return 0;
}

if ($year > 100) {
# Year already includes century.
return $year;
}
if ($year > 30 && $year < 100) {
$year += 1900;
} else {
$year += 2000;
}

return $year;
}

# Pass a valid month name and this method will return the corresponding
# value of 1-12. When passed an invalid month name, this method returns 0.
sub convertMonthNameToInt {
my($self,$month_name) = @_;
my(@month_names) = qw(jan feb mar apr may jun
jul aug sep oct nov dec);
my($temp_month_name,$month_int);

$month_int = 1;
for $temp_month_name (@month_names) {
if ($month_name =~ /^$temp_month_name/i) {
return $month_int;
} else {
$month_int++;
}
}

# Invalid month name passed!
return 0;
}

# Pass a value of 1-12 and this method will return the corresponding
# three-letter month name. When passed an invalid value, this returns ''.
sub convertIntToMonthName {
my($self,$int) = @_;
my(@month_names) = qw(jan feb mar apr may jun
jul aug sep oct nov dec);

if ( (1 <= $int) && ($int <= 12) ) {
return $month_names[$int-1];
}

# invalid integer passed!
else {
return '';
}
}

sub dateIsPast {
# Pre: Pass well-formed test date and optional ref date (default current).
# Post: If test date is before reference date, return true.
my $self = shift;
my($date,$ref_date) = @_;
my($emonth,$eday,$eyear,$rmonth,$rday,$ryear);

if (! defined($date)) {
return 0;
}

if (defined($ref_date)) {
($rmonth,$rday,$ryear) = split('/',$ref_date);
unless ((length $ryear) == 4)
{
$ryear = $self->convertYearToYYYY($ryear);
}
} else {
$rmonth = $CMONTH;
$rday = $CDAY;
$ryear = $CYEAR;
}

($emonth,$eday,$eyear) = split('/',$date);
unless((length $ryear) == 4)
{
$eyear = $self->convertYearToYYYY($eyear);
}

if (($eyear < $ryear) ||
($eyear == $ryear && $emonth < $rmonth) ||
($eyear == $ryear && $emonth == $rmonth && $eday <= $rday)) {
return 1;
} else {
return 0;
}
}

sub addNMonthsToDate {
my $self = shift;
my($start_date,$duration_in_months) = @_;
my($smonth,$sday,$syear,$emonth,$eday,$eyear,@month_last_days);

# If no valid duration given, cut out now.
$duration_in_months =~ s/^(\d+).*/$1/;
if ($duration_in_months eq '') {
warn 'Invalid duration passed';
return 0;
}

($smonth,$sday,$syear) = split('/',$start_date);

$emonth = $smonth + $duration_in_months;
$eday = $sday;
$eyear = $self->convertYearToYYYY($syear);

while ($emonth > 12) {
$emonth -= 12;
$eyear++;
}

@month_last_days = qw(0 31 28 31 30 31 30 31 31 30 31 30 31);
if ($eday > $month_last_days[$emonth]) {
$eday = $month_last_days[$emonth];
}

return $self->massageDate("$emonth/$eday/$eyear");
}

# takes: a positive or negative integer(a number of days), a wf date
# does: none
# returns: a new wf date
# NOTE: breaks when dealing with leap years (or rather ignores the
# fact that it's a leap year. This is good enough for now.
sub addNDaysToDate {
my $self = shift;
my ($date,$num_days) = @_;
my ($mon,$day,$yr);

if ($date =~ m|^(\d+)/(\d+)/(\d+)$|) {
($mon,$day,$yr) = ($1,$2,$3);
} else {
$self->_error("malformed date for addNDaysToDate : date-$date");
return 0;
}

if ($num_days < 0) {
$day += $num_days;
while ($day < 1) {
if ($mon == 1) {
$mon = 12;
$yr--;
} else {
$mon--;
}
$day = $MONTH_LENGTH{$mon} + $day;
}
} elsif ($num_days > 0) {
$day += $num_days;
while ($day > $MONTH_LENGTH{$mon}) {
$day = $day - $MONTH_LENGTH{$mon};
if ($mon == 12) {
$mon = 1;
$yr++;
} else {
$mon++;
}
}
} else {
return $date;
}

$mon =~ s/^(\d)$/0$1/;
$day =~ s/^(\d)$/0$1/;
$yr =~ s/^(\d)$/0$1/;

return "$mon/$day/$yr";
}


# Private routines

sub _getCurrentDate {
my $self = shift;
# Get current date. Don't forget to increment localtime()'s month.
my ($month, $day, $year) = (localtime(time))[4,3,5];
$month++;
$year += 1900;
my @date = ($month, $day, $year);
return massageDate(undef, \@date);
}


# takes: a string
# does: prints it as an error message
# returns: none
sub _error {
my $self = shift;
my ($error_msg) = @_;

warn "Problem in Util::Date.pm : $error_msg \n";
}

1;
Make your own free website on Tripod.com