InfiniteLoop has asked for the wisdom of the Perl Monks concerning the following question:

Greetings Monks,
I have been using Time::Piece to represent user entered date as an object. I just uncovered this gem:
perl -e ' use Time::Piece; $end_date = Time::Piece->strptime("31-02-2006", "%d-%m-%Y"); print $end_date;'
prints
Fri Mar 3 00:00:00 2006
I expected Time::Piece to throw an error because there are only 28 days in Feb 2006, however the date is being parsed. Is there a way in which I can have the TIme::Piece throw an error for the above condition ?

Replies are listed 'Best First'.
Re: Time::Piece strangeness
by davidrw (Prior) on Nov 17, 2005 at 19:02 UTC
    Are you stuck with Time::Piece ? Two alternatives from Date::Calc are to either use the check_date() method after manually split'ing the string:
    [davidrw@host ~]$ perl -MDate::Calc=check_date -le 'print check_date(r +everse split /-/, "31-02-2006" )?1:0' 0 [davidrw@host ~]$ perl -MDate::Calc=check_date -le 'print check_date(r +everse split /-/, "28-02-2006" )?1:0' 1
    or to use the Decode_Date_EU() method to do the parse/check all at once:
    [davidrw@host ~]$ perl -MDate::Calc=Decode_Date_EU -le 'print join ":" +, Decode_Date_EU("31-02-2006")' [davidrw@host ~]$ perl -MDate::Calc=Decode_Date_EU -le 'print join ":" +, Decode_Date_EU("28-02-2006")' 2006:2:28
Re: Time::Piece strangeness
by randyk (Parson) on Nov 17, 2005 at 21:54 UTC
    The discussion of date parsing in Time::Piece:
    my $t = Time::Piece->strptime("Sun 3rd Nov, 1943", "%A %drd %b, %Y"); print $t->strftime("%a, %d %b %Y"); Outputs: Wed, 03 Nov 1943 (see, it's even smart enough to fix my obvious date bug)
    suggests that this behaviour is an intended feature. It also suggests a way that you can verify dates:
    use strict; use warnings; use Time::Piece; my $format = "%d-%m-%Y"; while (my $date = <DATA>) { chomp $date; printf("%s %s legitimate\n", $date, (verify_date($date, $format) ? "is" : "is not"), ); } sub verify_date { my ($date_in, $format) = @_; my $t = Time::Piece->strptime($date_in, $format); my $date_out = $t->strftime($format); return $date_in eq $date_out ? 1 : 0; } __DATA__ 28-02-2004 29-02-2004 30-02-2004 28-02-2006 29-02-2006 30-02-2006
    which leads to:
    28-02-2004 is legitimate 29-02-2004 is legitimate 30-02-2004 is not legitimate 28-02-2006 is legitimate 29-02-2006 is not legitimate 30-02-2006 is not legitimate
an alternative with DateTime
by mojotoad (Monsignor) on Nov 18, 2005 at 05:10 UTC
    #!/usr/bin/perl use DateTime::Format::Strptime; my $strp = DateTime::Format::Strptime->new( pattern => '%d-%m-%Y', on_error => 'croak', ); my $dt = $strp->parse_datetime("31-02-2006"); print $dt->datetime, "\n";
    results: "Invalid day of month (day = 31 - month = 02)"

    You can assign any handler you want to 'on_error'

    Cheers,
    Matt

Re: Time::Piece strangeness
by ww (Archbishop) on Nov 17, 2005 at 17:55 UTC

    I have no clue how to make Time::Piece throw the error, but here's a simple way for your code to do so:

    if ( $end_date =~ /[A-Z]{3}\sFeb\s(\d+)\s/i && $1 > 28 ) { print "Wha???? Date is Feb $1 !!!\n"; }

    NB: generalizing to distinguish between leap years and non-leap years left as an exercise for the reader.

      This involves manual string parsing, which half-defeats the purpose of relying on the module to do it for you... also, this is not a february-specific "feature" of Time::Piece:
      [davidrw@host ~]$ perl -MTime::Piece -le 'print Time::Piece->strptime( +"31-06-2006", "%d-%m-%Y")' Sat Jul 1 00:00:00 2006