Reputation: 472
I have an interface written in PHP that documents employees time worked. I'd like to add a new algorithm that measures the amount of time worked between certain hours for the purpose of calculating shift differentials. I'm using MySQL's unix_time()
for the timestamps.
Example: Employee works from 6PM until 2AM. There is a paid shift differential between 10PM and 6AM. Employee receives regular pay for 4 hours and their pay + differential for 4 hours.
$start_time = 1459015200; // 18:00 03/26/2016
$end_time = 1459044000; // 02:00 03/27/2016
$diff_start = mktime('22','00','00','3','26','2016');
$diff_end = mktime('06','00','00','3','27','2016');
I'd like to write a script that calculates the amount of time worked between 10PM and 6PM, given a large set of possibilities of times worked. This answer accomplishes appears to do what I'm looking for, except in MySQL: Calculating time difference before 6am and after 10pm in MySQL
This answer is similar to my problem: Calculate the number of hours in a given timeframe between two datetimes but relies on a loop. Given that times are entered in seconds based on unix_time
, I would presume to use a loop would require a one second increment (which means a lot of passes through the loop for thousands of records per week).
Is there an efficient way (such as the MySQL example) to do this in PHP that doesn't require looping every incremental second/minute/hour as suggested in the second answer?
Upvotes: 0
Views: 184
Reputation: 1606
Loved this question. Looked like an interview question. I spent some time to implement this efficiently with no loops. First of all I implemented the following:
function date_intersection($a_from, $a_to, $b_from, $b_to);
returning the seconds in between two time ranges.
Then, with the constraint that a shift can't be longer than 24 hours, I needed to build two range blocks:
After it, solution is straightforward:
$total = $end_time - $start_time;
$differ = date_intersection($b1s, $b1e, $start_time, $end_time) + date_intersection($b2s, $b2e, $start_time, $end_time);
$normal = $total-$differ;
Efficiently solve this question involve not using mktime() or date() to extract part of your date, as you really can solve the problem with module operator over unix-timestamp. But taking care of DST really made it complex. If you only had not asked this question tonight I would have answer before (incorrectly) :)
Upvotes: 0
Reputation: 65264
There is: Use Arrays with specially formed keys. First of all
mktime('22','00','00','3','26','2016') => 1459026000
mktime('06','00','00','3','27','2016') => 1459051200
Now build your differential array:
$shifts=array (
0 => 0,
1459026000 => $diff,
1459051200 => 0
);
This means, that from timestamp 0..1459025999 the differential is 0, from 1459026000 to 1459051199 the differential is $diff
and from 1459051200 to infinity it is 0 again. Ofcourse your real shift table will be much larger.
You now run through this array exactly once with soemthing like foreach ($shifts as $start=>$differential)
and compare $start
to $start_time
and $end_time
, with these cases:
$start>$end_time
: ignore, this is in the future$start<$start_time
: ignore, this is in the pastUpvotes: 1