##---------------------------------------------------------------------------## ## File: ## $Id: mhtime.pl,v 2.10 2001/09/17 16:09:35 ehood Exp $ ## Author: ## Earl Hood mhonarc@mhonarc.org ## Description: ## Time related routines for mhonarc ##---------------------------------------------------------------------------## ## Copyright (C) 1996-1999 Earl Hood, mhonarc@mhonarc.org ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ## 02111-1307, USA ##---------------------------------------------------------------------------## package mhonarc; ##---------------------------------------------------------------------------## ## Date variables for date routines ## my %Month2Num = ( 'jan', 0, 'feb', 1, 'mar', 2, 'apr', 3, 'may', 4, 'jun', 5, 'jul', 6, 'aug', 7, 'sep', 8, 'oct', 9, 'nov', 10, 'dec', 11, 'january', 0, 'february', 1, 'march', 2, 'april', 3, 'may', 4, 'june', 5, 'july', 6, 'august', 7, 'september', 8, 'october', 9, 'november', 10, 'december', 11, ); my %WDay2Num = ( 'sun', 0, 'mon', 1, 'tue', 2, 'wed', 3, 'thu', 4, 'fri', 5, 'sat', 6, 'sunday', 0, 'monday', 1, 'tuesday', 2, 'wednesday', 3, 'thursday', 4, 'friday', 5, 'saturday', 6, ); my @wdays = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'); my @Wdays = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); my @mons = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'); my @Mons = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'); ## The following used in parse_date() regexes my $p_weekdays = 'Mon|Tue|Wed|Thu|Fri|Sat|Sun'; my $p_Weekdays = 'Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday'; my $p_months = 'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec'; my $p_Months = 'January|February|March|April|May|June|July|August'. '|September|October|November|December'; my $p_hrminsec = '\d{1,2}:\d\d:\d\d'; my $p_hrmin = '\d{1,2}:\d\d'; my $p_day = '\d{1,2}'; my $p_year = '\d\d\d\d|\d\d'; ##--------------------------------------------------------------------------- ## Set weekday and month names. This allows localization of ## names. ## sub set_date_names { my($in_wd, $in_Wd, $in_m, $in_M) = @_; @wdays = @$in_wd if defined($in_wd) && scalar(@$in_wd); @Wdays = @$in_Wd if defined($in_Wd) && scalar(@$in_Wd); @mons = @$in_m if defined($in_m) && scalar(@$in_m); @Mons = @$in_M if defined($in_M) && scalar(@$in_M); } ##--------------------------------------------------------------------------- ## Get date in date(1)-like format. $local flag is if local time ## should be used. ## sub getdate { &time2str('', time, $_[0]); } ##--------------------------------------------------------------------------- ## Convert a calander time to a string. ## sub time2str { my($fmt, $time, $local) = @_; my($date) = ""; ## Get current date/time my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = ($local ? localtime($time) : gmtime($time)); ## If format string blank, use default format if ($fmt !~ /\S/) { $fmt = '%a %b %d %H:%M:%S'; $fmt .= ' GMT' unless $local; $fmt .= ' %Y'; } POSIXMODCHK: { last POSIXMODCHK unless $POSIXstrftime; eval { require POSIX; }; last POSIXMODCHK if ($@) || !defined(&POSIX::strftime); return POSIX::strftime($fmt, $sec,$min,$hour,$mday,$mon,$year, $wday,$yday,$isdst); } ## Get here, we have to do it ourselves. my($yearfull, $hour12); $yearfull = $year + 1900; $year = $year % 100; $hour12 = $hour > 12 ? $hour-12 : $hour; ## Format output $fmt =~ s/\%c/\%a \%b \%d \%H:\%M:\%S \%Y/g; $fmt =~ s/\%a/$wdays[$wday]/g; $fmt =~ s/\%A/$Wdays[$wday]/g; $fmt =~ s/\%[bh]/$mons[$mon]/g; $fmt =~ s/\%B/$Mons[$mon]/g; $sec = sprintf("%02d", $sec); $min = sprintf("%02d", $min); $hour = sprintf("%02d", $hour); $hour12 = sprintf("%02d", $hour12); $mday = sprintf("%02d", $mday); $mon = sprintf("%02d", $mon+1); $year = sprintf("%02d", $year); $yearfull = sprintf("%04d", $yearfull); $wday = sprintf("%02d", $wday+1); $yday = sprintf("%03d", $yday); $fmt =~ s/\%d/$mday/g; $fmt =~ s/\%H/$hour/g; $fmt =~ s/\%I/$hour12/g; $fmt =~ s/\%j/$yday/g; $fmt =~ s/\%m/$mon/g; $fmt =~ s/\%M/$min/g; $fmt =~ s/\%n/\n/g; $fmt =~ s/\%p/am/g if ($hour < 12); $fmt =~ s/\%p/pm/g if ($hour >= 12); $fmt =~ s/\%P/AM/g if ($hour < 12); $fmt =~ s/\%P/PM/g if ($hour >= 12); $fmt =~ s/\%S/$sec/g; $fmt =~ s/\%w/$wday/g; $fmt =~ s/\%y/$year/g; $fmt =~ s/\%Y/$yearfull/g; $fmt =~ s/\%\%/\%/g ; $date = $fmt ; $date ; } ##--------------------------------------------------------------------------- ## parse_date takes a string date specified like the output of ## date(1) into its components. Parsing a string for a date is ## ugly since we have to watch out for differing formats. ## ## The following date formats are looked for: ## ## Wdy DD Mon YY HH:MM:SS Zone ## DD Mon YY HH:MM:SS Zone ## Wdy Mon DD HH:MM:SS Zone YYYY ## Wdy Mon DD HH:MM:SS YYYY ## ## The routine keys off of the day of time field "HH:MM:SS" and ## scans realtive to its location. ## ## If the parse fails, a null array is returned. Thus the routine ## may be used as follows: ## ## if ( (@x = &parse_date($date)) ) { Success } ## else { Fail } ## ## If success the array contents are as follows: ## ## (Weekday (0-6), Day of the month (1-31), Month (0-11), ## Year, Hour, Minutes, Seconds, Time Zone) ## ## Contributer(s): Frank J. Manion ## sub parse_date { my($date) = $_[0]; my($wday, $mday, $mon, $yr, $time, $hr, $min, $sec, $zone); my(@array); my($start, $rest); # Try to find the date by focusing on the "\d\d:\d\d" field. # All parsing is then done relative to this location. # $date =~ s/^\s+//; $time = ""; $rest = ""; # Don't use $p_hrmin(sec) vars in split due to bug in perl 5.003. ($start, $time, $rest) = split(/(\b\d{1,2}:\d\d:\d\d)/o, $date, 2); ($start, $time, $rest) = split(/(\b\d{1,2}:\d\d)/o, $date, 2) if !defined($time) or $time eq ""; return () unless defined($time) and $time ne ""; ($hr, $min, $sec) = split(/:/, $time); $sec = 0 unless $sec; # Sometimes seconds not defined # Strip $start of all but the last 4 tokens, # and stuff all tokens in $rest into @array # @array = split(' ', $start); $start = join(' ', ($#array-3 < 0) ? @array[0..$#array] : @array[$#array-3..$#array]); @array = split(' ', $rest); $rest = join(' ', ($#array >= 1) ? @array[0..1] : $array[0]); # Wdy DD Mon YY HH:MM:SS Zone if ( $start =~ /($p_weekdays),*\s+($p_day)\s+($p_months)\s+($p_year)$/io ) { ($wday, $mday, $mon, $yr, $zone) = ($1, $2, $3, $4, $array[0]); # DD Mon YY HH:MM:SS Zone } elsif ( $start =~ /($p_day)\s+($p_months)\s+($p_year)$/io ) { ($mday, $mon, $yr, $zone) = ($1, $2, $3, $array[0]); # Wdy Mon DD HH:MM:SS Zone YYYY # Wdy Mon DD HH:MM:SS YYYY } elsif ( $start =~ /($p_weekdays),?\s+($p_months)\s+($p_day)$/io ) { ($wday, $mon, $mday) = ($1, $2, $3); if ( $rest =~ /^(\S+)\s+($p_year)/o ) { # Zone YYYY ($zone, $yr) = ($1, $2); } elsif ( $rest =~ /^($p_year)/o ) { # YYYY ($yr) = ($1); } else { # zilch, use current year warn "Warning: No year in date ($date), using current\n"; $yr = (localtime(time))[5]; } # Weekday Month DD YYYY HH:MM Zone } elsif ( $start =~ /($p_Weekdays),?\s+($p_Months)\s+($p_day),?\s+($p_year)$/ ) { ($wday, $mon, $mday, $yr, $zone) = ($1, $2, $3, $4, $array[0]); # All else fails! } else { return (); } # Modify month and weekday for lookup $mon = $Month2Num{lc $mon} if defined($mon); $wday = $WDay2Num{lc $wday} if defined($wday); ($wday, $mday, $mon, $yr, $hr, $min, $sec, $zone); } ##--------------------------------------------------------------------------- ## Routine to convert time in seconds to a month, day, and year ## format. The format can be "mmddyy", "yymmdd", "ddmmyy". The ## year can be specifed as "yyyy" if a 4 digit year is needed. ## sub time2mmddyy { my($time, $fmt) = ($_[0], $_[1]); my($day,$mon,$year,$ylen,$tmp); if ($time) { ($day,$mon,$year) = (localtime($time))[3,4,5]; $year += 1900; ## Compute length for year field $ylen = $fmt =~ s/y/y/g; substr($year, 0, 4 - $ylen) = ''; ## Create string if ($fmt =~ /ddmmyy/i) { # DDMMYY $tmp = sprintf("%02d/%02d/%0${ylen}d", $day, $mon+1, $year); } elsif ($fmt =~ /yymmdd/i) { # YYMMDD $tmp = sprintf("%0${ylen}d/%02d/%02d", $year, $mon+1, $day); } else { # MMDDYY $tmp = sprintf("%02d/%02d/%0${ylen}d", $mon+1, $day, $year); } } else { $tmp = "--/--/--"; } } ##--------------------------------------------------------------------------- ## zone_offset_to_secs translates a [+-]HHMM zone offset to ## seconds. ## sub zone_offset_to_secs { my($off) = shift; my($sign, $min); ## Check if just an hour specification if (length($off) < 4) { return $off * 3600; } ## Check for sign if ($off =~ s/-//) { $sign = -1; } else { $sign = 1; s/\+//; } ## Extract minutes $min = substr($off, -2, 2); substr($off, -2, 2) = ""; # Just leave hour in $off ## Translate to seconds $sign * (($off * 3600) + ($min * 60)); } ##---------------------------------------------------------------------------## 1;