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

Hi monks,

i have question regarding to calculate the time difference between two dates. here is my problem:

i have a event based dataset, each event is with start and stop time. i want to calculate the duration of each event, which between time 2007-05-01 to 2008-04-30.

  • if the event starts before 2007-05-01, then i want calculate the duration between 2007-05-01 to event stop time.
  • if the event starts & stop time within the specified period, then i want to calculate the duration by differencing the start & stop time.
  • if the event start between specified time, but stopped after 2008-04-30, then i want to calculate the duration between start time and 2008-04-30.
  • i used the Date::Calc qw module and try to calculate the duration, however if i use delta_ms, as you know, it only gives the absolute value of the two differnt dates. that doesn't really help. anyone knows how to compare the two dates, if the earlier date subtracts the later date, it will gives a negative number ???

    also attached my code here for your reference:

    #!/usr/bin/perl #declare packages used use DateTime qw( ); use DateTime::Format::Strptime qw( ); use Date::Calc qw(Delta_YMDHMS); use Date::Calc qw(Add_Delta_DHMS); my $format = DateTime::Format::Strptime->new( pattern => '%Y-%m-%d %H:%M:%S', ); while(<TEMP>) { my @line=split(";"); my @start_time1=($line[3]=~/(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+ +)/); my $start_time = $format->parse_datetime( $line[3] ); + #parsing start time my $stop_time = $format->parse_datetime( $line[4] ); + #parsing stop time $_->set_time_zone('local') for $start_time, $stop_time; #print "$start_time, $stop_time\n"; my $initial_time=$format->parse_datetime("2007-5-1 00:00:00"); my $end_time=$format->parse_datetime("2008-4-30 23:59:59"); my $min_diff_1 = $start_time- $stop_time; print "$min_diff_1"; my $min_diff_2 = $start_time->delta_ms($initial_time)->in_units +('minutes'); my $min_diff_3 = $start_time->delta_ms($end_time)->in_units('mi +nutes'); my $min_diff_4 = $stop_time->delta_ms($initial_time)->in_units( +'minutes'); my $min_diff_5 = $stop_time->delta_ms($end_time)->in_units('min +utes'); my $min_diff_6 = $initial_time->delta_ms($stop_time)->in_units( +'minutes'); if ($min_diff_2>0 && $min_diff_3<0 && $min_diff_4<0 && $min_dif +f_5>0) { print OUTPUT "$line[0],$line[1],$line[2],$line[3],$line[4],$lin +e[5],$line[6],$line[7],$line[8],$line[9],$line[10],$line[11],$line[12 +],$line[13],$min_diff_6\n"; } elsif ($min_diff_2<0 && $min_diff_3>0 && $min_diff_4<0 && $min_ +diff_5>0) { print OUTPUT "$line[0],$line[1],$line[2],$line[3],$line[4],$lin +e[5],$line[6],$line[7],$line[8],$line[9],$line[10],$line[11],$line[12 +],$line[13],$min_diff_1\n"; print "$min_diff_1,$min_diff_2,$min_diff_3,$min_diff_4,$min_dif +f_5,$min_diff_6 \n"; } elsif ($min_diff_2<0 && $min_diff_3>0 && $min_diff_4<0 && $min_ +diff_5<0) { print OUTPUT "$line[0],$line[1],$line[2],$line[3],$line[4],$lin +e[5],$line[6],$line[7],$line[8],$line[9],$line[10],$line[11],$line[12 +],$line[13],$min_diff_3\n"; } }

    Replies are listed 'Best First'.
    Re: calculate date difference
    by ikegami (Patriarch) on Jul 06, 2010 at 19:14 UTC
      Looking at your question (not the code), it seems you're complicating the issue. Try the following approach:
      1. If the start date is before 2007-05-01,
        1. Set the start date to 2007-05-01.
      2. If the end date is before 2008-04-30,
        1. Set the end date to 2008-04-30.
      3. Calculate the difference.

      I don't know why you're using both DateTime and Date::Calc. Here's a solution that uses the former.

      my $format = DateTime::Format::Strptime->new( pattern => '%Y-%m-%d %H:%M:%S', time_zone => 'local', ); my $min_start = DateTime->new( year => 2007, month => 5, day => 1, tim +e_zone => 'local' ); my $max_stop = DateTime->new( year => 2008, month => 5, day => 1, tim +e_zone => 'local' );
      my $start = $format->parse_datetime(...); my $stop = $format->parse_datetime(...); $start = $min_start if $start < $min_start; $stop = $max_stop if $stop >= $max_stop; my $dur = $start->delta_ms($stop); printf("%d minutes and %d seconds\n", $dur->in_units(qw( minutes secon +ds ));

        Thanks Ikegami, i have modified my code according to your suggestions. however now i got an error message when i run the code:"use of uninitialized value in negation <-> at c:/perl/site/lib/DateTime.pm line 1693."

        the code i modified as below:

        #declare packages used use DateTime qw( ); use DateTime::Format::Strptime qw( ); my $format = DateTime::Format::Strptime->new( pattern => '%Y-%m-%d %H:%M:%S', time_zone => 'local', ); while(<TEMP>) { my @line=split(";"); my @start_time1=($line[3]=~/(\d+)-(\d+)-(\d+)\s(\d+):(\d+):(\d+ +)/); my $start_time = $format->parse_datetime($line[3]);; + #parsing start time my $stop_time = $format->parse_datetime($line[4]); + #parsing stop time $_->set_time_zone('local') for $start_time, $stop_time; print "$start_time, $stop_time\n"; my $min_start = DateTime->new( year => 2007, month => 5, day => + 1,hour=>0, minute=>0,second=>0, time_zone => 'local' ); my $max_stop = DateTime->new( year => 2008, month => 4, day => + 30, hour=>23, minute=>59,second=>59, time_zone => 'local' ); + $start_time = $min_start if $start < $min_start; $stop_time = $max_stop if $stop >= $max_stop; my $dur = $start_time->delta_ms($stop_time); print"%d minutes and %d seconds\n, $dur->in_units(qw( minutes s +econds ))"; }

        many thanks for your help !

          hey Ikegami, no worries, i have resolved the problem. i made a mistake at define a variable. after i correct that mistake, all went well, bingo !

          thanks again and all the other monks who give the advices !

    Re: calculate date difference
    by BrowserUk (Patriarch) on Jul 06, 2010 at 19:28 UTC

      Date::Manip will almost certainly do everything you want (and more). None of the design-by-committee, O'Woe engineered alternatives come close for its functionality.

      But...If only that functionality were documented worth a damn.

      You probably want the DateCalc() function, to construct a delta thingy from your two dates.

      But then, you will need to examine (use; print; base decisions upon), the result of that function...and that's where good things go queer. Because, In all the (excessive verbose and tortuous) documentation I've read (and read, and re-read), no where is the return value of DateCalc() documented. Which makes it decidedly hard to...um...decide, what to do with it, once you have it.

      Of course, what you will need to do with it will depend largely upon what you want to do with it, but since you don't know what "it" is, it makes the decision of how to do what you want to do with it all the harder.

      If you're lucky, sbeck, the clever and dedicated author of Date::Manip will breeze by and explain exactly and succinctly (his post are, unlike his documentation), what you need. Unfortunately, he does not appear to be a regular visitor.


      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.

        I'm not sure how you can say that the return value of DateCalc is not documented. The very first paragraph in the DateCalc description ends with:

        Two deltas add together to form a third delta. A date and a delta returns a 2nd date. Two dates return a delta (the difference between the two dates).

        True, the format of a delta or a date isn't explicitly described there, but that's not a bad thing. Almost all of Date::Manip functions return either a date or a delta, so it's not appropriate to describe them here.

        I am certainly open to suggestions as to how to improve the documentation for Date::Manip. It IS large, and could probably be better organized... but I've spent a lot of time trying to make it reasonably clear. If you don't, please send me specific examples of what you don't think is clear, and I'll be happy to consider them.

        However, as you said, I don't spend a lot of time on these forums (and it's outside of the scope of this question), so that should probably be done by email.

          The very first paragraph in the DateCalc description ends with:

          I acknowledge I was wrong about that.

          However, a long-time, but infrequent user of your powerful module, I always have to look up how to do things, and always find myself scrolling up and down the page trying to find the information I need.

          I do understand how difficult it is to write good documentation, and I do not have any simple suggestions for improvement. I note also that you have made considerable changes in that regard in recent builds. I will contact you by email.


          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.
    Re: calculate date difference
    by moritz (Cardinal) on Jul 06, 2010 at 19:31 UTC
      use Data::Simple qw(date); my $start = date('1940-02-12'); my $min = date('2007-05-01'); my $end = date('2010-08-23'); my $max = date('2008-04-30'); $start = $min if $star < $min; $end = $max if $max < $end; print "Duration: ", $end - $start, " date\n";
      Perl 6 - links to (nearly) everything that is Perl 6.
        Since he's using delta_ms, it sounds to me like the OP wants the duration in minutes and/or seconds.
          I don't see much sense in calculating milliseconds when the date is being parsed with the accuracy of seconds (looking at the regex), and the upper and lower limits are dates only, no time specified.

          Maybe I misunderstood something, but I'll blame it on the unclear question :-)

          Perl 6 - links to (nearly) everything that is Perl 6.
    Re: calculate date difference
    by Tux (Canon) on Jul 08, 2010 at 06:30 UTC

      Most negative values in Date::Calc have been addressed in version 5.8 (and up) giving you the N_Delta_YMDHMS () function. All N_ prefixed functions return normalized values, which are guaranteed to be all of the same sign.

      my ($D_y, $D_m, $D_d, $Dhh, $Dmm, $Dss) = N_Delta_YMDHMS ( $year1, $month1, $day1, $hour1, $min1, $sec1, $year2, $month2, $day2, $hour2, $min2, $sec2);

      From the docs:
      ... "The return values of this function (including the time differences) are guaranteed to all have the same sign (or to be zero). This is the reason for the "N" that precedes the name of this function, which is intended to mean "normalized" (or "new"). Moreover, the results are guaranteed to be "minimal", in the sense that |$D_m| < 12, |$D_d| < 31, |$Dhh| < 24, |$Dmm| < 60 and |$Dss| < 60 (which is equivalent to $D_m lying in the range [-11..+11], $D_d lying in the range [-30..+30], $Dhh lying in the range [-23..+23], and $Dmm and $Dss both lying in the range [-59..+59])."


      Enjoy, Have FUN! H.Merijn