Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Compare two dates

by Anonymous Monk
on Jul 22, 2019 at 18:55 UTC ( [id://11103158] : perlquestion . print w/replies, xml ) Need Help??

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

Hi Monks!

Whats the better way to compare if a date is greater or equal to another date? I have a test code, but its not right cause its setting to true that "2019-06-08 is greater than 2019-08-01". I would like to see if a date is equal or grater than "2019-08-01".
use strict; my $d1 = '2019-08-01'; my $d2 = '2019-06-08'; if ($d2 >= $1) { print "\n $d2 is greater or equal to $d1\n"; } else { print "\n $d2 is greater or equal to $d1\n"; }
Thanks for looking!

Replies are listed 'Best First'.
Re: Compare two dates
by roboticus (Chancellor) on Jul 22, 2019 at 19:19 UTC

    Anonymous Monk:

    If you had looked at the output, you'd see some hints that would guide you to the right answer:

    • Use of uninitialized value $1... line 7
      Here, perl is trying to tell you that the variable $1 doesn't have a value. Looking at your code, I expect you meant to use $d1 instead. Since 2019-06-08 is greater than *nothing*, the true result is actually correct.
    • Argument "2019-06-08" isn't numeric... line 7
      Perl is now telling you that you're comparing values incorrectly. Since you're using a good date format "YYYY-MM-DD" (I'm guessing), then you can use a simple comparison like the "cmp" function (compares alphabetically). If instead you're using "YYYY-DD-MM", then a simple comparison like that would fail and you'd need to do something fancier (such as my last comment below). If you really wanted to use a numeric comparison, you'd want to convert the values to numbers first.

    Or you could use a Date handling module from http://cpan.org to handle the date parsing and comparison.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: Compare two dates
by AnomalousMonk (Archbishop) on Jul 22, 2019 at 20:39 UTC

    Note that something like '2019-08-01' looks like a kind of ISO 8601-ish date, and as such will  lt le eq ne ge gt cmp stringwise compare properly with other such dates — if they have exactly the same format!

    c:\@Work\Perl\monks>perl -wMstrict -le "my $d1 = '2019-08-01'; my $d2 = '2019-06-08'; ;; my $comparison = $d1 cmp $d2; $comparison = $comparison > 0 ? 'greater than' : $comparison < 0 ? 'less than' : 'equal to'; ;; printf qq{$d1 is %s $d2}, $comparison; " 2019-08-01 is greater than 2019-06-08
    Of course, no date validation is done!


    Give a man a fish:  <%-{-{-{-<

Re: Compare two dates
by ablanke (Prior) on Jul 22, 2019 at 19:39 UTC
    2 minor comments on your code
    if ($d2 >= $1) {
    i guess $1 is a typo.
    print "\n $d2 is greater or equal to $d1\n";
    your print statements are equal.

    one recommended module on CPAN is DateTime

    from the DateTime docs:
    DateTime->compare( $dt1, $dt2 )
    Compare two DateTime objects. The semantics are compatible with Perl's sort() function; it returns -1 if $d1 < $d2, 0 if $d1 == $d2, 1 if $d1 > $d2.
    use strict; use warnings; use feature 'say'; use DateTime; my $d1 = DateTime->new( year => 2019, month => 8, day => 1, time_zone => 'America/Chicago', ); my $d2 = DateTime->new( year => 2019, month => 6, day => 8, time_zone => 'America/Chicago', );
    say DateTime->compare( $d1, $d2 )
    say DateTime->compare( $d1, $d2 ) > 0 ? $d1->ymd .' is greater than ' . $d2->ymd : $d2->ymd .' is greater or equal to ' . $d1->ymd;
    update: sorry, my fault <no excuse>
      your print statements are equal.

      There still seems to be a problem with the print statement because:

      c:\@Work\Perl\monks>perl -wMstrict -le "use feature 'say'; use DateTime; my $d1 = DateTime->new( year => 1999, month => 8, day => 1, time_zone => 'America/Chicago', ); my $d2 = DateTime->new( year => 2019, month => 6, day => 8, time_zone => 'America/Chicago', ); say DateTime->compare( $d1, $d2 ) ? $d1->ymd .' is greater than ' . $d2->ymd : $d2->ymd .' is greater or equal to ' . $d1->ymd; " 1999-08-01 is greater than 2019-06-08
      Maybe something like:
      c:\@Work\Perl\monks>perl -wMstrict -le "use feature 'say'; use DateTime; my $d1 = DateTime->new( year => 1999, month => 8, day => 1, time_zone => 'America/Chicago', ); my $d2 = DateTime->new( year => 2019, month => 6, day => 8, time_zone => 'America/Chicago', ); ;; my $comparison = DateTime->compare($d1, $d2); $comparison = $comparison > 0 ? 'greater than' : $comparison < 0 ? 'less than' : 'equal to'; ;; say sprintf '%s is %s %s', $d1->ymd, $comparison, $d2->ymd; " 1999-08-01 is less than 2019-06-08


      Give a man a fish:  <%-{-{-{-<

      This
      The semantics are compatible with Perl's sort() function; it returns -1 if $dt1 < $dt2, 0 if $dt1 == $dt2, 1 if $dt1 > $dt2.
      is not compatible with your code
      say DateTime->compare( $d1, $d2 ) ? $d1->ymd .' is greater than ' . $d2->ymd : $d2->ymd .' is greater or equal to ' . $d1->ymd;
      Better would be e.g.
      say $d1->ymd . ' is ' . ('earlier than', 'equal to', 'later than')[Dat +eTime->compare( $d1, $d2 ) + 1] . ' ' . $d2->ymd;
Re: Compare two dates
by davido (Cardinal) on Jul 23, 2019 at 14:22 UTC

    It's been pointed out that the date format you're using will sort by string value just fine. That means you can use the stringwise ge operator:

    use strict; use warnings; my $d1 = '2019-08-01'; my $d2 = '2019-06-08'; print "\n$d2 is ", ($d2 ge $d1 ? 'greater than or equal to' : 'less than'), " $d1\n";

    Perl also ships with the module Time::Piece which overloads numeric comparison operators. Therefore you could handle a broader variety of date formats, and also achieve basic date validation using the strptime method it provides:

    use strict; use warnings; use Time::Piece; my ($d1, $d2) = ('2019-08-01', '2019-06-08'); my ($tp1, $tp2) = map {Time::Piece->strptime($_, '%F')} ($d1, $d2); print "\n$d2 is ", ($tp2 >= $tp1 ? 'greater than or equal to' : 'less than'), " $d1\n";

    I know that DateTime was recommended. And that recommendation is fine, but what you're doing is quite simple, and doesn't really need the handling of the 800 pound gorilla. Time::Piece has been bundled with Perl since 5.10 (well, 5.9.5 if you count development releases), so it's been part of the core Perl distribution since December 2007. DateTime pulls in about 110 modules, some from the Perl core, but most from CPAN. It's a fantastic module. There's probably no better date handling module for Perl. But it's big, and you have a little problem.


    Dave

      Perl also ships with the module Time::Piece which overloads numeric comparison operators. Therefore you could handle a broader variety of date formats, and also achieve basic date validation using the strptime method it provides

      I just wanted to second this, and the use of modules in general. Of course, everyone in this thread who pointed out that YYYY-MM-DD works with string comparisons (iff the format is exactly the same for both strings, and this is validated beforehand) is correct, and for simply determining lt/eq/gt, it'll work fine. But in my experience, one date/time task is usually followed closely by another. Next thing you know, your requirements will change, and someone will ask you to check if a date is within a certain number of days of another date. And then you'll need a module anyway.

      One advantage that DateTime has over Time::Piece is that it handles time zones, which may become important if you need to handle times as well - as long as it's just dates, Time::Piece should be just fine.

Re: Compare two dates
by Marshall (Canon) on Jul 23, 2019 at 00:59 UTC
    my $d1 = '2019-08-01'; my $d2 = '2019-06-08';
    These are ISO 3 date formats.
    You can compare these values as strings and the ordering will be
    in date-time order.
    In Perl, string compares are "gt,ge,lt,le,eq,ne" and perhaps more.

    Update: see SQlite time formats.
    There are a dozen well known time formats - see "Time Strings".

    I would assume: '2019-06-08'; means YYYY-MM-DD.
    Using simple ASCII sort, '2019-06-08' will come before '2019-08-01'.

      These are ISO 3 date formats.

      Are you sure? Wikipedia seems to think this standard deals with something called a Renard series. (Update: Also here if you have some money burning a hole in your pocket and you just gotta know.)


      Give a man a fish:  <%-{-{-{-<

        ISO 8601 uses the YYYY-MM-DD format.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

        damned I am! I have been trying to find out what Renard Series is for some time now (edit: after I read about it for the first time just today, above), but all I could get was stereotypes copy-pasted from site to site and online encyclopaedia to o.e. Can anyone please explain in plain language what Renard Series and what Prefered Numbers are? I thought my life was complete but Renard Series set me back. Is this the engineering-clan equivalent of the "secret handshake"? Extra bonus for not looking it up on the net - I had enough of that.

        bw, bliako

Re: Compare two dates
by harangzsolt33 (Chaplain) on Jul 22, 2019 at 21:03 UTC
    Your code has some bugs in it.. I would do it like this :

    use strict; my $d1 = '2019-06-08'; my $d2 = '2019-06-08'; my $RESULT = $d2 cmp $d1; if ($RESULT > 0) { print "\n$d1 is smaller.\n"; } elsif ($RESULT < 0) { print "\n$d1 is larger.\n"; } else { print "\nThe dates are equal!\n"; }
      Yeah, that's correct, but it is slightly more complicated than it needs to be. Using the lt and gt string comparison operators is a bit simpler:
      my $d1 = '2019-06-08'; my $d2 = '2019-06-08'; if ($d1 lt $d2) { print "\n$d1 is smaller.\n"; } elsif ($d2 gt $d1) { print "\n$d1 is larger.\n"; } else { print "\nThe dates are equal!\n"; }
      Or:
      my $d1 = '2019-06-08'; my $d2 = '2019-06-08'; print $d1 lt $d2 ? "$d1 smaller" : $d1 gt $d2 ? "$d1 larger" : "Dates are equal";
Re: Compare two dates
by Ratazong (Monsignor) on Jul 23, 2019 at 05:55 UTC

    As others wrote, with the date-format chosen by you, you just have to apply normal string compare operators.

    However in case you have other date formats (e.g. the format DD.MM.YYYY, which is popular in Germany), this approach won't work.

    I made good experience in converting the date into a number (e.g. DD + 100*MM + 10000*(YYYY-2000) ), and then comparing them.

    HTH, Rata

      Yes, in the USA: MM/DD/YYYY is also common. However just like DD.MM.YYYY, there may not be leading zero'es for the days or months.

      I prefer one of the 12 formats shown in my previous post so that an ASCII sort order will work. Converting a date to a numeric value for comparisons will work and I don't have any big quibble with that. But I would run a regex to convert: DD.MM.YYYY to YYYY-MM-DD for storage and processing purposes. Wenn der Benutzer ein Deutscher ist, würde ich die Ziffernreihenfolge für Anzeigezwecke ändern. Or maybe not? My German is not that great!

      Update:
      I use YYYY-MM-DD as expressed in GMT/UTC time zone for all of my logging. If some conversion to local time formats for a user display is necessary, I do that. However almost all of my work is in GMT/UTC. Yes there is a small technical difference between these two definitions of time. For my work, this difference just doesn't matter. Some DB's use a numeric value for the date and some use a string. Any DB that uses a numeric value for a date, has a function to generate that numeric value from YYYY-MM-DD.

      I use the terms GMT and UTC interchangeably. See Coordinated Universal Time. This wiki article notwithstanding, I recollect that there is some extremely small difference having to do with small fractions of seconds related to this "leap second" stuff. Nobody here has to worry about UTC vs GMT => for all practical purposes, they are the same.

        Comparing strings usually takes longer than comparing numbers. Keep that in mind.