views:

424

answers:

2

I'm looking for an algorithm that will help calculate a workday working time length. It would have an input date range and then allow subtracting partially or completely intersecting time range slices from that date range and the result would be the number of minutes (or the fraction/multiple of a day) left in the original date range, after subtracting out the various non-working time slices.

For Example:

Input date range: 1/4/2010 11:21 am - 1/5/2010 3:00 pm
Subtract out any partially or completely intersecting slices like this:
Remove all day Sunday
Non-Sundays remove 11:00 - 12:00
Non-Sundays remove time after 5:00 pm
Non-Sundays remove time before 8:00 am
Non-Sundays remove time 9:15 - 9:30 am
Output: # of minutes left in the input date range

I don't need anything overly-general. I could hardcode the rules to simplify the code. If anyone knows of sample code or a library/function somewhere, or has some pseudo-code ideas, I'd love something to start with. I didn't see anything in DateUtils, for example. Even a basic function that calculates the number of minutes of overlap in two date ranges to subtract out would be a good start.

+1  A: 

Simply use the routines in DateUtils and elsewhere to implement the rules you yourself describe.

If you want an idea to get you started, off the cuff I'd suggest calculating the minutes in your input range (remembering that a TDateTime value is a floating point where the integral value is the number of days and the fractional part is a portion of a day) then incrementing thru the range and for each integer step (day) subtract the appropriate number of minutes for that day from the total based on your rules and the start/end-time of the first/last days in the range when those days are encountered in the range (intervening days being of course complete 24 hour periods).

Really to provide any more detailed "outline" one might as well implement the complete routine for you, which I might do if I had more time, which sadly I don't right now.

Deltics
I didn't see any particular routines in DateUtils that looked useful to intersect/slice date ranges, or similar things. If anyone knows of any particular RTL or third party routines, I'm all ears. I'm not a Delphi expert, so I'm very likely missing something. I think I can assume the slices to subtract out don't intersect each other (if I define the rules correctly), which seems to simplify the implementation some.
Anagoge
The routines to use in DateUtils are the ones that will identify the day of the week of a given date. That's really the only "hard part" of your problem. The rest is simple arithmetic and "greater than" / "less than" comparisons.
Deltics
+4  A: 

Interesting requirements... But not so hard to achieve in a "hardcoded" way.

Enjoy

uses
  Math, DateUtils;

function TimeRangeOverlap(Range1Start, Range1Finish, Range2Start, Range2Finish : TDateTime) : TDateTime;
begin
  Result := Max(Min(Range1Finish, Range2Finish) - Max(Range1Start, Range2Start), 0);
end;

function TotalTime(Start, Finish : TDateTime) : TDateTime;
var DayStart, DayFinish : TDateTime;
    I : Integer;
begin
  Result := 0;
  for I := Floor(Start) to Floor(Finish) do  //For each day in range;
  begin
    if DayOfWeek(I) = 1 then CONTINUE; //Remove all sundays.

    DayStart := Max(Start, I);     //Midnight on the start of the day, except on the first day;
    DayFinish   := Min(Finish, I + 1 - OneMillisecond); //Midnight minus 1 msec of the following day.

    Result := Result + DayFinish - DayStart;

    //Adjustment part
    Result := Result - TimeRangeOverlap(DayStart, DayFinish, I + EncodeTime(11,00,00,00), I + EncodeTime(12,00,00,00)); //Remove time between 11:00 and 12:00
    Result := Result - TimeRangeOverlap(DayStart, DayFinish, I + EncodeTime(17,00,00,00), I + 1); //Remove time after 5:00 PM
    Result := Result - TimeRangeOverlap(DayStart, DayFinish, I                          , I + EncodeTime(8,00,00,00)); //Remove time after 8:00 AM
    Result := Result - TimeRangeOverlap(DayStart, DayFinish, I + EncodeTime(9,15,00,00), I + EncodeTime(9,30,00,00)); //Remove time between 9:15 and 9:30
  end;
end;
Ken Bourassa
I like your approach to the problem. The two critical parts to me were how the dates can be operated on arithmetically to intersect them and how you separated the date range into individual days to process them. That and assuming no overlap in the rules made things easier, since you didn't have to have a data structure to keep track of the remaining slices of the date range. I ran a few tests on it, and after studying the code for a bit, the approach works well. I did add code for the real-world complexities (multiple rule sets, handling holidays, a few exceptions to the rules, etc.).
Anagoge