PHP: 3 time periods with different pricing - php

It's all about booking...
I've got 3 time periods:
Period 3 from 01.01.2014 to 29.04.2014 - Price 3 (per day)
Period 2 from 30.04.2014 to 14.06.2014 - Price 2 (per day)
Period 1 from 15.06.2014 to 21.09.2014 - Price 1 (per day)
Period 4 from 22.09.2014 to 29.04.2015 - Price 3 (per day)
I've already made a php calculation that calculates the booked days and pricing for each period. But I can't figure it out how to calculate between two periods.
For example:
Somebody books from 26.01.2014 to 25.04.2014 = 89 days * Price 1
But it gets really hard when somebody books from period 3 to period 1 for... I tried to separate the calculations:
if ($check_in >= '2013-04-30' && $check_out <= '2014-09-21')
{
//Days and price calcs here
// contains Period 2 and Period 1
}
But it doesn't work well...
Do you have any ideas how to make the whole calculation to work perfectly?
I missed something really important.
Here is the structure:
Period 1
if($numberDays == 1)
{
$price = $price1_period1
}
if($numberDays >= 2 && $numberDays <= 3)
{
$price = $price2_period1 * $numberDays;
}
if($numberDays >= 4 && $numberDays <= 6)
{
$price = $price3_period1 * $numberDays;
}
if($numberDays >= 7 && $numberDays <= 14)
{
$price = $price4_period1 * $numberDays;
}
if($numberDays >= 15 && $numberDays <= 29)
{
$price = $price5_period1 * $numberDays;
}
if($numberDays >= 30)
{
$price = $price6_period1 * $numberDays;
}
It's the same for the other periods. Ex.: for period 2 the price for 6 days is $price3_period2.

You could generate a price for each day. Then loop from start date to end date and sum the dayprice to get the total price:
<?php
$oneDay = 24*3600;
$configs = array(
array(
'startTime' => strtotime('2014-01-01'),
'endTime' => strtotime('2014-04-29'),
'price' => 10
),
array(
'startTime' => strtotime('2014-04-30'),
'endTime' => strtotime('2014-06-14'),
'price' => 20
),
array(
'startTime' => strtotime('2014-06-15'),
'endTime' => strtotime('2014-09-21'),
'price' => 30
),
array(
'startTime' => strtotime('2014-09-22'),
'endTime' => strtotime('2015-04-29'),
'price' => 40
),
);
$prices = array();
foreach ($configs as $config)
{
$time1 = $config['startTime'];
$time2 = $config['endTime'];
$price = $config['price'];
while ($time1 <= $time2)
{
$prices[date('Y-m-d', $time1)] = $price;
$time1 += $oneDay;
}
}
/**
* #param $checkIn in format YYYY-mm-dd
* #param $checkOut in format YYYY-mm-dd
*/
function getTotalPrice($checkIn, $checkOut, $prices)
{
$time1 = strtotime($checkIn);
$time2 = strtotime($checkOut);
$price = 0;
while ($time1 <= $time2)
{
$time1 += 24 * 3600;
$price += $prices[date('Y-m-d', $time1)];
}
return $price;
}
echo getTotalPrice('2014-01-04', '2014-01-09', $prices);

First things first, I assume $check_in and $check_out are strings that you get from some form, then you are comparing them with another string, any of them are dates.
What you can do is convert both $check_in and $check_out to Datetime and then do the comparison, example:
// Check In Date
$check_in_date = new Datetime($check_in);
$date_compare_in = new Datetime('2013-04-30');
$diff_in = $check_in_date->diff($date_compare_in);
// Check Out Date
$check_out_date = new Datetime($check_out);
$date_compare_out = new Datetime('2014-09-21');
$diff_out = $check_out_date->diff($date_compare_out);
Now $diff_in is a DateInterval object that you can check for the quantity of days, example, if the hours are greater than 0, the $check_in was later than the compare date, if is less than 0 the $check_in was before.
if($diff_in->h >= 0 and $diff_out->h <= 0){
// We are within this date range.
}
a DateInterval Object has the following structure:
DateInterval Object
(
[y] => 0
[m] => 0
[d] => 0
[h] => 0
[i] => 0
[s] => 0
[invert] => 0
[days] => 0
)

Related

How to identify dates that intersect x times within ranges in php?

