# Apex: Calculate hour ranges

Given a start time (datetime) and a finish time (datetime) how can I easily calculate the number of hours that fell between 6:00am - 12:00pm (day hours) and 12:00 - 6:00am (night hours). They would be rounded up, so if there is any time at all in a given hour, that constitutes a complete hour.

So for example if your start time is 22:00 (military time here) and finish time is at 01:00 it would yield 2 day hours, 1 night hour. If instead the finish time was 1:10, that would yield 2 day hours and 2 night hours.

I'm pretty sure I could find a brute force solution to this, but I'm pretty sure there is an elegant type of solution that is escaping me.

Thanks!

So you could do something like this:

Decimal MilliSeconds = EndTime.getTime() -StartTime.getTime() ; Decimal HourConvert = Millisecds / (1000.0*60.0*60.0)

When you use .getTime() it returns the number of Milliseconds since 1970.

Subtract the two to get the total milliseconds between the two dateTimes.

Then you take the milliseconds to calculate up to get your hours. 1000 (Seconds), * 60 (Minutes) * 60 (hours)

Edit Based On actually reading the questions correctly*

So the above gives you total time.

To get the Night/Day hours you need to break it up.

1. Figure out Hours of Day and Night for the Start Time
2. Figure out Hours of Day and Night for End Time
3. Figure out Total WHOLE DAYS between Start and End time. Then you can split that number to get your Day/Night Hours.

So that's the basis of this. Below is a class (Quick and Dirty) to do just what I listed above. What's not included is the Rounding up of the values.

``````public class TimeCalc {

Public static string TimeCalcHr(DateTime InputStart, Datetime InputEnd){
Datetime DayStart = datetime.newinstance(InputStart.Year(),InputStart.Month(), Inputstart.Day(),6,0,0);
Datetime DayEnd = datetime.newinstance(InputStart.Year(),InputStart.Month(), Inputstart.Day(),12,0,0);

Decimal StartingDaysMS = InputStart.getTime() - DayStart.getTime();
Decimal StartingNightsMS = InputStart.getTime() - DayEnd.getTime();

if(StartingDaysMS <0){
StartingDaysMS =0;
}
if(StartingNightsMS <0){
StartingNIghtsMS = 0;
}
//Repeat for End Date
DayStart = datetime.newinstance(InputEnd.Year(),InputEnd.Month(), InputEnd.Day(),6,0,0);
DayEnd = datetime.newinstance(InputEnd.Year(),InputEnd.Month(), InputEnd.Day(),12,0,0);

Decimal EndingDaysMS = InputEnd.getTime() - DayStart.getTime();
Decimal EndingNightsMS = InputEnd.getTime() - DayEnd.getTime();
if(EndingDaysMS <0){
EndingDaysMS =0;
}
if(EndingNightsMS <0){
EndingNIghtsMS = 0;
}
//Figure out hours between
decimal NightMSInADay = DayEnd.GetTime() - DayStart.getTime();
//figure out number of days between times
integer NumOfDays = InputStart.Date().daysBetween(InputEnd.Date());
Decimal NumMSBetweenDays = NumOfDays * DayMSInADay;
Decimal NumMSBetweenNights = NumOfDays * NightMSInADay;

Decimal TotalNightMS = NumMSBetweenNights +  EndingNIghtsMS + StartingNightsMS;
Decimal TotalDaysMS = NumMSBetweenDays+ EndingDaysMS+ StartingDaysMS;
Decimal TotalNightHrs = TotalNightMs / (1000*60*60);
Decimal TotalDayHrs = TotalDaysMS /  (1000*60*60);

String Output = 'Night Hours: ' + string.Valueof(TotalNighthrs) + ' Day Hours: ' + string.valueof(TotalDayHrs);
return Output;
}
``````

}

• This is calculating total time interval, but it doesn't help with the Day Hours and Night Hours issue, and it also doesn't address the fact that they need a whole number of hours based on the minutes value of the end time. I.e. 01 minutes is an entire additional hour over 00 minutes in some cases. Commented Aug 22, 2012 at 17:34
• Yep You're right Jeremy! Reading fail on my part. Updated with a bit more verbose. Commented Aug 22, 2012 at 18:26
• Looks real good. Haven't tested it yet, but given the completeness of the answer I'll definitely give ya the point for it. Thanks! Commented Aug 22, 2012 at 18:45
• One question I do have is that when testing your code, using 16.8.2012 22:00 as a start time and 17.8.2012 1:00 as an end time I'm getting 32 and 16 respectively for my hour counts. Looks like perhaps some division is off? I'll look real quick, just figured I'd mention it. Otherwise I think it's just about perfect. Commented Aug 22, 2012 at 18:59
• There's probably a few wonkies in that code... it's not quite Copy and paste worthy. Off the top of my head -- When 1 day difference you're getting the time in Start and the end PLUS between.. so it's probably adding an extra 24 hours total. Commented Aug 22, 2012 at 19:44

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
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
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);
}
}
``````

I don't know how elegant this is, but it makes the calculation simpler if I shift the hours "to the left" by 6. This eliminates a whole branch of logic, although it makes it a little more abstract.

``````//shift both times to the left, so day will go from 0 to 6, and night is 6 to 24
Integer starthour = StartTime.hour() - 6;
Integer endhour = EndTime.hour() - 6;

//add an hour for any minutes in the final hour
if (EndTime.Minutes() > 0) endhour++;
//if it ends at midnight, treat as 0
if (endHour == 24) endHour = 0;

//get full number of days
Integer FullDays = (EndTime.day() - StartTime.day())
if (endhour < starthour) FullDays -= 1;

//negative times are moved to the end of the night hours
if (starthour < 0) starthour += 24;
if (endhour < 0) endhour += 24;

if (starthour < endhour)
{
dayhours = (starthour >= 6) ? 0 : ((endhour >= 6) ? (endhour - 6) : (endhour - starthour));
nighthours = (starthour >= 6) ? (endhour - starthour) : ((endhour <= 6) ? 0 : (endhour - 6);
}

if (endhour < starthour)
{
dayhours = (endhour >= 6) ? 6 : ((starthour >= 6) ? (6 - endhour) : ((6 - starthour) + endhour)));
nighthours = (endhour >= 6) ? ((endhour - 6) + (24 - starthour)) : ((starthour >= 6) ? (24 - starthour) : 18;
}

dayhours += FullDays * 6;
nighthours += FullDays * 18;
``````