package Handy::Dandy::TimeTools;
use strict;
use vars qw( $AUTOLOAD $ATL $SEC $MIN $HOUR $DAY $WEEK $YEAR );
use constant UTC_OFFSET => -5;
use Exporter;
use OOorNO qw( :all );
$Handy::Dandy::TimeTools::VERSION     = 0.1_5;     # 9/23/02, 3:05 pm
@Handy::Dandy::TimeTools::ISA         = qw( Exporter OOorNO );
@Handy::Dandy::TimeTools::EXPORT_OK   = qw
   (
      UTC_OFFSET
      stamp   to_seconds   convert_time   seconds_since
      second   minute   hour   month   year   dayofweek   dayofyear
      minutestart   hourstart   daystart   weekstart   monthstart   yearstart
   );
%Handy::Dandy::TimeTools::EXPORT_TAGS =
   (
      'all' => [ @Handy::Dandy::TimeTools::EXPORT_OK ]
   );

=pod

   AUTHOR
      Tommy Butler <tommy @ atrixnet.com>
      phone: (817)-468-7716
      6711 Forest Park Dr
      Arlington, TX
           76001-8403

   COPYRIGHT   Tommy Butler. All rights reserved
   LISCENCE    This software is free, use/distribute under the GNU GPL.
   BUGS TO     Tommy Butler <perlmod @ atrixnet.com>

=cut


$SEC  = qr/^SEC/;  $MIN  = qr/^MIN/; $HOUR = qr/^HOUR/;
$DAY  = qr/^DAY/; $WEEK = qr/^WEEK/; $YEAR = qr/^YEAR/;

# --------------------------------------------------------
# Constructor
# --------------------------------------------------------
sub new { bless({}, shift(@_)); }


# --------------------------------------------------------
# Handy::Dandy::TimeTools::stamp()
# --------------------------------------------------------

{
   my($months) =
      [
         'January',   'February', 'March',   'April',
         'May',       'June',     'July',    'August',
         'September', 'October',  'November', 'December',
      ];

   my($days) =
      [
         'Sunday',   'Monday', 'Tuesday', 'Wednesday',
         'Thursday', 'Friday', 'Saturday'
      ];

   sub stamp  {

      my($opts)            = shave_opts(\@_);
      my($argtime,$offset) = myargs(@_);

      $argtime ||= time unless Handy::Dandy->isint($argtime);
      $offset  ||= UTC_OFFSET unless Handy::Dandy->isint($offset);
      $argtime  += ($offset * 3600);

      my($sec,$min,$h24,$date,$mon,$year,$wday,$yday) = gmtime($argtime);

      my($hour) = $h24;

      $hour = $h24 - 12 if ($h24 > 12); $hour = 12 if ($hour == 0);

      my($AMPM) = ($h24 >= 12) ? 'pm' : 'am';

      goto DEFAULT if ($opts->{'--short'} or !scalar(keys(%$opts)));

      # -June-15-2002-16.22.43
      return
        (
         sprintf(
            q[-%s-%u-%u-%u.%02u.%02u],
            $months->[$mon], $date, $year + 1900,
            $h24,          $min,          $sec
         )
        ) if ($opts->{'--file'} || $opts->{'--filename'});

      # 5/15/02
      return
        (
         sprintf(
            q[%s/%s/%2s],
            $mon + 1, $date, substr($year + 1900, 2)
         )
        ) if $opts->{'--mdy'};

      # Saturday, June 15, 2002, 4:22 pm
      return
        (
         sprintf(
            q[%s, %s %u, %u, %s:%02u %s],
            $days->[$wday], $months->[$mon], $date, $year + 1900,
            $hour,        $min,         $AMPM
         )
        )  if ($opts->{'--formal'} || $opts->{'--long'});

      # Sat, 15 Jun 2002 16.22.43 GMT
      return
        (
         sprintf(
            q[%s, %0.2f %s:%02u:%02u GMT],
            substr($days->[$wday],0,3), $date, substr($months->[$mon],0,3),
            $year + 1900,  $h24,  $min, $sec
         )
        )  if $opts->{'--ISO'};

      # Sat 5/15/02 16:22:43
      return
        (
         sprintf(
            q[%s %s/%s/%2s %s:%02u:%02u],
            substr($days->[$wday],0,3), $mon + 1, $date,
            substr($year + 1900, 2),  $h24,  $min, $sec
         )
        )  if $opts->{'--succinct'};

      # 4:22 pm
      return
        (
         sprintf(
            q[%s:%02u %s],
            $hour, $min, $AMPM
         )
        ) if $opts->{'--hm'};

      # 4:22:43 pm
      return
        (
         sprintf(
            q[%s:%02u:%02u %s],
            $hour, $min, $sec, $AMPM
         )
        ) if $opts->{'--hms'};

      # 16:22:43
      return
        (
         sprintf(
            q[%s:%02u:%02u],
            $h24, $min, $sec
         )
        ) if $opts->{'--24hms'};


   # MOVING TO IF-ELSIF-ELSE SEQUENCE NOW...
      if ($opts->{'--dayofmonth'}) {

         # (number of the date) 1 - [28-31]
         return($date);
      }
      elsif ($opts->{'--dayofweek'}) {

         # (number for day of the week) 1 - 7
         return($wday+1) if $opts->{'--num'};

         # (name of the day) Sunday - Saturday
         return($days->[$wday]);
      }
      elsif ($opts->{'--dayofyear'}) {

         # (number for day of the year) 1 - 365 (non-leap)
         return($yday);
      }
      elsif ($opts->{'--month'}) {

         # (number of the month) 1 - 12
         return($mon+1) if $opts->{'--num'};

         # (name of the month) January - December
         return($months->[$mon]);
      }
      elsif ($opts->{'--year'}) {

         # (year number) 2002
         return($year + 1900);
      }
      elsif ($opts->{'--shortyear'}) {

         # (abbreviated year number)
         return(substr($year + 1900, 2));
      }
      elsif ($opts->{'--minute'}) {

         # (number of the minute) 0 - 59
         return($min);
      }
      elsif ( $opts->{'--hour'}) {

         # (number of the hour) 0 - 24
         return($h24);
      }
      elsif ($opts->{'--second'}) {

         # (number of the second) 0 - 59
         return($sec);
      }
      else {

         DEFAULT:

         # 5/15/02, 4:22 pm
         return
           (
            sprintf(
               q[%s/%u/%2s, %s:%02u %s],
               $mon + 1, $date, substr($year + 1900, 2),
               $hour, $min, $AMPM
            )
           );
      }

      '';
   }
}


