for anyone interested, this is the final solution I came up with. The hardest part really was accounting for timespans that cross multiple days, especially when the finishing time was earlier than the starting time from the previous day.
public class timeCalc
{
Public class hourCount
{
public decimal nightHours = 0;
public decimal dayHours = 0;
}
Public static hourCount TimeCalcHr(DateTime StartTime, Datetime EndTime)
{
hourCount hours = new hourCount();
//everything is in minutes
decimal morningMinutes = 0;
decimal eveningMinutes = 0;
decimal startMinutes = StartTime.Hour() * 60 + StartTime.Minute();
decimal endMinutes = EndTime.Hour() * 60 + EndTime.Minute();
integer wholeDays = startTime.date().daysBetween(EndTime.date());
//if end minutes is greater than start minutes, that means we can use regular calculations (presumable both are on the same day)
if(endMinutes > startMinutes)
{
//evening minutes take place from 0 - 360
if(startMinutes < 360)
{
eveningMinutes += math.MIN(endMinutes,360) - startMinutes;
}
//morning minutes take place from 360 - 1440
if(endMinutes > 360)
{
morningMinutes += endMinutes - Math.max(360,startMinutes);
}
if (wholeDays > 0)
{
eveningMinutes += wholeDays * 6 * 60;
morningMinutes += wholeDays * 18 * 60;
}
}
//otherwise we need to calculate them a bit differently
else
{
eveningMinutes += math.MIN(endMinutes,360);
morningMinutes += 1440 - startMinutes;
if (wholeDays > 0)
{
wholeDays--;
eveningMinutes += wholeDays * 6 * 60;
morningMinutes += wholeDays * 18 * 60;
}
}
system.debug('Evening Minutes: ' + eveningMinutes);
system.debug('Morning Minutes: ' + morningMinutes);
hours.nightHours = math.ceil(eveningMinutes / 60);
hours.dayHours = math.ceil(morningMinutes / 60);
return hours;
}
@isTest
public static void testGetHours()
{
//test a timespan that goes across both day and night hours
Datetime startDate = datetime.newInstance(2008, 12, 1, 0, 0, 1);
Datetime endTime = datetime.newInstance(2008, 12, 1, 18, 0, 1);
hourCount hours = TimeCalcHr(startDate, endTime);
system.assertEquals(6, hours.nightHours);
system.assertEquals(12,hours.dayHours);
//add a day to the end time. This should grow the hours by 6 night hours, and 18 day hours in addition to the original 6 night and 12 day from the previous setup
endTime = endTime.addDays(1);
hours = TimeCalcHr(startDate, endTime);
system.assertEquals(12, hours.nightHours);
system.assertEquals(30,hours.dayHours);
//add an hour to the end time. This should grow the hours by 0 night hours, and 1 day hour
endTime = endTime.addHOurs(1);
hours = TimeCalcHr(startDate, endTime);
system.assertEquals(12, hours.nightHours);
system.assertEquals(31,hours.dayHours);
//now lets test for a small amount of night hours.
startDate = datetime.newInstance(2008, 12, 1, 0, 0, 1);
endTime = datetime.newInstance(2008, 12, 1, 5, 0, 1);
hours = TimeCalcHr(startDate, endTime);
system.assertEquals(5, hours.nightHours);
system.assertEquals(0, hours.dayHours);
//now lets test both times starting in the day
startDate = datetime.newInstance(2008, 12, 1, 8, 0, 1);
endTime = datetime.newInstance(2008, 12, 1, 12, 0, 1);
hours = TimeCalcHr(startDate, endTime);
system.assertEquals(0, hours.nightHours);
system.assertEquals(4, hours.dayHours);
//now lets test a time range that crosses midnight
startDate = datetime.newInstance(2008, 12, 1, 22, 0, 1);
endTime = datetime.newInstance(2008, 12, 2, 1, 0, 1);
hours = TimeCalcHr(startDate, endTime);
system.assertEquals(1, hours.nightHours);
system.assertEquals(2, hours.dayHours);
//k now test with several days between with the last date crossing midnight
startDate = datetime.newInstance(2008, 12, 1, 22, 0, 1);
endTime = datetime.newInstance(2008, 12, 3, 1, 0, 1);
hours = TimeCalcHr(startDate, endTime);
system.assertEquals(7, hours.nightHours);
system.assertEquals(20, hours.dayHours);
}
}