Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Last day of the month. Any shorter

by Scarborough (Hermit)
on May 19, 2004 at 12:23 UTC ( #354580=perlmeditation: print w/ replies, xml ) Need Help??

I was today tasked with produceing code which took a date in the format CCYYMMDD, printing out a message if it was not the last day of a month. For the real job I used a well notated sub routine but wondered how short I could make a stand alone program. With a little help from the vaults of The Monks, the leap year bit(I used the Date::leapyear module), I came up with this
my ($y, $m, $d) = (substr($ARGV[0],0,4),substr($ARGV[0],4,2),substr($A +RGV[0],6,2)); my $ld = (31,28,31,30,31,30,31,31,30,31,30,31)[$m-1]; if (($m-1) == 1){$ld++ if(!($y%100) && !($y%400))||(($y % 100)&&!($y%4 +))} print "$ARGV[0] is NOT a month end day\n" unless $d eq $ld;
I'm sure that some of the Monks, Saints and Gods around the monestery could make this look like War & Peace but it gave a warm feeling of satisfaction.

I'd also like to thank everyone in the monestery for there continuing help

Comment on Last day of the month. Any shorter
Download Code
Re: Last day of the month. Any shorter
by japhy (Canon) on May 19, 2004 at 13:01 UTC
    Note: this code is not "shorter", but it's the code I usually use for this sort of thing.

    The way I usually go about determining the last day of a month is:

    use Time::Local; ### this will get me the Unix time ### for the last day in May # get today's month and year my ($m, $y) = (localtime)[4,5]; # get next month's month and year my ($nm, $ny) = ($m == 11) ? (0, $y+1) : ($m+1, $y); # get noon on the first of next month my $next_month_noon = timelocal(0,0,12, 1,$nm,$ny); # subtract a day from it (86400 seconds) my $month_last_day = $next_month_noon - 86400;
    Now I have a time value in $month_last_day that I can pass to localtime() if I want. I can use this code, then, to determine if a given date is the last day of its month:
    # my code above, as a function sub lastday_time { use Time::Local; # I never do this in real code # I put module uses at the top of # the entire program, not each sub # arguments are assumed to be in "human" terms my ($m, $y) = @_; $m--, $y -= 1900; # perl-ify them my ($nm, $ny) = ($m == 11) ? (0,$y+1) : ($m+1,$y); # return the first of next month, minus one day return timelocal(0,0,12, 1,$nm,$ny) - 86400; } sub is_last_of_month { # arguments are "human" # lastday_time() subtracts 1 from $m and 1900 from $y my ($d, $m, $y) = @_; my $last_day = (localtime lastday_time($m,$y))[3]; # fixed that last line -- I had $d, meant $m return $d == $last_day; }
    There, the is_last_of_month() function returns true if the given date (like (31,5,2004)) is the last of that month, and false otherwise.
    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;
Re: Last day of the month. Any shorter
by Abigail-II (Bishop) on May 19, 2004 at 13:18 UTC
    From the command line:
    perl -MDate::Manip -le 'print "Last day" if UnixDate (ParseDate ($ARGV + [0]), "%B") ne UnixDate DateCalc ($ARGV [0], "1day"), "%B"' YYYYMMDD
    Abigail
      Sadly working on a windows server, weep weep.
        Yes, and? Date::Manip is written in 100% Perl.

        Abigail

Re: Last day of the month. Any shorter
by eserte (Deacon) on May 19, 2004 at 13:23 UTC
    The leapyear check could be made somewhat shorter: ($year % 4 == 0 && (($year % 100 != 0) || ($year % 400 == 0)))
Re: Last day of the month. Any shorter
by Ven'Tatsu (Deacon) on May 19, 2004 at 13:25 UTC
    You can save some characters and avoid some repetition by replacing the first line with
    my ($y, $m, $d) = unpack('A4A2A2', $ARGV[0]);
      I'd like to if I understood what is happening here.
        unpack extracts values of various types from a string based on a format, in this case A4A2A2, and returns them as a list.
        A is a space padded sting of bytes, the number after it is the number of bytes in each value.
        So this format will return a string of the first 4 bytes (stripping away any trailing spaces if there were any) then the next 2 bytes and then the next 2 bytes.
        The end result is identical to the 3 calls to substr, but a bit more concise.
Re: Last day of the month. Any shorter
by eserte (Deacon) on May 19, 2004 at 13:27 UTC
    With Date::Calc:

    perl -l -MDate::Calc=Days_in_Month -e 'print Days_in_Month(@ARGV)' 2004 2

Re: Last day of the month. Any shorter
by BrowserUk (Pope) on May 19, 2004 at 13:29 UTC

    $ARGV[ 0 ] =~ m[^(\d{4})(\d{2})(\d{2})] or die "Bad date: $ARGV[ 0 ]"; print "$ARGV[0] is NOT a month end day" if $3-28-substr(' 303232332323',$2,1)-($2==2and not$1%4xor$1%100xo +r$1%400);

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
      Very, Very good 2 months ago I would have sratched and gone back to bed but at least I know what I'm looking at now. Thanks.
Re: Last day of the month. Any shorter
by jdporter (Canon) on May 19, 2004 at 13:32 UTC
    I think you're taking a risk by trying to implement your own leap-year calculation.
    use Time::Local; sub is_last_day_of_month { my($y,$m,$d) = $_[0] =~ /(.{2,4})(..)(..)/; (gmtime(timegm(0,0,12,$d,$m-1,$y)+24*60*60))[4] != $m-1 } my $a = shift; print is_last_day_of_month($a) ? "$a: is last day of month\n" : "$a: is NOT last day of month\n";
      The code thats gone into production uses something very similer to your example here. I'm only a contractor here and the full time staff(at the moment not perl experts) have to be able to maintain the code after I've gone, so the shortest version in this case is not the best. I just wonted to give myself a bit of a test and see where my skills where compared to the rest of the Monks. Still pretty low I'm afriad.

      "I think you're taking a risk by trying to implement your own leap-year calculation."

      Why? It's a simple and well documented algorithm, you can write it in a line of Perl. What's the risk?

      Personally I keep hoping that a rudimentary date time module (maybe DateTime.pm or a subset) will be included with Perl core, then using the "comes with" will be easier than cutting and pasting the line of perl around :)

      --
      Do not seek to follow in the footsteps of the wise. Seek what they sought. -Basho

        Still not sure why you'd want to. Just doing the leap-year check by hand is longer than some of the entire solutions presented.

        And also:

        use Time::Local; sub is_leapyear { 1 == (gmtime(timegm(0,0,1,28,1,$_[0])+24*60*60))[4] }
        (But this has severe limitations on the range of valid dates input.)
