chexmix has asked for the wisdom of the Perl Monks concerning the following question:
Good day, Monks!
As loath as I am to expose the true weakness of my brains in this austere, sacred realm, well, I'm at a point in my path to enlightenment where I should probably do just that. And so I am going to post an example of the kind of "CCNTwBnOGbSWDRGIY" (Crud Code Nailed Together with Boogers 'n' Other Goo by Someone Who Doesn't Really Get It Yet) I am so far capable of, for your delectation and my edification.
The genesis of the eldritch thing below is that I need to pass start and stop time values and a "pad" value (in seconds) for a database query. The pad value will be subtracted from the start value and added to the stop value to yield a time range which will be "pad value larger" on either end than the stop and start times indicate.
I had considered trying to let Sybase handle all the time-stuff but then found the "timelocal" function in Time::Local and its inverse, the builtin localtime, and thought, well, let's see if I can make it work in Perl.
The posted code only takes a single time value and has a 300 second padding hardcoded in. This was for simplicity's sake. It seems to work (It chokes though on a year like 2202), but wow, is it ugly. In particular I am sure there is something wrong with the way I am getting a properly formatted string back out of localtime. There is a lot of stuff that feels as though it should be pulled into subroutines and made more elegant ...
I invite and welcome comments, suggestions on how to do this better. I know it's pretty rugose and squamous at this point :^) It felt like something that should have been easy and wasn't as easy as it looked (especially when I found I couldn't use split properly on the string returned by localtime). But I am probably missing a lot of things.
#!/usr/bin/perl
use warnings;
use strict;
use Time::Local;
# Get timestamp input from user
print "Enter a timestamp in mm/dd/yyyy hh:mm:ss format: ";
chomp(my $stamp = <STDIN>);
# Split out various fields for timelocal
my($date, $time) = split / /, $stamp;
my($month, $day, $year) = split /\//, $date;
my($hour, $minute, $second) = split /:/, $time;
# Both timelocal and localtime understand month as in 0 - 11 range
$month -= 1;
# Get time value in seconds since Epoch with timelocal
my $now = timelocal($second,$minute,$hour,$day,$month,$year);
# Add padding (this will become interactive)
my $paddednow = $now + 300;
# Get sensible timestamp back out with localtime
my $answer = localtime($paddednow);
# Convert result to array for laborious processing of fields
my @Answer = split //, $answer;
my $Amon = $Answer[4].$Answer[5].$Answer[6];
my $Aday = $Answer[8].$Answer[9];
my $Atime = $Answer[11].$Answer[12].$Answer[13].$Answer[14].$Answer[15
+].$Answer[16].$Answer[17].$Answer[18];
my $Ayear = $Answer[20].$Answer[21].$Answer[22].$Answer[23];
# Declare hash for conversion of monthname values
my %monthy = (
"Jan" => "01",
"Feb" => "02",
"Mar" => "03",
"Apr" => "04",
"May" => "05",
"Jun" => "06",
"Jul" => "07",
"Aug" => "08",
"Sep" => "09",
"Oct" => "10",
"Nov" => "11",
"Dec" => "12"
);
# Handle leading zero issue with day of month
my %daything = (
" 1" => "01",
" 2" => "02",
" 3" => "03",
" 4" => "04",
" 5" => "05",
" 6" => "06",
" 7" => "07",
" 8" => "08",
" 9" => "09"
);
my $Amonth = $monthy{$Amon};
my $Arealday;
if ($Aday < 10) {
$Arealday = $daything{$Aday};
} else {
$Arealday = $Aday
}
# Cobble together result in same format as input
my $Adate = join "/", $Amonth, $Arealday, $Ayear;
my $realanswer = join " ", $Adate, $Atime;
print "$stamp + 5 minutes is $realanswer\n";
Re: Attack of the killer timestamps
by jwkrahn (Abbot) on Mar 20, 2008 at 20:43 UTC
|
It seems to work (It chokes though on a year like 2202)
That is because localtime is based on the Unix concept of the number of seconds since the epoch of January 1, 1970 and has historically been represented as a 32 bit number which only goes up to the year 2038.
This may work better:
#!/usr/bin/perl
use warnings;
use strict;
use POSIX qw/ mktime strftime /;
# Get timestamp input from user
print 'Enter a timestamp in mm/dd/yyyy hh:mm:ss format: ';
chomp( my $stamp = <STDIN> );
my ( $month, $day, $year, $hour, $minute, $second ) = $stamp =~ m! (\d
++) / (\d+) / (\d+) \s+ (\d+) : (\d+) : (\d+) !x;
# Get time value in seconds since Epoch with mktime
my $now = mktime $second, $minute, $hour, $day, $month - 1, $year - 19
+00;
my $realanswer = strftime '%m/%d/%Y %H:%M:%S', localtime $now + 300;
print "$stamp + 5 minutes is $realanswer\n";
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
|
Well, being a poet with a really nice grasp of English vocabulary should assist in becoming a conversant to fluent Perl programmer. Perl is very sensitive to context and has a plethora of its own vocabulary. Some people in fact deride it for these things, but others revel in them. My impression of you from this thread is that if you apply yourself to learning Perl's vocabulary and grammar as you have to those of English, you will be a master. All it takes is time, effort, a bit of intelligence, and the ability to handle symbolic thought. You're clearly, then, well on your way. Take heart and take practice, and you'll be writing like an old hand soon.
| [reply] [Watch: Dir/Any] |
|
|
use Class::Date qw(date -DateParse);
$Class::Date::DATE_FORMAT="%Y/%m/%d %H:%M:%S";
print date('2008/01/01 10:00:00') + 300, "\n", date('2008-01-01 10:00:
+00') + '5m';
update: golf opened ;)
| [reply] [Watch: Dir/Any] [d/l] |
Re: Attack of the killer timestamps
by kyle (Abbot) on Mar 20, 2008 at 20:39 UTC
|
I think your parsing of localtime would be a lot easier once you realize that it returns a list if used in a list context.
#print "Enter a timestamp in mm/dd/yyyy hh:mm:ss format: ";
#chomp(my $stamp = <STDIN>);
my $stamp = '11/02/2007 11:02:07';
# Split out various fields for timelocal
my($date, $time) = split / /, $stamp;
my($month, $day, $year) = split /\//, $date;
my($hour, $minute, $second) = split /:/, $time;
# Both timelocal and localtime understand month as in 0 - 11 range
$month -= 1;
# Get time value in seconds since Epoch with timelocal
my $now = timelocal($second,$minute,$hour,$day,$month,$year);
# Add padding (this will become interactive)
my $paddednow = $now + 300;
my @padded_time = localtime $paddednow;
$padded_time[5] += 1900; # year
$padded_time[4]++; # month
printf "$stamp + 5 minutes is %02d/%02d/%d %02d:%02d:%02d\n",
@padded_time[4,3,5, 2,1,0];
# 11/02/2007 11:02:07 + 5 minutes is 11/02/2007 11:07:07
This is a fairly easy way to do this, but one of the date modules that have already been mentioned might make it even easier. | [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
Re: Attack of the killer timestamps
by apl (Monsignor) on Mar 20, 2008 at 20:31 UTC
|
my %daything = (
" 1" => "01",
" 2" => "02",
" 3" => "03",
" 4" => "04",
" 5" => "05",
" 6" => "06",
" 7" => "07",
" 8" => "08",
" 9" => "09"
);
my $Arealday;
if ($Aday < 10) {
$Arealday = $daything{$Aday};
} else {
$Arealday = $Aday
}
with
my $Arealday = sprintf( '%02d', $Aday );
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Attack of the killer timestamps
by dwm042 (Priest) on Mar 20, 2008 at 20:29 UTC
|
Not speaking speifically to the issue of talking with Sybase, but to date manipulations in general, there are some good date modules in CPAN, including Date::Simple, Date::Calc and Date::Manip.
Update: fixed typo. | [reply] [Watch: Dir/Any] |
Re: Attack of the killer timestamps
by mpeppler (Vicar) on Mar 21, 2008 at 17:12 UTC
|
I had considered trying to let Sybase handle all the time-stuff
Personally that's what I might do, using the T-SQL dateadd() call, something like
select ... from ...
where the_date between dateadd(ss, -$seconds, $start_date)
and dateadd(ss, $seconds, $end_date)
(using placeholders of course, $var used here for illustration purposes)
But then I'm really a database developer writing tons of SQL, these days... :-)
Michael
| [reply] [Watch: Dir/Any] [d/l] |
|
Thanks for pointing that out - it might make the task a lot easier.
| [reply] [Watch: Dir/Any] |
A reply falls below the community's threshold of quality. You may see it by logging in. |
|
|