- Feature Articles
- CodeSOD
- Error'd
- Forums
-
Other Articles
- Random Article
- Other Series
- Alex's Soapbox
- Announcements
- Best of…
- Best of Email
- Best of the Sidebar
- Bring Your Own Code
- Coded Smorgasbord
- Mandatory Fun Day
- Off Topic
- Representative Line
- News Roundup
- Editor's Soapbox
- Software on the Rocks
- Souvenir Potpourri
- Sponsor Post
- Tales from the Interview
- The Daily WTF: Live
- Virtudyne
Admin
damnit! I'm the first poster for once, but I don't read Perl! I have nothing to say! arrrg!
Admin
leap years? what leap years?
Admin
It's another reinventing the wheel. The so called 'Perl Guy' uses /bin/date to reproduce a handful of Perl date functions, which Alex linked to.
It's a pretty big WTF, but it doesn't burn like some of them do.
Admin
How about those scalar names too? eek!
Admin
haha...so if it is the first of the year it returns the first day of last year as yesterday's date?
I also love how he has a separate if block after the first chunk of code to assign a value to $ftpdate when he could have done that in the first block of code. He was smart enough to do one elsif if it was not the first of the year, but couldn't do another if it was also not the first of the month.
Of course I'm not a perl programmer, so I have no idea how much worse this general method is than the built in module.
Admin
I only caught 2 failures. Did Jeff have to step in on the day after February 29th or on January 1st???
I really like subtracting 1 from $datestring. Does he append "0" when he wants to multiply by 10??
Admin
Of course I'm not a perl programmer, so I have no idea how much worse this general method is than the built in module.
(not going to try to quoteblock it, forum will just spit it out)
anyways, we could use localtime(time()) to convert to a nice array of day/month/etc then do the "are we first of month", etc, but it's easier just to:
$sec_in_day = 606024;
$yesterday = time() - $sec_in_day;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($yesterday);
print "yesterday was $mday/" . ($mon+1) . "/" . ($year+1900) . "\n";
Admin
Admin
Hey now, this is the Perl Way: TIMTOWTDI!
To be honest, 'Perl Guy' looks like a guy who enjoys his unix. It's not uncommon to find code that captures the output of a command to be used in a program/script.
That said, doing it for /bin/date is pretty silly. Not to mention the fact that he does it incorrectly; ignoring leap years, and saying that the previous day of the first of the year is December 1st. But those have already been mentioned.
The more serious wtfery is the fact that this code is totally non-portable. Try running it on Windows.
Admin
It works. Perl will automatically treat $datestring as a number since the string itself is composed of contiguous digits and the literal 1 is being subtracted from it.
Admin
You, my friend, have never seen brainfuck.
http://www.muppetlabs.com/~breadbox/bf/
Admin
Those DateTime modules are not builtin. They have to be downloaded separately, so Perl beginners don't know about them. That's still not a reason to resort to /bin/date!
# Print the time/date 24 hours ago
perl -e 'print scalar localtime(time()-606024)'
Admin
Even if the guy is a Unix guy who writes Perl code like shell scripts (not uncommon), it's still a WTF because he doesn't bother to use the built-in facilities of /bin/date to automatically figure out what "yesterday" was:
$yesterday = `/bin/date -d yesterday +%Y%m%d`;
Admin
Yeah, I actually just remembered that the date utility can do all that work for you. I've used it before in shell scripts (not perl though), php also has functions that work similar to date.
Admin
That will only work in the GNU version of /bin/date, so it won't run on many systems.
Stick to Perl, it is more portable.
Admin
I've found that there is a pretty big difference between a Perl "programmer" and a Perl "scripter". This Perl Guy definitely seems to fall in the latter category.
Perl, although its syntax is awkward at times, does have good modularization, layering, and object-oriented capabilities. Real Perl programs are written this way.
Then you have the Perl scripter...who rights quick one file scripts. These are the crontab and other quick-fix kings. It's more about getting the little script to do exactly what it's supposed to do on this exact system at this time than it is about writing good portable modular reusable code.
I'm not saying this isn't a WTF (with how the logic is just wrong), but that it's non-portable and haphazard doesn't really surprise me.
Admin
There's an additional WTF of calling /bin/date multiple times and expecting the date to be the same every time. Better not run this function at 23:59:59!
Admin
As previously noted...
The code will break on January 1 every year (will return a year ago rather than a day ago), and on March 1 in leap years (will return invalid date February 31.) Otherwise it looks OK
Admin
Multiplying by 10 by appending a "0" also works...
my $x = 1234;
$x .= "0";
Its still stupid.
Admin
Sorry, I misread your original comment. I thought you said "multiply by 0"
In anycase, you're talking about two different types of operations: mathematic and string. Not sure if it necessarily qualifies as stupid.
Admin
This is nothing. I was once conversing on a forum with someone who posted a date validation written entirely in regex. Theoretically, it worked but it was uglier than sin.
Admin
Is there good documentation on the api for perl? Closest thing I've tried to perl was cshell. I know those are probably extremely different. The problem I kept running into was lack of api documentation, everything was about bash.
Admin
man perl
man perldoc
http://perldoc.perl.org/perl.html
Admin
Well, brainfuck doesn't use every single special character on the keyboard as a special variable (I know for sure $| is legal in Perl, wouldn't be surprised if it also had $/, $\, $-, and $*...)
TMTOWTDI is really a killer for maintainability. In a team of four programmers I bet every single one has her own idioms for doing trivial stuff. Perl has them all.
I am currently in the unfortunate position that I need to do a code review for a 2 KSLOC Perl module I wrote three years ago as a summer intern. Documentation? Nope. Using Perl in imaginative ways? Yup. I'll see if I find anything worth a WTF entry in there...
Admin
You sure it wasn't a email address validator?
That's the output from make_rfc822re(), a function that is hidden inside the module.
Admin
I find it "interesting" that the previous check was inconsistant:
Both those checks should be eliminated though - the $it_is_the_first_of_the_month case is just a few more lines on the month case above, and the ($it_is_the_first_of_the_year == 0) case is an else to the first two ifs.
Not to mention the bugs that everyone else has mentioned.
Admin
Even crap like the following is less retarded than that WTF
<FONT face="Courier New">my ($sec,$min,$hour,$mday,$mon,$year, $wday,$yday,$isdst) = localtime time - 24 * 3600;
my $date = (1900 + $year) . "-" . $mon . "-" . $mday;
print $date;</FONT>
(0-padding left as an exercise for the reader)
Admin
Perhaps I'm just missing something, but won't it return February 28 on a leap year March 1, rather than February 29 like it's supposed to?
Admin
http://perldoc.perl.org/perlvar.html shows...
$| $! $_ $/ $` $' $+ $* $. $, $\ $" $; $# $% $= $- $~ $^ $: $? $@ $$ $<
%!
@+ @-
And a lot more I'm not going to bother to list. Every one of them has a more verbose version, but perl hackers prefer the meaningless line noise version most of the time. (C hackers would probably use them if available. Hmm... I wonder if #define would work here.)
Admin
You are assuming that every day has 60*60*24 seconds. In a country with daylight savings time this asumption will fail twice a year. See:
#!/usr/bin/perl
# On Sun Oct 30 23:23:20 CEST 2005:
$today = 1130711000;
$sec_in_day = 60*60*24;
$yesterday = $today - $sec_in_day;
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($today);
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($yesterday);
print "today is $mday/" . ($mon+1) . "/" . ($year+1900) . "\n";
print "yesterday was $mday/" . ($mon+1) . "/" . ($year+1900) . "\n";
which in my time zone outputs:
today is 30/10/2005
yesterday was 30/10/2005
ymmv, but in every time zone with daylight savings time you'll find something like this.
Admin
$datestring = `/bin/date +%Y%m%d`;
($year, $month, $day) = $datestring =~ /(\d\d\d\d)(\d\d)(\d\d)/;
$daysinfebruary =
$year % 400 == 0 ? 29 : # 1600, 2000, 2400 ...
$year % 100 == 0 ? 28 : # 1800, 1900, 2100 ...
$year % 4 == 0 ? 29 : # 2004, 2008, 2012 ...
28; # 2005, 2006, 2007, ...
@daysinmonth = (undef, 31, $daysinfebruary, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
if ($month eq "01" and $day eq "01")
{ $ftpdate = ($year - 1) . $month . $day;
} elsif ($day eq "01")
{ $ftpdate = $year . ($month - 1) . $daysinmonth[$month - 1];
} else
{ $ftpdate = $year . $month . ($day - 1);
}
Admin
Oops should be
if ($month eq "01" and $day eq "01")
{ $ftpdate = ($year - 1) . "1231";
} ...
Admin
Double oops forgot zero-padding
$datestring =
/bin/date +%Y%m%d
;($year, $month, $day) = $datestring =~ /(\d\d\d\d)(\d\d)(\d\d)/;
$daysinfebruary =
$year % 400 == 0 ? 29 : # 1600, 2000, 2400 ...
$year % 100 == 0 ? 28 : # 1800, 1900, 2100 ...
$year % 4 == 0 ? 29 : # 2004, 2008, 2012 ...
28; # 2005, 2006, 2007, ...
@daysinmonth = (undef, 31, $daysinfebruary, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
if ($month eq "01" and $day eq "01")
{ $ftpdate = ($year - 1) . "1231";
} elsif ($day eq "01")
{ $ftpdate = $year . zeropad($month - 1) . $daysinmonth[$month - 1];
} else
{ $ftpdate = $year . $month . zeropad($day - 1);
}
sub zeropad($)
{ my $num = shift;
return $num < 10 ? "0$num" : $num;
}
Admin
What does it output if you write it correctly? The reason it prints the same thing twice is that you clobbered all the variables with the result of localtime($yesterday).
# Here's my code:
$today = 1130711000;
$yesterday = $today - 60*60*24;
print scalar localtime($today), "\n";
print scalar localtime($yesterday), "\n";
# Results:
Sun Oct 30 17:23:20 2005
Sat Oct 29 18:23:20 2005
Admin
I see where you were going with that now . . .
# Sun Oct 30 23:23:00 EST 2005
$today = 1130732580;
$yesterday = $today - 60*60*24;
print scalar localtime($today), "\n";
print scalar localtime($yesterday), "\n";
# Result:
Sun Oct 30 23:23:00 2005
Sun Oct 30 00:23:00 2005
This happens to be a 25 hour day, because the clock says 1AM for two hours.
Admin
jesus.
i would eat someone if they put that code out. like a snake. face first.
Admin
Nobody reads perl, they only write it.
Admin
May I be the first to say...
$ftpdate =
/bin/date -d yesterday +%Y%m%d
Thank you and goodnight.
Admin
Real programmers can write FORTRAN in any language...
I know quite a lot about Perl and I can assure you Perl doesn't intrinsically look fucked up.
That said, the fact that this guy is shelling out to
/bin/date
is not a serious WTF. The people writing replies and trying to implement date math in 5 lines are much worse. The fact that he is groping around with a regex is the primary WTF, that he is shelling out multiple times is the secondary. His date math bugs are just cherry topping.Date math is hard. Really, really, really ridiculously hard. That's why the dudes who set out to write the DateTime modules started that project in the first place; so noone would have to implement the math again and get it wrong again.
The funny thing is, this coder could have had
/bin/date
do all this for him:Admin
Damn you Chris, beat me to the punch by a few minutes.
Admin
First, Chris wasn't the first to mention it.
Second, it won't run on most versions of Unix. Just because it works on Linux doesn't mean it's Unix.
Admin
Whatever happened to laziness?
Admin
Actually that has a trailing newline. You could do
$ftpdate =
/bin/date -d yesterday +%Y%m%d
chomp($ftpdate);
or if you want to keep it a one-liner:
$ftpdate = (split /\n/,
/bin/date -d yesterday +%Y%m%d
)[0];Admin
I am a perl guy and I can say that getting yesterday's date is not a complicated matter and doesn't even need any "built-in" modules. A fair method involves some hacking with the timelocal() and localtime() built-ins, maybe slightly tricky but not very complex; an expert doesn't think twice to pull in DateTime or Date::Calc and have it all done in about 2 lines of code.
Admin
Once you bring dst into the equation you might as well throw out localtime() alltogether and go straight to gmt. Don't forget leap seconds!
Admin
It's not Unix, but I am no fan of using the lowest common denominator either. That gets you things like AWT. Or database apps that use only the widely supported subset of SQL92. Portability has a price; this does not seem like a case where paying the price would have been worth it. Things would be different if this were supposed to go onto the CPAN or such.
;)
Actually, it is. You can write code that works almost all of the time fairly easily, but there are way way more edge cases than you care to think about. Don't be fooled by the seeming simplicity, like so many others.
Admin
Nope, second.
Admin
chomp works on assignments, too:
chomp($ftpdate = `/bin/date`);
Although I think it's more readable to use two lines for this.
Admin
Well, I'm sure it would run in Cygwin on Windows. So it's portable to the extent of whether you're willing to install Cygwin, to "make" it portable.
And, I suppose you could argue it's not portable on a couple days a year no matter what.
Admin
Lest we not forget that supposedly, this came out of a shop that develops on a number of different platforms... portability much?