For Programmers: Free Programming Magazines  


Home > Archive > PERL Miscellaneous > June 2005 > improve subroutine.. seems klunky









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author improve subroutine.. seems klunky
matg

2005-06-10, 8:56 am

I've cobbled together a subroutine to get a timestamp and create a
further string that represents the previous day.

Can anyone improve on this .. it seems very klunky.. and I'm sure
there's a neater way to do things..

the code:...................

###########################
#
# creates timestamp from current date
# adds a leading 0 to the month and day string
# and works on previous day
#
###########################
sub create_timestamp{

($sec,$min,$hour,$mday,$mon,$year,$wday,
$yday,$isdst) = gmtime(time);
if ($year == "102"){;
$year = "2002";
}
if ($year == "103"){;
$year = "2003";
}
if ($year == "104"){;
$year = "2004";
}
if ($year == "105"){;
$year = "2005";
}
if ($year == "105"){;
$year = "2005";
}
if ($year == "106"){;
$year = "2006";
}
if ($mon== "0"){
$month = "01"
}
if ($mon== "1"){
$month = "02"
}
if ($mon== "2"){
$month = "03"
}
if ($mon== "3"){
$month = "04"
}
if ($mon== "4"){
$month = "05"
}
if ($mon== "5"){
$month = "06"
}
if ($mon== "6"){
$month = "07"
}
if ($mon== "7"){
$month = "08"
}
if ($mon== "8"){
$month = "09"
}
if ($mon== "9"){
$month = "10"
}
if ($mon== "10"){
$month = "11"
}
if ($mon== "11"){
$month = "12"
}
if ($mday > 10){
$previous_day=($mday-1)
}
if ($mday == "2") {
$previous_day="01";
}
if ($mday == "3") {
$previous_day="02";
}
if ($mday == "4") {
$previous_day="03";
}
if ($mday == "5") {
$previous_day="04";
}
if ($mday == "6") {
$previous_day="05";
}
if ($mday == "7") {
$previous_day="06";
}
if ($mday == "8") {
$previous_day="07";
}
if ($mday == "9") {
$previous_day="08";
}
if ($mday == "10") {
$previous_day="09";
}
$timestamp = "$month$previous_day";
}

Ian Wilson

2005-06-10, 8:56 am

matg wrote:
> I've cobbled together a subroutine to get a timestamp and create a
> further string that represents the previous day.
>
> Can anyone improve on this .. it seems very klunky.. and I'm sure
> there's a neater way to do things..
>
> the code:...................
>
> ###########################
> #
> # creates timestamp from current date
> # adds a leading 0 to the month and day string
> # and works on previous day
> #
> ###########################
> sub create_timestamp{


See the posting guidelines. "use strict;" and "use warnings;" might have
shed some light on a few things.

>
> ($sec,$min,$hour,$mday,$mon,$year,$wday,
$yday,$isdst) = gmtime(time);
> if ($year == "102"){;

"==" is a numeric comparison, "102" is a string. Use "eq" for string
comparisons. However you *should* be treating the variables as numbers.

$year += 1900;

> $year = "2002";
> }
> if ($year == "103"){;
> $year = "2003";
> }
> if ($year == "104"){;
> $year = "2004";
> }
> if ($year == "105"){;
> $year = "2005";
> }
> if ($year == "105"){;
> $year = "2005";
> }
> if ($year == "106"){;
> $year = "2006";
> }


All the above are redundant if you simply add 1900 to the year.

> if ($mon== "0"){
> $month = "01"
> }
> if ($mon== "1"){
> $month = "02"
> }
> if ($mon== "2"){
> $month = "03"
> }
> if ($mon== "3"){
> $month = "04"
> }
> if ($mon== "4"){
> $month = "05"
> }
> if ($mon== "5"){
> $month = "06"
> }
> if ($mon== "6"){
> $month = "07"
> }
> if ($mon== "7"){
> $month = "08"
> }
> if ($mon== "8"){
> $month = "09"
> }
> if ($mon== "9"){
> $month = "10"
> }
> if ($mon== "10"){
> $month = "11"
> }
> if ($mon== "11"){
> $month = "12"
> }


$mon++;

> if ($mday > 10){
> $previous_day=($mday-1)
> }
> if ($mday == "2") {
> $previous_day="01";
> }
> if ($mday == "3") {
> $previous_day="02";
> }
> if ($mday == "4") {
> $previous_day="03";
> }
> if ($mday == "5") {
> $previous_day="04";
> }
> if ($mday == "6") {
> $previous_day="05";
> }
> if ($mday == "7") {
> $previous_day="06";
> }
> if ($mday == "8") {
> $previous_day="07";
> }
> if ($mday == "9") {
> $previous_day="08";
> }
> if ($mday == "10") {
> $previous_day="09";
> }


The obvious (but still incorrect) way to do this is
$previous_day = sprintf("%.2d", $mday-1);
What happens if today is the first of the month?

If doing date arithmetic, I'd use the date modules Date::Manip or
Date::Calc.

The answer is in 'perldoc -q yesterday' and in the Perl FAQ 4.17 often
posted here.

> $timestamp = "$month$previous_day";
> }
>