We are looking to create a list of comma separated dates that tell us when a reservation is full. There are 7 units to rent so we want to know which dates are present >= 7
This Stackoverflow thread is close as it identifies intersections but I am looking for the specific dates where they intersect x amount of times.
<?php
// 2019-2-21 is present 8 times in the following array
$ranges = array(
array('id' =>'59','start' => new DateTime('2019-02-19'), 'end' => new DateTime('2019-02-21')),
array('id' =>'58','start' => new DateTime('2019-02-19'), 'end' => new DateTime('2019-02-21')),
array('id' =>'55','start' => new DateTime('2019-02-19'), 'end' => new DateTime('2019-02-21')),
array('id' =>'57','start' => new DateTime('2019-02-19'), 'end' => new DateTime('2019-02-21')),
array('id' =>'108','start' => new DateTime('2019-02-21'), 'end' => new DateTime('2019-02-28')),
array('id' =>'109','start' => new DateTime('2019-02-19'), 'end' => new DateTime('2019-02-24')),
array('id' =>'110','start' => new DateTime('2019-02-21'), 'end' => new DateTime('2019-02-23')),
array('id' =>'111','start' => new DateTime('2019-02-21'), 'end' => new DateTime('2019-02-25')),
);
function intersects($lhs, $rhs) {
return !($lhs['start'] > $rhs['end'] || $lhs['end'] < $rhs['start']);
}
function checkDates($ranges) {
// Comparison loop
for($i = 0; $i < sizeof($ranges); $i++) {
for($j = $i+1; $j < sizeof($ranges); $j++) {
if(intersects($ranges[$i], $ranges[$j])) {
echo "Date {$i} intersects with date {$j}<br>";
}
}
}
}
checkDates($ranges);
?>
I'm able to identify on a known specific date when we are over the limit
SELECT COUNT(*) FROM reservations
WHERE reservations.`date` <= '$date' AND reservations.`dateLast` >= '$date'
This gives us a count that we can compare to our qty of units but I'm not sure how to create a list of dates that intersect >= x so we can know in advance if we are sold out.
UPDATE to confirm solution:
foreach ($ranges as $range) {
while ($range['start'] <= $range['end']) {
$date = $range['start']->format('Y-m-d');
$dates[$date] = (isset($dates[$date]) ? $dates[$date] : 0) + 1; 1;//define new $dates array
$range['start']->modify('+1 day');
}
}
echo $sold_out = array_filter($dates, function($n) { return $n >= 7; });
echo '<pre>';
print_r($range);
echo '</pre>';
I think you don't need to intersect the ranges. You just need to know how many times each date appears in your list of ranges, so you can just iterate each range in ranges and count the dates.
foreach ($ranges as $range) {
while ($range['start'] <= $range['end']) {
$date = $range['start']->format('Y-m-d');
$dates[$date] = ($dates[$date] ?? 0) + 1;
// or $dates[$date] = (isset($dates[$date]) ? $dates[$date] : 0) + 1;
$range['start']->modify('+1 day');
}
}
/* Result:
array (size=10)
'2019-02-19' => int 5
'2019-02-20' => int 5
'2019-02-21' => int 8
'2019-02-22' => int 4 ...
*/
Then you can filter that to find any sold out dates.
$sold_out = array_filter($dates, function($n) { return $n >= 7; });
I think you can probably also do this in SQL by creating a temporary table with all dates in the date range you're interested in and joining it to your count query.

PHP- Carbon Datetime DiffInSeconds and How to Group By Hour

I'm calculating difference between 2 datetimes in seconds but I want to group them by hours. It should be like that.
This is how I'm getting difference between 2 dates.
$date_diff = $first->created_at->diffInSeconds($second->created_at);
And For example first time is 09:47:20 and next one is 11:47:50 I need the group them by hour like this.
In 09:00 = 12.40 Minutes
In 10:00 = 60.00 Minutes
In 11:00 = 47.50 Minutes
How can I do that ? And what you'll suggest for this ?
Thanks
You can check out the code below.
Keep in mind that it works as expected if the start time <= the end time.
Also, there's room for improvement, like extracting logic to separate functions or adding trailing/leading zeros where you'd need them, but it should get you started.
$start = '09:47:20';
$end = '11:47:50';
$startTime = Carbon::createFromFormat('H:i:s', $start);
$endTime = Carbon::createFromFormat('H:i:s', $end);
$startHour = $startTime->hour; // 9
$endHour = $endTime->hour; // 11
$hourRange = range($startHour, $endHour); // array(9, 10, 11)
if (count($hourRange) === 1) {
// start and end time share the same hour
$hourRange = array($startHour, $endHour);
}
$lastIndex = count($hourRange) - 1;
$diffGrouped = array();
foreach ($hourRange as $index => $hour) {
if ($index === 0) {
// start hour has the next full hour - calculate the difference
$nextFullHour = $startHour + 1;
$nextFullHourTime = Carbon::createFromFormat('H', $nextFullHour);
$minutes = $startTime->diff($nextFullHourTime)->format('%i.%s');
} else if ($index === $lastIndex) {
// end hour is its own "previous full hour"
$previousFullHourTime = Carbon::createFromFormat('H', $endHour);
$minutes = $endTime->diff($previousFullHourTime)->format('%i.%s');
} else {
// everything between the start and the end hour
$minutes = '60.00';
}
$diffGrouped[] = array(
'hour' => $hour,
'minutes' => $minutes
);
}
dd($diffGrouped);
/*
array:3 [
0 => array:2 [
"hour" => 9
"minutes" => "12.40"
]
1 => array:2 [
"hour" => 10
"minutes" => "60.00"
]
2 => array:2 [
"hour" => 11
"minutes" => "47.50"
]
]
*/

