• Fregas (unregistered)

    damnit!  I'm the first poster for once, but I don't read Perl!  I have nothing to say!  arrrg!

  • Owen (unregistered) in reply to Fregas

    leap years?  what leap years?

  • (cs) in reply to Fregas
    Anonymous:

    damnit!  I'm the first poster for once, but I don't read Perl!  I have nothing to say!  arrrg!



    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.
  • Munk Yee (unregistered)

    How about those scalar names too?  eek!

    it_is_the_first_of_the_year
    it_is_the_first_of_the_month
  • David (unregistered)

    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.

  • (cs)

    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??

  • th0mas (unregistered) in reply to David

    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";

  • Hermann Klinke (unregistered) in reply to Fregas
    Exactly, that is the WTF: they used Perl!
     
    P.S.: Don't flame me, I don't know anything about Perl, but the language looks so fucked up, that I just had to make that comment ;-).
  • (cs)

    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.

  • (cs) in reply to Rick
    Rick:

    I really like subtracting 1 from $datestring. Does he append "0" when he wants to multiply by 10??


    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.
  • (cs) in reply to Hermann Klinke
    Anonymous:
    P.S.: Don't flame me, I don't know anything about Perl, but the language looks so fucked up, that I just had to make that comment ;-).


    You, my friend, have never seen brainfuck.

    http://www.muppetlabs.com/~breadbox/bf/
  • (cs)

    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)'

  • James Sneeringer (unregistered) in reply to mizhi

    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`;

  • (cs) in reply to James Sneeringer
    Anonymous:
    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`;


    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.

  • (cs) in reply to James Sneeringer
    Anonymous:
    $yesterday = `/bin/date -d yesterday +%Y%m%d`;

    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.

  • (cs)

    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.

  • Andrew Medico (unregistered)

    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!

  • (cs) in reply to Andrew Medico

    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

  • (cs) in reply to mizhi

    Multiplying by 10 by appending a "0" also works...
    my $x = 1234;
    $x .= "0";
    Its still stupid.

    mizhi:
    Rick:

    I really like subtracting 1 from $datestring. Does he append "0" when he wants to multiply by 10??


    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.
  • (cs) in reply to Rick
    Rick:
    Multiplying by 10 by appending a "0" also works...
    my $x = 1234;
    $x .= "0";
    Its still stupid.


    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.
  • (cs)

    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.

  • (cs)

    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.

  • (cs) in reply to whojoedaddy
    whojoedaddy:
    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.


    man perl
    man perldoc

    http://perldoc.perl.org/perl.html
  • -L (unregistered) in reply to mizhi
    mizhi:

    You, my friend, have never seen brainfuck.

    http://www.muppetlabs.com/~breadbox/bf/

    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...
  • (cs) in reply to dubwai
    dubwai:
    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.

    You sure it wasn't a email address validator?

    That's the output from make_rfc822re(), a function that is hidden inside the module.

  • Hank Miller (unregistered)

    elsif ($it_is_the_first_of_the_year == 0) 
    I find it "interesting" that the previous check was inconsistant:
    if ($it_is_the_first_of_the_month)
    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.




  • (cs)

    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)

  • (cs) in reply to Maurits
    original code:

       .
       .
       .
      <font size="3"> elsif ($datestring =~ /01$/) {

       $it_is_the_first_of_the_month = 1;

       $monthofyear = /bin/date +%m;

       chomp $monthofyear;

       $monthofyear = $monthofyear - 1;

       # Here are our thirty-day months

       if (($monthofyear == "04") || ($monthofyear == "06") ||

           ($monthofyear == "09") || ($monthofyear == "11")) {

          $day = 30;

       # Our single 28-day month

       } elsif ($monthofyear == "02") {

          $day = 28;

       # Fall through as a 31-day month

       } else {

          $day = 31;

       }</font>



    Maurits:
    As previously noted...
    The code will break ... on March 1 in leap years (will return invalid date February 31.)  Otherwise it looks OK


    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?
  • (cs) in reply to -L
    Anonymous:
    mizhi:

    You, my friend, have never seen brainfuck.

    http://www.muppetlabs.com/~breadbox/bf/

    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...

    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.)
  • (cs) in reply to th0mas
    Anonymous:
    but it's easier just to:

    $sec_in_day = 60*60*24;
    $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";



    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.



  • (cs) in reply to eown

    $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);
    }

  • (cs) in reply to eown

    Oops should be
    if ($month eq "01" and $day eq "01")
    {    $ftpdate = ($year - 1) . "1231";
    } ...

  • (cs) in reply to Maurits

    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;
    }

  • (cs) in reply to eown
    eown:
    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.

    eown:

    which in my time zone outputs:

    today is 30/10/2005
    yesterday was 30/10/2005

    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

  • (cs) in reply to loneprogrammer
    loneprogrammer:
    What does it output if you write it correctly?

    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.

  • terry (unregistered)

    jesus.

    i would eat someone if they put that code out. like a snake. face first.

  • Richard (unregistered) in reply to Fregas

    Nobody reads perl, they only write it.

  • Chris Brien (unregistered)

    May I be the first to say...

    $ftpdate = /bin/date -d yesterday +%Y%m%d

    Thank you and goodnight.

  • Aristotle (unregistered) in reply to Hermann Klinke
    Anonymous:
    I don't know anything about Perl, but the language looks so fucked up, that I just had to make that comment ;-).

    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:

    my $ftpdate = `date -d yesterday +%Y%m%d`;
  • Aristotle (unregistered) in reply to Chris Brien

    Damn you Chris, beat me to the punch by a few minutes.

  • (cs) in reply to Aristotle
    Anonymous:
    Damn you Chris, beat me to the punch by a few minutes.

    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.
  • toffer (unregistered) in reply to Maurits

    Whatever happened to laziness?

    use Date::Calc qw(Add_Delta_Days Today);

    $ftpdate = sprintf("%04d-%02d-%02d",
               Add_Delta_Days(Today(), -1));
    print $ftpdate, "\n";


    You may have to install Date::Calc, of course. I generally find it's worth it. :)


  • (cs) in reply to Chris Brien
    Anonymous:
    May I be the first to say...

    $ftpdate = /bin/date -d yesterday +%Y%m%d

    Thank you and goodnight.



    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];

  • Alan (unregistered) in reply to David

    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.

  • (cs) in reply to Maurits

    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!

  • (cs) in reply to loneprogrammer
    loneprogrammer:
    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.

    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.

    Anonymous:
    use Date::Calc qw(Add_Delta_Days Today);
    

    $ftpdate = sprintf("%04d-%02d-%02d", Add_Delta_Days(Today(), -1)); print $ftpdate, "\n";

    use DateTime;
    
    $ftpdate = DateTime->now->subtract( days => 1 )->ymd( '' );

    ;)

    Alan:
    A fair method involves some hacking with the timelocal() and localtime() built-ins, maybe slightly tricky but not very complex

    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.

  • Sean Connery (unregistered) in reply to Chris Brien
    Anonymous:
    May I be the first to say...

    $ftpdate = /bin/date -d yesterday +%Y%m%d

    Thank you and goodnight.



    Nope, second.

  • (cs) in reply to Maurits
    Maurits:
    You could do
    $ftpdate = `/bin/date -d yesterday +%Y%m%d`
    chomp($ftpdate);

    chomp works on assignments, too:

    chomp($ftpdate = `/bin/date`);

    Although I think it's more readable to use two lines for this.
  • Rustam (unregistered) in reply to mizhi

    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.

  • (cs) in reply to loneprogrammer
    loneprogrammer:
    Anonymous:
    Damn you Chris, beat me to the punch by a few minutes.

    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.


    Lest we not forget that supposedly, this came out of a shop that develops on a number of different platforms... portability much?

Leave a comment on “Perly Dates”

Log In or post as a guest

Replying to comment #:

« Return to Article