Convert your dates to days since your membership started. Date::Manip is excellent at parsing dates in pretty much any known format (though the docs are little incomprehensible, its worth the effort persevering with them). Its function ParseDate() and Date_Calc() will do all the work for you.

The basic idea is to build up a string one char per day, '1' when they were a member and '0' when not. Then testing for membership on a particular day involves just calculating the offset for the day in question an using substr to test if there's a '1' there. For a range of days, calculate the offset and length, substr the string and use a simple m// to see if there is a '1' anywhere in the substring. That's is really.

If the length of the strings times the number of members, eg. 10years * 356 days * 4500 members = approx 16MB is considered too much then you could trade substr in for vec and use bits instead of bytes. A little more complex, but 2MB only.

I've heard that Date::Calc is almost as good at parsing strnge date formats as Date::Manip and runs much faster because its in C. I haven't had occasion t ry it.

A bit of sample code to demonstrate the idea. Kinda of works but has bugs. Might get you started though with the date stuff.

#! perl -slw use strict; use Data::Dumper; use Date::Manip; my $days_since_open = 365*3; # 3 years my $base_date = ParseDate( '1st January 2000' ); my %members; while(<DATA>) { my @stuff = split/\|/; my $member = shift@stuff; $members{$member}= '0' x ($days_since_open); # print $member; while (@stuff > 1) { my $date1 = ParseDate( shift@stuff ); my $start = Delta_Format( DateCalc( $base_date, $date1), 0, '% +dh' ); my $date2 = ParseDate( shift@stuff ); my $length = Delta_Format( DateCalc($date1, $date2), 0, '%dh') +; substr($members{$member}, $start, $length) = '1' x $length; } if (@stuff) { my $last = ParseDate( shift@stuff ); my $last_offset = DateCalc($base_date, $last ); my $current = Delta_Format( $last_offset, 0, '%dh'); substr($members{$member}, $current) = '1' x ($days_since_open +- $current); } } my $start = do{{ print 'Start date?: '; warn "Bad date, try again", redo unless ParseDate(scalar <STDIN>); }}; my $end = do{ print 'End date?: ';ParseDate(scalar <STDIN>); }; #print "$start, $end"; my $start_days = Delta_Format( DateCalc( $base_date, $start ), 0, '%dh +' ); my $end_days = Delta_Format( DateCalc( $base_date, $end ), 0, '%dh' ) +if $end; #print "$start_days, $end_days"; for my $member (keys %members) { print $member, ' was a member ', $end ? 'between ' : 'on ', $start, $end ? ' and '.$end : '', if substr($members{$member}, $start_days, $end_days||1) =~ m[1 +]; } =pod Output C:\test>232212 Start date?: 1st April 2000 End date?: ^Z Use of uninitialized value in string eq at e:/Perl/site/lib/Date/Manip +.pm line 4155, <STDIN> line 1. Fred was a member on 2000040100:00:00 Barney was a member on 2000040100:00:00 Wilma was a member on 2000040100:00:00 C:\test>232212 Start date?: 1st may 2000 End date?: 1st december 2002 Fred was a member between 2000050100:00:00 and 2002120100:00:00 Barney was a member between 2000050100:00:00 and 2002120100:00:00 Betty was a member between 2000050100:00:00 and 2002120100:00:00 Wilma was a member between 2000050100:00:00 and 2002120100:00:00 C:\test>232212 Start date?: 1st may 2000 End date?: 1st december 2001 Fred was a member between 2000050100:00:00 and 2001120100:00:00 Barney was a member between 2000050100:00:00 and 2001120100:00:00 Wilma was a member between 2000050100:00:00 and 2001120100:00:00 C:\test> =cut __DATA__ Fred| 2000-march 31 | 2000/april/7 | 19/jan/2002 | 24th february 2002 +| 16sep2002 Barney| 15 february 2000 | Feb 15th 2002 | 1 dec 2002 Wilma| 01apr2000| 2002/19sep Betty| 01DEC2002

Examine what is said, not who speaks.

The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.


In reply to Re: Multiple date ranges by BrowserUk
in thread Multiple date ranges by AndyH

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.