DatePeriod returning empty object

Using code from this post https://stackoverflow.com/a/4312630/257629
I am getting an empty object when attempting to use DatePeriod(). My PHP is version 5.4.3 and I can't see any errors. The DateTime and DateInterval appear to return the correct objects, but when passing it to the DatePeriod, I am left with an empty object. (debug is from CakePHP and outputs the contents of the variable.)
// values passed from form, to a function
// $arrival = 2013-09-05
// $departure = 2013-08-16
$start = new DateTime($arrival);
/*
object(DateTime) {
date => '2013-09-05 00:00:00'
timezone_type => (int) 3
timezone => 'UTC'
}
*/
$interval = new DateInterval('P1D');
/*
object(DateInterval) {
y => (int) 0
m => (int) 0
d => (int) 1
h => (int) 0
i => (int) 0
s => (int) 0
invert => (int) 0
days => false
}
*/
$end = new DateTime($departure);
/*
object(DateTime) {
date => '2013-08-16 00:00:00'
timezone_type => (int) 3
timezone => 'UTC'
}
*/
$period = new DatePeriod($start, $interval, $end);
debug($period);
/*
object(DatePeriod) {
}
*/
foreach ($period as $date) {
echo $date->format('Y-m-d')."\n";
}
$arrival = 2013-09-05
$departure = 2013-08-16
Arrival is not greater than Departure. If you set $arrival = 2013-08-05. Then output will be
2013-08-05
2013-08-06
2013-08-07
2013-08-08
2013-08-09
2013-08-10
2013-08-11
2013-08-12
2013-08-13
2013-08-14
2013-08-15
$end date is before $start date because you mixed up $arrival and $departure vars

How to differentiate between hours?

I'm developing a simple sistem so our employees could save their overtime so, at the end of the month, they're payed for those extra hours they made.
Normal hours are counted as 1 but nightly ones (through 23:00 to 07:00) should be 1,25 hours each.
That said, we're requesting to introduce the day, and the start and end hours. So, I thought I could have an array like this:
$hours= array(
'0' => true, '1' => true, '2' => true, '3' => true, '4' => true, '5' => true, '6' => true,
'7' => false, '8' => false, '9' => false, '10' => false, '11' => false, '12' => false, '13' => false,
'14' => false, '15' => false, '16' => false, '17' => false, '18' => false, '19' => false, '20' => false,
'21' => false, '22' => false, '23' => true
);
So, basically I test if an hour is special or it isn't with a loop like this:
$normals = 0;
$specials = 0;
$hour_since = '23:00:00';
$hour_since_expl = explode(':',$hour_since);
$hour_to = '23:15:00';
$hour_to_expl= explode(':',$hour_to );
$date = $fecha = '2012-03-14';
$datetime_since= strtotime($date ." ".$hour_since );
$datetime_to= ((int) $hour_to_expl[0] > (int) $hour_to_expl[0]) ?
strtotime(date("Y-m-d h:i:s", strtotime($date ." ".$hour_to)) . " +1 day") :
strtotime($date." ".$hour_to);
$difference = $datetime_to - $datetime_since;
$hours_difference = $difference / SECONDS_PER_HOUR; //60*60
for ($i = 0; $i <= $difference; $i++){
$hour = $i + (int) $hour_since_expl [0];
if ($hours[$hour]) //Special hour here... Pay more!
$specials++;
else
$normals++;
}
But the problem is when hours are not exact and you have started somewhere like 22:30 and ended 00:30 where you have 0,5 hours being not special. I've been struggling my mind but I can't find any solution.
Do someone have any ideas?
Edit: More code given.
Let's assume that start time is 15:15 and endtime is 22:45. First, convert start time to nearest HOUR on or after it and endtime to nearest HOUR on or before it. So we get 15:00 and 22:00. Use your loop for these hours and use the differences as follows: if the difference time (15:15-15:00 or 22:45-22:00) is in overtime, then do overtime*numMins/60 else do standard*numMins/60. I'm not familiar with PHP, so had to do this in pseudo code
edit: took a bit of time, but i love intresting chalanges ;D i hope it helps!
$specialStart = 23;
$specialEnd = 7;
$normals = 0;
$specials = 0;
$hour_since = '22:00:00';
$hour_since_expl = explode(':',$hour_since);
$start_time = mktime($hour_since_expl[0], $hour_since_expl[1], $hour_since_expl[2]);
$hour_to = '07:30:00';
$hour_to_expl= explode(':',$hour_to );
$end_time = mktime($hour_to_expl[0], $hour_to_expl[1], $hour_to_expl[2]);
if($end_time < $start_time){
//worked passed minight, add a day
$end_time = $end_time + 86400;
}
$work_time = ( $end_time - $start_time ) / 60;
for($i = 0; $i < $work_time; $i++){
$time = $start_time + ( $i * 60 );
$hour_of_day = date("H", $time);
if($hour_of_day >= $specialStart || $hour_of_day < $specialEnd){
$specials += 60;
}else{
$normals += 60;
}
}
$specials = $specials / 3600;
$normals = $normals / 3600;
echo "specials: ".$specials;
echo "<br/>normals: ".$normals;
I would suggest you to calculate total time in two separate fields.Firstly get normal hours i.e. before 23:00 and then special hours i.e. after 23:00 and before 07:00.
Then you can calculate pay easily.