Re: Last day of the month. Any shorter
by johndageek (Hermit) on May 19, 2004 at 15:38 UTC
    use Time::Local; print "last day" if (((localtime(time()+60*60*24))[3]) < 2);

    Just a bit of fun.

    Enjoy!
    Dageek
Re: Last day of the month. Any shorter
by jeffa (Chancellor) on May 19, 2004 at 20:08 UTC
    TIMTWOTDI. This one via Time::Piece, which relies on localtime:
    perl -MTime::Piece -le'$t=localtime->strptime(shift,"%Y%m%d");print "l +ast day" if $t->mday == $t->month_last_day' 20010731
    Haven't tested this, but this one-liner should work for Win32 systems:
    perl -MTime::Piece -le"$t=localtime->strptime(shift,'%Y%m%d');print 'l +ast day' if $t->mday == $t->month_last_day" 20010731

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: Last day of the month. Any shorter
by zude (Scribe) on May 19, 2004 at 21:45 UTC
    my ($y,$m,$d) = unpack "A4A2A2", $ARGV[0]; # ven'tatsu! $d == 30+($m&1^$m>7)-($m==2)*(2-!($y%4||(!($y%100)&&$y%400))) or print "not month end day\n";

    -------
    ~%{${@_[0]}}->{0}&&+++ NO CARRIER

Re: Last day of the month. Any shorter
by Paulster2 (Priest) on May 20, 2004 at 10:50 UTC

    While most, if not all of the nodes already giving feedback to your question have very good ideas, Abigail-II is on the money in my book. Date::Manip is probably the best date manipulation (as the name implies) module that I have come accross. It is a little large, but it works VERY well. It handles almost any form of manipulate, also uses regular speech paterns to decipher what you want to do (such as "last day of the month"). Also, the documentation is NOT lacking. This module will not only make your code shorter (as in the number of lines to produce the same result), it also makes your code a lot easier for someone else to read. It will definitly make life a lot easier where dates are concerned.

    Paulster2


    You're so sly, but so am I. - From the movie Manhunter.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://354580]
Approved by BrowserUk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (7)
As of 2014-10-02 00:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    What is your favourite meta-syntactic variable name?














    Results (41 votes), past polls