Brian Wakem

2005-06-10, 8:56 am

matg wrote:

> I've cobbled together a subroutine to get a timestamp and create a
> further string that represents the previous day.
>
> Can anyone improve on this .. it seems very klunky.. and I'm sure
> there's a neater way to do things..
>
> the code:...................
>

<snip very longwinded code>


my ($day,$month) = (localtime(time-86400))[3,4];
my $timestamp = sprintf "%02d%02d",$month+1,$day;


--
Brian Wakem

matg

2005-06-10, 8:56 am

wow - thanks to all for the replies .. I'll have to pay more
attention.. and read more first....

again many thanks

Ian Wilson

2005-06-10, 8:56 am

Brian Wakem wrote:
> matg wrote:
>
>
>
> <snip very longwinded code>
>
>
> my ($day,$month) = (localtime(time-86400))[3,4];
> my $timestamp = sprintf "%02d%02d",$month+1,$day;
>
>


Doesn't that give the wrong answer for two hours every year in locales
which have DST?
Brian Wakem

2005-06-10, 3:58 pm

Ian Wilson wrote:

> Brian Wakem wrote:
>
> Doesn't that give the wrong answer for two hours every year in locales
> which have DST?



Probably.

This should avoid DST issues -


use Time::Local;
my ($day,$month,$year) = (localtime(time))[3..5];
my $time = timelocal(0,0,12,$day,$month,$year);
($day,$month) = (localtime($time-86400))[3,4];
my $timestamp = sprintf "%02d%02d",$month+1,$day;



--
Brian Wakem

axel@white-eagle.invalid.uk

2005-06-10, 3:58 pm

matg <mathew_grove@yahoo.co.uk> wrote:
> I've cobbled together a subroutine to get a timestamp and create a
> further string that represents the previous day.


> Can anyone improve on this .. it seems very klunky.. and I'm sure
> there's a neater way to do things..