Get value of previous array key in PHP

I am trying to do a Drug Half life calculator with PHP. I want to pass in the amount of the drug taken per day in MG's and pass in the Half-life hours, then it will calculate how much of the drug is left after X amount of time and how much is still left from previous doses.
So far this is what I have...
function calcHalfLife( $mgTaken , $drugHalfLifeHours , $day = 1 ) {
//total number of half-lifes elapsed
$total_half_lifes = ($day * 24) / $drugHalfLifeHours;
//total reduction in dosage
$reductionFactor = pow( 0.5 , $total_half_lifes );
//return the current dosage in the person's system
return round( $mgTaken * $reductionFactor , 8 );
}
Then I am working on this function below which will let me pass in an Array of Days and the MG taken for each day, the function should then iterate the array and run the function above on each day's value.
function HalfLifeChart(array $days, $drugHalfLifeHours ) {
$out = array();
foreach ($days as $day => $dosage) {
$out[$day] = calcHalfLife( $dosage , $drugHalfLifeHours , 1 );
}
return $out;
}
Example usage...
$day = array(1 => 30,
2 => 0,
3 => 0,
4 => 40,
5 => 30,
6 => 10,
7 => 60);
echo '<br><pre>';
print_r(HalfLifeChart( $day, 4.5));
echo '</pre><br><br>';
Now I have a pretty good start but the HalfLifeChart function is where I need to do more work, right now it will run the Half-life calculations on the number passed for each day which is good, but I need to get the result from the previous day and add that to the MG taken on the current day and then run the Calculations on that number.
So for example, if I have 0.8043mg left from the previous day and I took 30mg today, then the calculation should be ran on 0.8043 + 30 and then pass that result through my Half life calculator function.
I am not sure how to grab the result from the previous day though, any help please?
Why don't you store the result of the previous day on another variable?
Something like:
function HalfLifeChart(array $days, $drugHalfLifeHours ) {
$out = array();
$prevDay = 0;
foreach ($days as $k => $v) {
$out[$k] = calcHalfLife( $v , $drugHalfLifeHours , 1 ); //change this
$prevDay = $out[$k];
}
return $out;
}
function HalfLifeChart(array $days, $drugHalfLifeHours ) {
$out=array();
$remains=0;
foreach ($days as $day => $dosage) {
$total=$remains+$dosage;
$out[$day]=$total;
$remains=calcHalfLife( $total , $drugHalfLifeHours , 1 );
}
return $out;
}
gives you
print_r(HalfLifeChart( $day, 4.5));
Array
(
[1] => 30
[2] => 0.74409424
[3] => 0.01845587
[4] => 40.00045776
[5] => 30.99213701
[6] => 10.76870236
[7] => 60.26709765
)
Just store it.
function HalfLifeChart(array $days, $drugHalfLifeHours ) {
$out = array();
$yesterday = 0;
foreach ($days as $k => $v) {
$out[$k] = calcHalfLife($v + $yesterday, $drugHalfLifeHours, 1);
$yesterday = $out[$k];
}
return $out;
}

Categories