# Help - Calculate the time span in time range between checkin & checkout

Published 1 month ago by GTHell

I have a time range of 20:00-8:00 and I want to calculate the time span between checkout and checkin but only count the time that is inside the time range. For example, if I checkin in `2019-09-15 23:00:00` and checkout at `2019-09-16 10:00:00` then the time span is only 9 hours.

Has anyone done something like this before? Seem easy to solve from reading but inevitable without using formula or face a if-else hell.

##### lostdreamer_nl
4 weeks ago

Here's an easy way:

``````\$start = new DateTime('2019-09-15 23:00:00');
\$end = new DateTime('2019-09-16 10:00:00');

// create a period based on 1 hour intervals
\$interval = new DateInterval('PT1H');
\$period = new DatePeriod(\$start, \$interval, \$end, DatePeriod::EXCLUDE_START_DATE );
\$hours = [];
foreach (\$period as \$date) {
// foreach hour, check if it's between 8 & 20
if(\$date->format('H') >= 20 || \$date->format('H')  < 8) {
\$hours[] = \$date->format('Y-m-d H:i');
}
}
print_r(\$hours);
``````
##### Tray2
1 month ago (109,870 XP)

Try.

``````\$checkin = Carbon::parse(2019-09-15 23:00:00));
\$checkout = Carbon::parse(2019-09-16 10:00:00));
\$diff = \$checkin->diffInHours(\$checkout); //Or the other way around.
``````
##### Cronix
4 weeks ago (783,370 XP)

As Tray2 said (except \$checkout/\$checkin would be reversed). Here are the docs: https://carbon.nesbot.com/docs/#api-difference

##### GTHell
4 weeks ago (8,140 XP)

Nah, that’s too simple. What if I check in at 21:00 and checkout at 3:00? if-else? There’re multiple cases. If-else is inefficient.

##### Snapey
4 weeks ago (1,036,605 XP)

its not a problem if your checkin/checkout includes the date

##### GTHell
4 weeks ago (8,140 XP)

I think this problem I'm proposing can only be done by formula. I thought someone has done it before without using too many if-else. I've done it but it's flaw.

@Snapey If the the checkout is smaller than checkin then it's already overnight. Not hard to assume.

Here's the visual:

``````0      chin         0             0                0         cout   0
||------*--  --|==||==|------||-------|==||==|-----*-----||
23         10                 23        10
``````

It's sound simple, I know because I thought the same when first started out but then not realize it's not. Anyone I know, implement with a lot, if not hundred, if else. lol

The above answer is 11 * 2 = 22h

##### lostdreamer_nl
4 weeks ago (301,300 XP)

Here's an easy way:

``````\$start = new DateTime('2019-09-15 23:00:00');
\$end = new DateTime('2019-09-16 10:00:00');

// create a period based on 1 hour intervals
\$interval = new DateInterval('PT1H');
\$period = new DatePeriod(\$start, \$interval, \$end, DatePeriod::EXCLUDE_START_DATE );
\$hours = [];
foreach (\$period as \$date) {
// foreach hour, check if it's between 8 & 20
if(\$date->format('H') >= 20 || \$date->format('H')  < 8) {
\$hours[] = \$date->format('Y-m-d H:i');
}
}
print_r(\$hours);
``````
##### GTHell
4 weeks ago (8,140 XP)

@lostdreamer_nl This is gold. I'm interesting in knowing how did you come up with this solution? Also If I were to google, what keywords are essential? I googled this problem but seem to find no answer that related to what I wanted to do even though it probably has done by many. One more question if what if I want to do in minute instead of hours?

##### lostdreamer_nl
4 weeks ago (301,300 XP)

A few years ago I had to do a lot of checks with overlapping date period objects (was working at a parking company back) so I've used this logic quite a few times already.

This example is the simplest form of what you asked, but if you run into comparing dates with periods, or periods with other periods a lot at your job you might want to write your own classes for the logic, it makes life a lot easier.

Back then, I eventually created my own classes that held arrays of data for the interval's so I could use array functions to get unique's, merge, and subtract periods of time.

To change it into minutes, change the DatePeriod's interval ( http://php.net/manual/en/class.dateinterval.php )

``````\$interval = new DateInterval('PT1H');  // interval of 1 hour
\$interval = new DateInterval('PT1M')  // interval of 1 minute;
\$interval = new DateInterval('PT45S')  // interval of 45 seconds;
``````

By the way, I was bored a bit, so here's something to start you off:

``````
Class Roster {
private \$openPeriod = [];
private \$interval;

public function __construct(array \$openPeriods, \$interval) {
\$this->interval = new DateInterval(\$interval);
foreach(\$openPeriods as \$period) {
\$period = new DatePeriod(new DateTime(\$period[0]), \$this->interval, new DateTime(\$period[1]), DatePeriod::EXCLUDE_START_DATE );
foreach(\$period as \$interval) {
\$this->openPeriod[\$interval->format('Y-m-d H:i:s')] = 1;
}
}
}

public function getOverlap(\$start, \$end) {
\$periodToCheck = new DatePeriod(new DateTime(\$start), \$this->interval, new DateTime(\$end));
\$intervalsWithinPeriod = [];
foreach(\$periodToCheck as \$interval) {
\$moment = \$interval->format('Y-m-d H:i:s');
\$intervalsWithinPeriod[\$moment] = isset(\$this->openPeriod[\$moment]);
}
return array_filter(\$intervalsWithinPeriod);
}
}

\$opened = [
['2019-09-15 20:00:00', '2019-09-16 08:00:00'],
['2019-09-16 20:00:00', '2019-09-17 08:00:00'],
['2019-09-17 20:00:00', '2019-09-18 08:00:00'],
['2019-09-18 20:00:00', '2019-09-19 08:00:00']
];

// use PT1H  /  PT1M  /  PT1S  here for hours -> seconds
\$roster = new Roster(\$opened, 'PT1H');
\$intervals = \$roster->getOverlap('2019-09-15 23:00:00', '2019-09-18 23:00:00');
print_r(\$intervals);
``````
##### GTHell
4 weeks ago (8,140 XP)

@lostdreamer_nl Thank man, the first algorithm work great.