7

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!

6

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();
    Decimal DayMSInADay = DayStart.Adddays(1).getTime()-DayEnd.GetTime();
    //figure out number of days between times
    integer NumOfDays = InputStart.Date().daysBetween(InputEnd.Date()); 
    Decimal NumMSBetweenDays = NumOfDays * DayMSInADay;
    Decimal NumMSBetweenNights = NumOfDays * NightMSInADay;

    //Add Everything Togeter
    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. – Jeremy Nottingham Aug 22 '12 at 17:34
  • Yep You're right Jeremy! Reading fail on my part. Updated with a bit more verbose. – Salesforce Wizard Aug 22 '12 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! – Kenji776 Aug 22 '12 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. – Kenji776 Aug 22 '12 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. – Salesforce Wizard Aug 22 '12 at 19:44
2

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

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;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.