http://www.perlmonks.org?node_id=762975


in reply to Sorting Call durations into Timeframes

You just need Time::Local, the % operator, and the ability to write a little data driven code. Here is the idea:
use strict; use Time::Local qw(timelocal); sub get_time { my $date_time = shift; if ($date_time =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/) { my ($year, $mon, $day, $hour, $min, $sec) = ( $1, $2, $3, $4, $5, $6); # Months are 0-based in this API. $mon =~ s/^0//; $mon--; return timelocal($sec, $min, $hour, $dd, $mm, $yyyy); } else { die "Invalid time '$date_time'"; } }
Input a date, and you have a time in seconds. What do you do with it? Well you want to make it the time from a known moment in time that is at a known point versus the calendar. What moment? Well looking at my calendar, Jan 1, 2000 was a Saturday. So one time you could subtract is get_time("20000101000000"). OK, now we have seconds from the start of a Saturday, how do we find where you are in the week? Well % is the mod operator, that lets you find the remainder when one number is divided by another:
my $sec_in_week = (get_time($date_time) - $base_time)%(60*60*24*7);
This gives you what second you are in a week. But now what do you do with this?

Well you can build a couple of lookup arrays. One has seconds. The other parallel array has what bucket you're in. Like this:

my $base_time = get_time("20000101000000"); my @time_start; my @time_bucket; # We start at the beginning of the weekend. push @time_start, 0; push @time_bucket, "weekend"; # Monday starts with the 3rd. push @time_start, get_time("20000103000000") - $base_time; push @time_bucket, "19-7"; # Now fill in the week. for $day (3..7) { my $date = "2000010$day"; # 7 AM push @time_start, get_time($date . "070000") - $base_time; push @time_bucket, "7-9"; # 9 AM push @time_start, get_time($date . "090000") - $base_time; push @time_bucket, "9-18"; # 6 PM push @time_start, get_time($date . "180000") - $base_time; push @time_bucket, "18-19"; # 7 PM push @time_start, get_time($date . "190000") - $base_time; push @time_bucket, "19-7"; } push @time_start, get_time("20000108000000") - $base_time; push @time_bucket, "next week";
And now what you'll do is take your time mod 60*60*24*7 (the number of seconds in a week), and look in the @time_start array to find what is the last array position that is less than or equal to your time. What you then do is figure out how much of the duration goes to that bucket, increment time to the start of the next bucket, and decrement your remaining duration by the time tracked. Then look at the next time slot. When you manage to assign all of your remaining duration, you're done. If you ever get to "next week", then subtract off 60*60*24*7 from your time, go to index 0, and keep going.

What this strategy does is moves a lot of logic out of if conditions, into the setting up of the data structures.