# --------------------------------------------------------
# Handy::Dandy::TimeTools::to_seconds()
# --------------------------------------------------------
sub to_seconds {

   my($unit)   = ${\myargs(@_)} || return(undef);
   my($amt)    = 0;

   ($amt,$unit) = split(/ /,$unit); $unit = uc($unit);

   return(0) unless Handy::Dandy::isnum($amt);

   if    ($unit =~ /$SEC/)   { return($amt);            }
   elsif ($unit =~ /$MIN/)   { return($amt * 60);       }
   elsif ($unit =~ /$HOUR/)  { return($amt * 3600);     }
   elsif ($unit =~ /$DAY/)   { return($amt * 86400);    }
   elsif ($unit =~ /$WEEK/)  { return($amt * 604800);   }
   elsif ($unit =~ /$YEAR/)  { return($amt * 31536000); }

   -1;
}


# --------------------------------------------------------
# Handy::Dandy::TimeTools::seconds_since()
# --------------------------------------------------------
sub seconds_since {

   my($opts)      = shave_opts(\@_);
   my($h,$m,$s)   =
      (
         stamp('--hour','--num'),
         stamp('--minute'),
         stamp('--second')
      );

   $h = to_seconds(qq[$h hours]); $m = to_seconds(qq[$m minutes]);

   if    ($opts->{'--minutestart'}) { return($s);           }
   elsif ($opts->{'--hourstart'})   { return($m + $s);      }
   elsif ($opts->{'--daystart'})    { return($h + $m + $s); }
   elsif ($opts->{'--weekstart'})   {

      return($h + $m + $s + to_seconds(stamp('--dayofweek','--num') - 1 . q[ days]));
   }
   elsif ($opts->{'--monthstart'}) {

      return($h + $m + $s + to_seconds(stamp('--dayofmonth') - 1 . q[ days]));
   }
   elsif ($opts->{'--yearstart'}) {

      return($h + $m + $s + to_seconds(stamp('--dayofyear') . q[ days]) - 3600);
   }

   undef;
}


# Handy::Dandy::TimeTools::-------------------------------
#   minutestart(),   hourstart(),   daystart(),
#   weekstart(),   monthstart(),   yearstart()
# --------------------------------------------------------
sub minutestart   { time - seconds_since(q[--minutestart]); }
sub hourstart     { time - seconds_since(q[--hourstart]);   }
sub daystart      { time - seconds_since(q[--daystart]);    }
sub weekstart     { time - seconds_since(q[--weekstart]);   }
sub monthstart    { time - seconds_since(q[--monthstart]);  }
sub yearstart     { time - seconds_since(q[--yearstart]);   }