> ###########################
> #
> # creates timestamp from current date
> # adds a leading 0 to the month and day string
> # and works on previous day
> #
> ###########################
> sub create_timestamp{
>
> ($sec,$min,$hour,$mday,$mon,$year,$wday,
$yday,$isdst) = gmtime(time);
> if ($year == "102"){;
> $year = "2002";
> }
> if ($year == "103"){;
> $year = "2003";
> }


Why not just:

$year += 1900;

But then you might as well add on an extra 753 to to base the year
on A.U.C. (Ab urbe condita) since you never actually use the year
in the timestamp.

> if ($mon== "0"){
> $month = "01"
> }


Again

$month = $mon + 1;

And worry about the formatting when you build the timestamp. Actually
you could omit this step entirely... see below.

> if ($mday > 10){
> $previous_day=($mday-1)
> }
> if ($mday == "2") {
> $previous_day="01";
> }


So when the $mday is 1, it just gets ignored. An interesting approach to
the problem.

> $timestamp = "$month$previous_day";


This should then be built from something along the lines of:

$timestamp = sprintf ("%02d:%02d\n", $mon + 1, $previous_day);

But far more easier would be to use a CPAN module... maybe
Date::Format...

use Date::Format;
print time2str("%Y:%m:%d", time);

prints

2005:06:10

and if you subtract 86400 seconds from the time given to time2str, you
should get the previous day.

Axel
James David

2005-06-10, 3:58 pm

"Brian Wakem" <no@email.com> wrote in message
news:3gtbgcFe7341U1@individual.net...

<snip>

> my ($day,$month,$year) = (localtime(time))[3..5];

^^^^^^^^^^^^^^^^^

I'm almost embarrassed to ask, but could someone explain what's happening in
this statement. Thanks.


James David

2005-06-10, 3:58 pm


"James David" <solar@infidel.ixx> wrote in message
news:23hqe.115299$w15.113536@tornado.tampabay.rr.com...
> "Brian Wakem" <no@email.com> wrote in message
> news:3gtbgcFe7341U1@individual.net...
>
> <snip>
>
> ^^^^^^^^^^^^^^^^^
>
> I'm almost embarrassed to ask, but could someone explain what's happening

in
> this statement. Thanks.


More specifically, I'm trying to understand the theory or the construct of
the placement of [3..5] adjacent to localtime() to extract the needed
elements. Thanks again.


axel@white-eagle.invalid.uk

2005-06-10, 3:58 pm

James David <solar@infidel.ixx> wrote:
[color=darkred]
> in
[color=darkred]
> More specifically, I'm trying to understand the theory or the construct of
> the placement of [3..5] adjacent to localtime() to extract the needed
> elements. Thanks again.


Think of it as applying an array slice to the list returned by
localtime().

Axel

Brian Wakem

2005-06-10, 3:58 pm

James David wrote:

>
> "James David" <solar@infidel.ixx> wrote in message
> news:23hqe.115299$w15.113536@tornado.tampabay.rr.com...
> in
>
> More specifically, I'm trying to understand the theory or the construct of
> the placement of [3..5] adjacent to localtime() to extract the needed
> elements. Thanks again.



localtime(time) returns an array that is commonly split into its parts like
this -

my ($sec, $min, $hour, $mday, $month, $year, $wday, $yday, $isdst) =
localtime(time);

[3..5] selects just the 4th,5th and 6th elements of the array, which are
then assigned to ($day,$month,$year).

... being the range operator.


--
Brian Wakem
Tad McClellan

2005-06-10, 3:58 pm

Ian Wilson <scobloke2@infotop.co.uk> wrote:
> matg wrote:


^
^
^ What's that for?

[color=darkred]
> "==" is a numeric comparison, "102" is a string. Use "eq" for string
> comparisons. However you *should* be treating the variables as numbers.



So, it should either be:

if ($year == 102) { # treat $year as a number

or

if ($year eq '102') { # treat $year as a string


--
Tad McClellan SGML consulting
tadmc@augustmail.com Perl programming
Fort Worth, Texas
John Bokma

2005-06-10, 3:58 pm

Tad McClellan wrote:

> So, it should either be:
>
> if ($year == 102) { # treat $year as a number
>
> or
>
> if ($year eq '102') { # treat $year as a string


Moreover, I would recommend adding 1900 to the year first, so it's more
clear that you mean 2002.

--
John Small Perl scripts: http://johnbokma.com/perl/
Perl programmer available: http://castleamber.com/
Happy Customers: http://castleamber.com/testimonials.html

Tad McClellan

2005-06-10, 3:58 pm

Brian Wakem <no@email.com> wrote:
> James David wrote:
>
[color=darkred]
>
>
> localtime(time) returns an array



Functions in Perl never return an "array", they return a "list".


> that is commonly split into its parts like
> this -
>
> my ($sec, $min, $hour, $mday, $month, $year, $wday, $yday, $isdst) =
> localtime(time);
>
> [3..5] selects just the 4th,5th and 6th elements of the array,



And that is known as a "list slice". The syntax for a list slice is
"stuff in parenthesis followed by stuff in square brackets".

The parens stuff is the list to be sliced, and the square stuff
is a list of the indexes that you want to slice out.


See the "Slices" section in

perldoc perldata

for info on all three kinds of slices (array slice and hash slice being
the other two).


--
Tad McClellan SGML consulting
tadmc@augustmail.com Perl programming
Fort Worth, Texas
l v

2005-06-11, 3:57 am

matg wrote:
> I've cobbled together a subroutine to get a timestamp and create a
> further string that represents the previous day.
>
> Can anyone improve on this .. it seems very klunky.. and I'm sure
> there's a neater way to do things..
>
> the code:...................
>
> ###########################
> #
> # creates timestamp from current date
> # adds a leading 0 to the month and day string
> # and works on previous day
> #
> ###########################
> sub create_timestamp{
>

[snipped]
> }
>


I may have not read the intent of you code exactly as you desire, have
you looked at any of the date modules on CPAN? I use Date::Calc or
Date::Pcalc the most.

The below code would correctly subtract one day from current gmt date:

use Date::Calc qw(Today Add_Delta_Days);
($year,$month,$day) = Add_Delta_Days(Today(1), -1);
$timestamp = sprintf "%02d%02d", $month, $day;

Len

----== Posted via Newsfeeds.Com - Unlimited-Uncensored-Secure Usenet News==----
http://www.newsfeeds.com The #1 Newsgroup Service in the World! 120,000+ Newsgroups
----= East and West-Coast Server Farms - Total Privacy via Encryption =----
Mark Seger

2005-06-11, 3:57 pm

> The below code would correctly subtract one day from current gmt date:
>
> use Date::Calc qw(Today Add_Delta_Days);
> ($year,$month,$day) = Add_Delta_Days(Today(1), -1);
> $timestamp = sprintf "%02d%02d", $month, $day;
>

This whole thread also reminded me of something I stumbled on with
'time' a long time ago and I'm still not sure if it's a bug or a feature
and that has to do with how perl treats DST in general. It all happened
long ago when I had written a routine that reported the number of
seconds between 2 times and when DST was in the middle it reported the
wrong number!

While I can appreciate on one level if you adjust the clock you
sometimes 'lose' an hour and sometimes 'gain' one, but aren't the actual
number of seconds that passed still 86400? Clearly there are 2 'time's
involved, one which is the number of seconds since the epoch and the
other is the number of seconds that representst the current time and
perl uses the same for both which simply cannot be right. I suspect
that's probably what the O/S does, unless someone already knows.

Perhaps another way to look at this is with the following script which
starts 100 days ago (because I wasn't sure which date the times change
since they differ all over the world) and also because I'm lazy and
computers are fast. Basically it gets 2 concecutive dates (but it will
break if you run right around DST, but that's a different topic) and
uses those to get the timestamps at midnight on both days. It then
reports the number of seconds in the first day.

#!/usr/bin/perl -w

use Time::Local;

$start=time-86400*100;
for ($i=$start;; $i+=86400)
{
($secs1, $mins1, $hour1, $day1, $mon1, $year1)=localtime($i);
($day2, $mon2, $year2)=(localtime($i+86400))[3..5];

$seconds1=timelocal(0, 0, 0, $day1, $mon1, $year1);
$seconds2=timelocal(0, 0, 0, $day2, $mon2, $year2);

printf "%4d%02d%02d %02d:%02d:%02d SECS: %d\n", $year1+1900,
$mon1+1, $day1,\
$hour1, $mins1, $secs1, $seconds2-$seconds1;
}

-mark

Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2009 codecomments.com