Beefy Boxes and Bandwidth Generously Provided by pair Networks DiBona
Think about Loose Coupling
 
PerlMonks  

Need help comparing 4 dates

by dirtdog (Beadle)
on Oct 02, 2010 at 21:19 UTC ( #863112=perlquestion: print w/ replies, xml ) Need Help??
dirtdog has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks! I'm having trouble trying to compare 4 dates. Each row of data that i have contains 4 dates and i need to compare them and print the order. I have some sample code below.

#!/usr/bin/env perl use warnings; use strict; use Data::Dumper; use Date::Manip; my $flag; my $jim; my $bob; while (<DATA>) { if ( $. == 1 ) { print; next; } my $aref = [split /~/]; #print "JIM is $aref->[1], BOB is $aref->[2]\n"; $flag = Date_Cmp($aref->[1],$aref->[2]); if ($flag) { $jim = '1'; $bob = '2';} else { $bob = '1'; $jim = '2';} print "$aref->[0] $jim $bob\n"; } __DATA__ EVENT JIM BOB SAM JACK PTRED~09/29/10 03:23:05 PM ~09/28/10 02:21:09 PM ~09/26/10 11:00:03 AM + ~09/27/10 09:33:41 PM RED~08/29/10 01:55:00 AM ~08/30/09 12:10:10 PM ~08/27/10 08:16:21 PM ~ +09/01/10 12:12:12 AM INT~07/04/10 03:21:15 AM ~07/08/10 04:17:33 PM ~06/30/10 04:22:11 AM ~ +06/28/10 10:11:01 PM PTRED~06/19/10 09:19:55 PM ~04/25/10 07:39:22 PM ~09/16/10 10:34:24 AM + ~07/22/10 06:19:38 PM RED~04/29/10 12:10:59 AM ~04/20/10 02:13:33 AM ~07/17/10 01:00:05 PM ~ +09/01/10 11:10:15 PM INT~05/23/10 11:11:11 PM ~01/08/10 10:45:12 PM ~05/15/09 03:29:37 AM ~ +05/18/09 12:59:59 PM

For the purposes of this question i added the __DATA__ but date_cmp doesn't seem to recognize the date format when i do this. When i actually return the dates from oracle and compare 2 dates date_cmp works fine. I'm explaining this because the real issue is that i'm struggling with the concept of how to compare 4 dates..not that __DATA__ doesn't recognize the date formats i added for this sample. The goal is to print output as follows:

EVENT JIM BOB SAM JACK PTRED 4 3 1 2 RED 3 1 2 4 INT 3 4 2 1 PTRED 2 1 4 3 RED 2 1 3 4 INT 4 3 1 2

Any help provided will be mucho appreciated! Thanks Tony

Comment on Need help comparing 4 dates
Select or Download Code
Re: Need help comparing 4 dates
by ig (Vicar) on Oct 02, 2010 at 23:17 UTC

    I'm not sure why you say Date_Cmp doesn't recognize the date formats. Maybe because your test of $flag isn't doing what you think it should. $flag can be -1, 0 or 1. Your output suggests the same result whenever the dates for Bob and Jim are different, regardless of which is the earlier date, and something else when they are the same. You might try the test $flag > 0 and see if that gives you the result you are expecting.

      Thanks to all for the replies, but I guess my post was a little confusing. My question really is does anyone have a recommendation for the best way to compare 4 dates and then print the order. Comparing 2 dates is easy, but it gets tricky when comparing 4 dates and displaying the order from the earliest to latest as below:

      EVENT JIM BOB SAM JACK PTRED 4 3 1 2 RED 3 1 2 4 INT 3 4 2 1 PTRED 2 1 4 3 RED 2 1 3 4 INT 4 3 1 2

      I got some ideas from ww and will give it a try. Thanks

        Given a list of dates, to calculate a corresponding list of ordinals I might do something like the following:

        # Make a list of tuples: (index, date) my @list = map { [ $_, $dates[$_] ] } (0..$#dates); # Sort the list by date @list = sort { Date_Cmp($a->[1], $b->[1]) } @list; # Determine the corresponding ordinal numbers my @ordinals; foreach (0..$#dates) { $ordinals[ $list[$_]->[0] ] = $_ + 1; }

        I don't find this very elegant - there may be much better ways. There may be a module that does this, though I don't know one.

Re: Need help comparing 4 dates
by ww (Bishop) on Oct 03, 2010 at 00:10 UTC
    "When i actually return the dates from oracle and compare 2 dates date_cmp works fine. I'm explaining this because the real issue is that i'm struggling with the concept of how to compare 4 dates..not that __DATA__ doesn't recognize the date formats i added for this sample. "
    1. Then what Oracle is returning includes some sort of timezone information absent from your __DATA__ section. See "ERROR reporting," below. [BUT, see also ig's reply to this node, which may clear dirtdog of malfeasance in regard to timezone & error reporting.]
    2. Perhaps you meant to repeat your reference to "data_cmp" (sic) but it's just as well you didn't, as the proper reference is to "Date_Cmp". As to your statement later re __DATA__ you should know __DATA__ doesn't recognize anything. Perl recognizes what follows __DATA__ as data to be used by the script above (assuming the script reads that data).

    Returning to Date_Cmp, the doc (perldoc Date::Manip)tells us:

    . Compare two dates $date1 = ParseDate($string1); $date2 = ParseDate($string2); $flag = Date_Cmp($date1,$date2); if ($flag<0) { # date1 is earlier } elsif ($flag==0) { # the two dates are identical } else { # date2 is earlier }

    Also in the doc, and directly relevant to your desire to compare four dates,

    Among other things, Date::Manip allow you to: ... 2. Compare two dates, entered in widely different formats to determine which is earlier

    "Compare two dates" is relevant. Comparing two dates is one of the things Date::Manip does; comparing four (without more help from you) is NOT. Since you want to do more, you'll need to change your approach.

    For example, you could convert all the date_times to seconds and sort those values, then assign ranks, based on the order in which they sorted (Tedious, but straightforward). Or you could use another capability in Date::Manip, the ability to determines deltas, by comparing each date_date in each row to the date_time of execution, and ranking (ordering) the deltas. Others will undoubtedly suggest other approaches, some of which may be superior to anything here.

    ERROR reporting: When executed, your code outputs an error message, "ERROR: Date::Manip unable to determine TimeZone. You didn't tell us that; did you notice it?

    Turning again to the doc, you'll find several discussions of TimeZone which may help you turn the code you posted into code that actually compiles. Doing so -- preferably before posting -- is a good way to help assure yourself that Monks inclined to help aren't ( annoyed | turned off ) by the failure to do so.

    Update: Likely irrelevant portions stricken. My bad for failing to consider OSen (happened to be on a Win32 machine at the time) and vintage of said machine's modules.

      I ran the OPs code on a linux system and didn't see an error about timezone. Maybe the OP didn't have such an error either.

      Older versions of Date::Manip need to be told the time zone when running on Windows, but were generally able to determine the time zone from the system on *nix platforms. Documentation for current version of Date::Manip suggests it tries to get the time zone by reading the registry on Windows, so it may no longer need to be told - I haven't tried current version on Windows.

      I am guessing you are running an older version of Date::Manip (5.x) on Windows and the OP is running current version on Windows or any verson on some other platform.

Re: Need help comparing 4 dates
by jwkrahn (Monsignor) on Oct 03, 2010 at 05:08 UTC

    This appears to work:

    #!/usr/bin/perl use warnings; use strict; my @headers; while ( <DATA> ) { if ( $. == 1 ) { print; @headers = split; next; } chomp; my %data; @data{ @headers } = split /\s*~\s*/; for ( @data{ @headers[ 1 .. $#headers ] } ) { my ( $mon, $day, $year, $hour, $min, $sec ) = /\d\d/g; $hour += 12 if /PM/i; $_ = join '', $year, $mon, $day, $hour, $min, $sec; } my $new_order = 1; my %ordered = ( @headers[ 0, 0 ], map { $headers[ $_ ], $new_order++ } sort { $data{ $headers[ $a ] } <=> $data{ $headers[ $b ] } } 1 .. $#headers ); printf "%-7s %-7s %-7s %-7s %-7s\n", @ordered{ @headers }; } __DATA__ EVENT JIM BOB SAM JACK PTRED~09/29/10 03:23:05 PM ~09/28/10 02:21:09 PM ~09/26/10 11:00:03 AM + ~09/27/10 09:33:41 PM RED~08/29/10 01:55:00 AM ~08/30/09 12:10:10 PM ~08/27/10 08:16:21 PM ~ +09/01/10 12:12:12 AM INT~07/04/10 03:21:15 AM ~07/08/10 04:17:33 PM ~06/30/10 04:22:11 AM ~ +06/28/10 10:11:01 PM PTRED~06/19/10 09:19:55 PM ~04/25/10 07:39:22 PM ~09/16/10 10:34:24 AM + ~07/22/10 06:19:38 PM RED~04/29/10 12:10:59 AM ~04/20/10 02:13:33 AM ~07/17/10 01:00:05 PM ~ +09/01/10 11:10:15 PM INT~05/23/10 11:11:11 PM ~01/08/10 10:45:12 PM ~05/15/09 03:29:37 AM ~ +05/18/09 12:59:59 PM
Re: Need help comparing 4 dates (sort errors?)
by BrowserUk (Pope) on Oct 03, 2010 at 05:24 UTC

    Comparing more than two values--is sorting.

    In theory, this should work(Update:see 2 posts down for fixed code):

    #! perl -slw use warnings; use strict; use Data::Dumper; use Date::Manip; print scalar <DATA>; while (<DATA>) { chomp; my @dates = split '~'; my $label = shift @dates; my @order = map $_+1, sort{ Date_Cmp( $dates[ $a ], $dates[ $b ] ); } 0 .. $#dates; print join "\t", $label, @order; } __DATA__ EVENT JIM BOB SAM JACK PTRED~09/29/10 03:23:05 PM ~09/28/10 02:21:09 PM ~09/26/10 11:00:03 AM + ~09/27/10 09:33:41 PM RED~08/29/10 01:55:00 AM ~08/30/09 12:10:10 PM ~08/27/10 08:16:21 PM ~ +09/01/10 12:12:12 AM INT~07/04/10 03:21:15 AM ~07/08/10 04:17:33 PM ~06/30/10 04:22:11 AM ~ +06/28/10 10:11:01 PM PTRED~06/19/10 09:19:55 PM ~04/25/10 07:39:22 PM ~09/16/10 10:34:24 AM + ~07/22/10 06:19:38 PM RED~04/29/10 12:10:59 AM ~04/20/10 02:13:33 AM ~07/17/10 01:00:05 PM ~ +09/01/10 11:10:15 PM INT~05/23/10 11:11:11 PM ~01/08/10 10:45:12 PM ~05/15/09 03:29:37 AM ~ +05/18/09 12:59:59 PM

    It populates an array with the dates; sorts the indices of the array, by the (ascending) values of the dates indexed; then adds one to the ordered indices to produce your "ranking value".

    It really should work, but it doesn't! It produces this output which is clearly wrong, but for reasons I do not (yet) understand:

    c:\test>junk51 EVENT JIM BOB SAM JACK PTRED 3 4 2 1 RED 2 3 1 4 INT 4 3 1 2 PTRED 2 1 4 3 RED 2 1 3 4 INT 3 4 2 1

    At first I thought that maybe Date_Cmp(), needed the dates to be pre-parsed, but that didn't fix it.

    Then I thought that maybe D::M was getting confused by the 2 digit years, so I fixed that up, but no change either.

    So then I thought that the (elusively documented) "flag" returned by Date_Cmp() might be incompatible with the expectations of sort, but that doesn't seem to be the case either.

    This modified version:

    #! perl -slw use warnings; use strict; use Data::Dumper; use Date::Manip; print scalar <DATA>; while (<DATA>) { chomp; my @dates = split '~'; my $label = shift @dates; my @parsed = map{ s[/(\d\d) ][/20$1 ]; ParseDate( $_ ) } @dates; my @order = map $_+1, sort{ my $res = Date_Cmp( $dates[ $a ], $dates[ $b ] ); print "$dates[ $a ] <-> $dates[ $b ] := $res"; $res; } 0 .. $#parsed; print join "\t", $label, @order; } __DATA__ EVENT JIM BOB SAM JACK PTRED~09/29/10 03:23:05 PM ~09/28/10 02:21:09 PM ~09/26/10 11:00:03 AM + ~09/27/10 09:33:41 PM RED~08/29/10 01:55:00 AM ~08/30/09 12:10:10 PM ~08/27/10 08:16:21 PM ~ +09/01/10 12:12:12 AM INT~07/04/10 03:21:15 AM ~07/08/10 04:17:33 PM ~06/30/10 04:22:11 AM ~ +06/28/10 10:11:01 PM PTRED~06/19/10 09:19:55 PM ~04/25/10 07:39:22 PM ~09/16/10 10:34:24 AM + ~07/22/10 06:19:38 PM RED~04/29/10 12:10:59 AM ~04/20/10 02:13:33 AM ~07/17/10 01:00:05 PM ~ +09/01/10 11:10:15 PM INT~05/23/10 11:11:11 PM ~01/08/10 10:45:12 PM ~05/15/09 03:29:37 AM ~ +05/18/09 12:59:59 PM

    Produces this:

    c:\test>junk51 EVENT JIM BOB SAM JACK 09/29/2010 03:23:05 PM <-> 09/28/2010 02:21:09 PM := 1 09/26/2010 11:00:03 AM <-> 09/27/2010 09:33:41 PM := -1 09/28/2010 02:21:09 PM <-> 09/26/2010 11:00:03 AM := 1 09/28/2010 02:21:09 PM <-> 09/27/2010 09:33:41 PM := 1 PTRED 3 4 2 1 08/29/2010 01:55:00 AM <-> 08/30/2009 12:10:10 PM := 1 08/27/2010 08:16:21 PM <-> 09/01/2010 12:12:12 AM := -1 08/30/2009 12:10:10 PM <-> 08/27/2010 08:16:21 PM := -1 08/27/2010 08:16:21 PM <-> 08/29/2010 01:55:00 AM := -1 08/29/2010 01:55:00 AM <-> 09/01/2010 12:12:12 AM := -1 RED 2 3 1 4 07/04/2010 03:21:15 AM <-> 07/08/2010 04:17:33 PM := -1 06/30/2010 04:22:11 AM <-> 06/28/2010 10:11:01 PM := 1 07/04/2010 03:21:15 AM <-> 06/28/2010 10:11:01 PM := 1 07/04/2010 03:21:15 AM <-> 06/30/2010 04:22:11 AM := 1 INT 4 3 1 2 06/19/2010 09:19:55 PM <-> 04/25/2010 07:39:22 PM := 1 09/16/2010 10:34:24 AM <-> 07/22/2010 06:19:38 PM := 1 04/25/2010 07:39:22 PM <-> 07/22/2010 06:19:38 PM := -1 07/22/2010 06:19:38 PM <-> 06/19/2010 09:19:55 PM := 1 PTRED 2 1 4 3 04/29/2010 12:10:59 AM <-> 04/20/2010 02:13:33 AM := 1 07/17/2010 01:00:05 PM <-> 09/01/2010 11:10:15 PM := -1 04/20/2010 02:13:33 AM <-> 07/17/2010 01:00:05 PM := -1 07/17/2010 01:00:05 PM <-> 04/29/2010 12:10:59 AM := 1 RED 2 1 3 4 05/23/2010 11:11:11 PM <-> 01/08/2010 10:45:12 PM := 1 05/15/2009 03:29:37 AM <-> 05/18/2009 12:59:59 PM := -1 01/08/2010 10:45:12 PM <-> 05/15/2009 03:29:37 AM := 1 01/08/2010 10:45:12 PM <-> 05/18/2009 12:59:59 PM := 1 INT 3 4 2 1

    Each individual comparison produces the expected result, but still the ordering is wrong. For all teh world it looks like sort is making mistakes :)

    At this point I ran out of ideas, so I throw it over to the monks to point out my mis deliberate error?


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      It populates an array with the dates; sorts the indices of the array, by the (ascending) values of the dates indexed; then adds one to the ordered indices to produce your "ranking value".

      But the 'ranking value' is really the date itself: the earliest date is 'first place', the latest date is 'fourth place' (in the OPed example data). It's necessary to convert each date, at some point, into its 'place' from 1 to n.

      Once the ordered dates have been converted to place values, they have to be restored to the original order of the 'players' Jim .. Jack for printing.

        Duh! Of course. Thanks.

        Once I've sorted the indices by the dates, in order to rank them; I need to sort the indices by that ranking to get them back into the original order:

        #! perl -slw use warnings; use strict; use Data::Dumper; use Date::Manip; print scalar <DATA>; while (<DATA>) { chomp; my @dates = split '~'; my $label = shift @dates; my @order = sort{ Date_Cmp( $dates[ $a ], $dates[ $b ] ); } 0 .. $#dates; print join "\t", $label, map $_+1, sort{ $order[ $a ] <=> $order[ $b ] } 0..$#order; } __DATA__ EVENT JIM BOB SAM JACK PTRED~09/29/10 03:23:05 PM ~09/28/10 02:21:09 PM ~09/26/10 11:00:03 AM + ~09/27/10 09:33:41 PM RED~08/29/10 01:55:00 AM ~08/30/09 12:10:10 PM ~08/27/10 08:16:21 PM ~ +09/01/10 12:12:12 AM INT~07/04/10 03:21:15 AM ~07/08/10 04:17:33 PM ~06/30/10 04:22:11 AM ~ +06/28/10 10:11:01 PM PTRED~06/19/10 09:19:55 PM ~04/25/10 07:39:22 PM ~09/16/10 10:34:24 AM + ~07/22/10 06:19:38 PM RED~04/29/10 12:10:59 AM ~04/20/10 02:13:33 AM ~07/17/10 01:00:05 PM ~ +09/01/10 11:10:15 PM INT~05/23/10 11:11:11 PM ~01/08/10 10:45:12 PM ~05/15/09 03:29:37 AM ~ +05/18/09 12:59:59 PM

        Produces:

        c:\test>junk51 EVENT JIM BOB SAM JACK PTRED 4 3 1 2 RED 3 1 2 4 INT 3 4 2 1 PTRED 2 1 4 3 RED 2 1 3 4 INT 4 3 1 2

        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (9)
As of 2014-04-24 10:24 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (565 votes), past polls