# --------------------------------------------------------
# Handy::Dandy::TimeTools::AUTOLOAD()
# --------------------------------------------------------
sub AUTOLOAD {

   my($sub) = $AUTOLOAD; $sub =~ s/^.*\:\://o;

   if (ref($ATL) ne 'HASH') { $ATL = eval($ATL); }

   if (ref(eval(qq[\$sub])) eq 'CODE') { goto &$sub; }

   unless ($ATL->{ $sub }) {

      die(qq[BAD AUTOLOAD. Can't do $sub().  Don't know what it is.]);
   }

   eval($ATL->{ $sub }); CORE::delete($ATL->{ $sub });

   goto &$sub;
}


# --------------------------------------------------------
# Handy::Dandy::TimeTools::DESTROY()
# --------------------------------------------------------
sub DESTROY {}



INIT { $ATL = <<'___AUTOLOADED___'; }
   {
'convert_time' => <<'__SUB__',
# --------------------------------------------------------
# Handy::Dandy::TimeTools::convert_time()
# --------------------------------------------------------
sub convert_time {

   # syntax: $dandy->convert_time($int, q[days to hours])

   my($amt, $cmd) = myargs(@_);

   return(undef) unless Handy::Dandy::isnum($amt);

   my(@specs)     = split(/ /,uc($cmd));
   my($from)      = $specs[0];
   my($to)        = $specs[-1];

   # FROM conversions
   # (note: conversion FROM seconds TO unit x is implicit)
   if    ($from =~ /$MIN/)   { $amt *= 60;        }
   elsif ($from =~ /$HOUR/)  { $amt *= 3600;      }
   elsif ($from =~ /$DAY/)   { $amt *= 86400;     }
   elsif ($from =~ /$WEEK/)  { $amt *= 604800;    }
   elsif ($from =~ /$YEAR/)  { $amt *= 31536000;  }

   # TO conversions
   if    ($to =~ /$MIN/)     { return($amt / 60);        }
   elsif ($to =~ /$HOUR/)    { return($amt / 3600);      }
   elsif ($to =~ /$DAY/)     { return($amt / 86400);     }
   elsif ($to =~ /$WEEK/)    { return($amt / 604800);    }
   elsif ($to =~ /$YEAR/)    { return($amt / 31536000);  }

   return($amt);
}
__SUB__

# Handy::Dandy::TimeTools::-------------------------------
#   second(),  minute(),  hour(),  month(),  year()
#   dayofmonth(),  dayofweek(),  dayofyear()
# --------------------------------------------------------
'second'       => <<'__SUB__',
sub second     { stamp('--second');             }
__SUB__
'minute'       => <<'__SUB__',
sub minute     { stamp('--minute');             }
__SUB__
'hour'         => <<'__SUB__',
sub hour       { stamp('--hour');               }
__SUB__
'month'        => <<'__SUB__',
sub month      { stamp('--month','--num');      }
__SUB__
'year'         => <<'__SUB__',
sub year       { stamp('--year');               }
__SUB__
'dayofmonth'   => <<'__SUB__',
sub dayofmonth { stamp('--dayofmonth','--num'); }
__SUB__
'dayofweek'    => <<'__SUB__',
sub dayofweek  { stamp('--dayofweek','--num');  }
__SUB__
'dayofyear'    => <<'__SUB__',
sub dayofyear  { stamp('--dayofyear');          }
__SUB__
}
___AUTOLOADED___


# --------------------------------------------------------
# end Handy::Dandy::TimeTools Class, return true on import
# --------------------------------------------------------

1;

=FOR REFERENCE

   TIME CONVERSION REFERENCE TABLE

                  1 minute = 60 seconds
                    1 hour = 60 minutes
                60 minutes = 3600 seconds
                     1 day = 24 hours
                  24 hours = 1440 minutes
              1440 minutes = 86400 seconds
                    1 week = 7 days
                    1 week = 7.0347222 days (leap sec)
            7.0347222 days = 168.8333333 hours (leap sec)
                    7 days = 604800 seconds
         168.8333333 hours = 10130 minutes
             10130 minutes = 604800 seconds
                    1 year = 365.2425116 days
          365.2425116 days = 8765.8202778 hours
        8765.8202778 hours = 525949.2166667 minutes
    525949.2166667 minutes = 31556953 seconds

      (
         1  => [31, 31,  0],
         2  => [28, 59, 31],
         3  => [31, 90, 59],
         4  => [30,120, 90],
         5  => [31,151,120],
         6  => [30,181,151],
         7  => [31,212,181],
         8  => [31,243,212],
         9  => [30,273,243],
         10 => [31,304,273],
         11 => [30,334,304],
         12 => [31,365,334],
      );

   for each item in the above data structure:
   month =>  [(days within), (total days after), (total days before)]

=cut