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;