Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

jdavidb (1361)

jdavidb
  (email not shown publicly)
http://voiceofjohn.blogspot.com/

J. David Blackstone has a Bachelor of Science in Computer Science and Engineering and nine years of experience at a wireless telecommunications company, where he learned Perl and never looked back. J. David has an advantage in that he works really hard, he has a passion for writing good software, and he knows many of the world's best Perl programmers.

Journal of jdavidb (1361)

Tuesday August 07, 2007
02:29 PM

Simple math in DateTime: partitioning an interval

[ #34032 ]

I loved Time::Piece (formerly Time::Object) with a passion. After Y2K I sketched out a plan for two Perl libraries that would perform date functions. It sat on my whiteboard for several months, and then suddenly Matt Sergeant released a module that implemented it almost exactly. It worked exactly the way I wanted to think. There was a class to represent moments in time, there was a class to represent intervals of time, and every operator possible was overloaded in meaningful ways. If you subtracted two points in time you got an interval, if you added an interval to a point in time you got a point in time, if you performed multiplication or division on an interval it just worked, and on and on. Intervals were real objects, not just integers representing some unit of time like in Oracle or other systems I've seen.

DateTime came out, and I knew it was the next best thing, and in fact Time::Object was reimplemented to use it internally. But the learning curve was steep. I slowly picked up a bit here and there, wrote myself some utility programs, and then when I got interested in other calendars and some of the more esoteric things that happened to have DateTime-related modules on CPAN I converted and started writing with it.

But DateTime's two classes did not work the way I expected. I tried to perform division on a time interval and got something that was not a time interval. DateTime refused to perform all sorts of conversions because of exception cases that meant that certain things were not always true. Every time I touched it I found myself looking things up ...

... and looking things up didn't help. Time interval objects didn't stringify. They appeared to have redundant methods. There weren't ways to say "Look, don't give me the seconds component of the interval, tell me how many seconds long the interval really is, and since I computed it from two moments in time this is not a meaningless question that can't be answered; tell me, and tell me in a way that I can perform math on." And the documentation had an oh-so-helpful section on "How Datetime Math is done" that really didn't help at all, because it dealt with all sorts of exceptions I didn't care about and didn't actually tell me how to do what I used to just do with Time::Object.

Of course, I couldn't go back for a host of reasons. So I'd puzzle something out, and then when I needed it again I'd hope I'd find it lying around in one of my programs somewhere so I could refer to it again.

So it's time to start recording some of this knowledge here. IMO, DateTime long since should've had a date math section in the documentation that actually shows how to do these things.

So, today's puzzle is: given two DateTimes, divide the interval between them into N equal parts and give me back a series of N + 1 DateTimes, from $start to $end, all evenly spaced:

sub partition_datetime
{
  my($start, $end, $n) = @_;
  my @a;
  my $int = $end - $start;
  for my $i (0 .. $n)
  {
    push @a, $start + $int->clone->multiply($i / $n);
  }
  return @a;
}

Of course, I'm scared to death this somehow doesn't really work right because DateTime is so pathological about saying, "I can't really tell if this interval divides like this or like that because sometimes days have extra hours or seconds or something." Even though I'm doing it all in UTC.

Update: No, it doesn't work. If I divide into too many partitions, DateTime can't handle adding fractional days, hours, minutes, seconds, etc. Look, the interval is three days long. Divide it into 250 equal portions and let me know where those breaks are. I could do it in a snap with Time::Object.

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
 Full
 Abbreviated
 Hidden
More | Login | Reply
Loading... please wait.
  • sub partition {
        my ($start, $end, $n) = @_;
        my $s = $start->epoch;
        my $e = $end->epoch;
        my $i = int( ($e - $s) / $n );    # interval
        return map DateTime->from_epoch(epoch => $s + $i * $_), 0 .. $n;
    }
    ?
    • What's the point of having classes to handle date arithmetic if you have to convert back into numbers in order to do the math?

      That works, but one shouldn't have to go to those lengths.

      --
      J. David works really hard, has a passion for writing good software, and knows many of the world's best Perl programmers
  • http://datetime.perl.org/index.cgi?FAQSampleCalculations [perl.org]

    # As a Perl list
    my $start_dt = DateTime->new(year => 1998, month  => 4,  day => 7);
    my $end_dt   = DateTime->new(year => 1998, month  => 7,  day => 7);

    my @list = ();
    for (my $dt = $start_dt->clone();
         $dt <= $end_dt;
         $dt->add(weeks => 1) )
    {
      push @list, $dt->clone();
    }

    # As a DateTime::Set.  We use DateTime::Event::Recurrence to easily
    # c

    • The first of those solutions only works if you know the size of the intervals you want to partition into ahead of time. I want to deal with the general case: say I have two moments in time that are exactly 29 days, 3 hours, 1 minute, and 17 seconds apart, and I want to divide it into 8 equal intervals? (My next journal entry does reveal the solution, but I'm just responding to show that I'm dealing with a harder problem than your solution addressed.)

      I think the same is true of the second solution. The

      --
      J. David works really hard, has a passion for writing good software, and knows many of the world's best Perl programmers