Calulating days for delivery

I have this block of code that was written by someone years ago :-

<?php

	class BoDelivery {
		
		public function EstimatedDays($off, $standard_days, $saturday, $delay) {
            
            $today = date("N"); // Weekday - number 1-7
            $now = strtotime("now"); // Unix
            
            $off_array = explode(":", str_replace(".", ":", $off));
            
            $off_unix = mktime($off_array[0], $off_array[1], "00", date("n"), date("j"), date("Y"));

            $sending_days_from_now = 0;
            if ($now > $off_unix) {
                $sending_days_from_now++;
            }
            $sending_day = $today + $sending_days_from_now;
            switch ($sending_day) {
                case 6:
                    $sending_days_from_now++;
                    $sending_days_from_now++;
                    break;
                case 7:
                    $sending_days_from_now++;
                    break;
            }
            $sending_day = $today + $sending_days_from_now;

            // Estimated delivery time
            $delivery_days = $standard_days;
            $over_weekends = 0;

            // Add Delay
            if ($delay) {
                $delivery_days = $delivery_days + $delay;
            }
			            
            if ($sending_day == 5 && !$saturday) {
                $delivery_days++;
                $delivery_days++;
                $over_weekends++;
            }
            
            $delivery_day = $sending_day + $delivery_days;

            switch ($delivery_day) {
                case 6:
                    if (!$saturday) {
                        $delivery_days++;
                        $delivery_days++;
                        $over_weekends++;
                    }
                    break;
                case 7:
                    $delivery_days++;
                    $over_weekends++;
                    break;
            }
            
            if ($over_weekends == 0 && $delivery_days > 5) {
                $delivery_days++;
                $delivery_days++;
            }
            
            $delivery_day = $sending_day + $delivery_days;
            
            switch ($delivery_day) {
                case 13:
                    $delivery_days++;
                    $delivery_days++;
                    $over_weekends++;
                    break;
                case 14:
                    $delivery_days++;
                    $over_weekends++;
                    break;
            }
            
            $delivery_day = $sending_day + $delivery_days;
            
            $delivery_days = $delivery_day - $today;
            			
			return $delivery_days;
			
		}
		
	}
	
?>

Which is supposed to calculate the number of days for delivery, taking into account weekends & with a delay variable that we can set.

It isn’t working as expected, may never have worked(??) or maybe down to PHP upgrades.

For example, if today (18th)I set the delay to 2 days the delivery estimate is the 21st (which is correct), if I change the delay to 3 OR 4 it becomes the 24th, 5 then becomes the 26th!

I can’t get my head around the code at all, I wonder if someone could assist in what may be going on or add comments to the code snippets so I can maybe work it out?

Thanks for any help.

Well, I just spent a bit of time reformatting it so I could read it and you edited it while I was typing… HA!

So, it appears that they did this a long time ago and did a lot of case work to select how to handle the days
off. The code uses some case-work to handle skipping days if they are weekends. But, there are no checks for holidays, either. This code is a bit outdated. There are hundreds of ways to handle this type of function. Instead of writing code from scratch, I found this one on Stack-Overflow which is easy to change to fit your needs. It also adds in a holiday list that you can alter to track your company ones.
Hope it helps…

$holidayDates = array(
    '2016-03-26',
    '2016-03-27',
    '2016-03-28',
    '2016-03-29',
    '2016-04-05',
);

$count5WD = 0;
$temp = strtotime("2016-03-25 00:00:00"); //example as today is 2016-03-25
while($count5WD<5){
    $next1WD = strtotime('+1 weekday', $temp);
    $next1WDDate = date('Y-m-d', $next1WD);
    if(!in_array($next1WDDate, $holidayDates)){
        $count5WD++;
    }
    $temp = $next1WD;
}

$next5WD = date("Y-m-d", $temp);

echo $next5WD; //if today is 2016-03-25 then it will return 2016-04-06 as many days between are holidays

It is just an example. If you have problems show us your new version and we can help you with it.
( Note that this one adds 5 days to the date. You can change that as needed… )

Thanks! I’ll have a play around with the example you found :wink:

After digging deeper I now realise the code above is used in another PHP file, the section in question looks like this :-

                $today = date("N"); // Weekday - number 1-7
                $now = strtotime("now"); // Unix

                if ($this->mCart['deliveryoption'] == '0') {
                    $standard_days = 1;
                    $saturday_del = 0;
                    $off = $this->mDoSettings->GetSetting(175);
                    $delay = $this->mDoSettings->GetSetting(47);
					$delay_upper = $this->mDoSettings->GetSetting(176);
                } else {
                    $delivery = $this->mDoCart->GetPremiumDeliveryInfo($this->mCart['deliveryoption']);
                    $standard_days = $delivery['standarddays'];
                    $saturday_del = $delivery['saturdaydel'];
                    switch ($today) {
                        case 1: $off = $delivery['monoff']; break;
                        case 2: $off = $delivery['tueoff']; break;
                        case 3: $off = $delivery['wedoff']; break;
                        case 4: $off = $delivery['thuroff']; break;
                        case 5: $off = $delivery['frioff']; break;
                        case 6: $off = $delivery['satoff']; break;
                        case 7: $off = $delivery['sunoff']; break;
                    }
                    $delay = 0;
					$delay_upper = $delivery['days_upper'];
                }
                $delivery_days = $this->mBoDelivery->EstimatedDays($off, $standard_days, $saturday_del, $delay);
				$delivery_days_upper = $this->mBoDelivery->EstimatedDays($off, $standard_days, $saturday_del, $delay_upper);
                $this->mCart['est_delivery'] = date("D jS F", strtotime("+" . $delivery_days . " days"));
				//$delivery_days_upper = $delivery_days + $delay_upper;
				$this->mCart['est_delivery_upper'] = date("D jS F", strtotime("+" . $delivery_days_upper . " days"));

So I guess my new PHP will have to be the part that generates the $delivery_days variable?
I’m no coder, I think I’m going to need one as this is a bit above my limited capabilities!

Well, this is totally different code. It shows that you have a card system with probably multiple carts running at the same time. Also, there are a lot of arrays that we would need to know more about to be able to fix anything.

You can hire a coder here, or perhaps give one access to the rest of your code to sort this out. But, just looking at that part show there is a lot more behind the scenes. Also, there is a line commented out, so not sure if it ever worked correctly in the first place. Some of this code is odd. It gets the day of the week 1-7 and then uses a switch to convert it into a text and then uses that text later on to store in an object. All that code can be done in one simple line.

Not really sure how to help you. Is this a “canned” template? Is it a live site somewhere that can be viewed?

The current object is created from mCart. Then, the delivery days object is created and put back into the mCart object. So, a wild guess is that you can replace all of delivery-days code with something like the new code I gave you adjusted to your use. In there, it gets some system settings that appear to be global values and that gives you the $off and $delay which I would assume is the days off and the delay-days in shipping. So, probably could change all that part to do the calculations…

Hmm, I think I need a coder! The line commented out was me testing something :wink:

Of what I can gather, delivery is always 1 day, $off is the cutoff time for delivery that day, eg, after 4pm that delivery will not ship that day, it will ship the next business day. $standard_days is the number of business days for delivery. $saturday is whether delivery can happen on a Saturday (so if a user orders on a Friday & selects Saturday delivery). But Saturdays should NOT count towards delivery progress for other delivery options. $delay adds day(s) as required to the delivery progress.

I think my best bet is to get a coder to modify the sample you found so it takes into account delivery cut off time and Saturday delivery flag?

Just one thing, could you expand on this comment at all?

" It gets the day of the week 1-7 and then uses a switch to convert it into a text and then uses that text later on to store in an object. All that code can be done in one simple line."

I’m glad a pro thinks the code is convoluted too!

Thanks again, you’ve been a great help.

Well, first, I did not really say it was convoluted. I find that helping people get me into deep trouble sometimes. This code might be just fine for awhile ago. And, it really depends how all of the other parts work. Such as how it gets all of the values set up in the first place. I am a bit weak in object orientated programs. Others here on the site are better for that. But, I think the code is simple enough to work out.

Give me a few minutes and I will drum up a test for you. Is this on a live site at the moment? I mean, can you run tests without it bothering live orders?

Also, were you planning on adding in your company holidays?

Hi, sorry, I didn’t mean to put words into your mouth :wink: I have a backup server I can run tests on so no issue there. It would be good if we can add holidays, we already have holidays for another part of the site. That uses two settings, one for holidays every year stored in the DB as MM/DD separated by a comma and one for one off dates that are stored in the DB as YYYY/MM/DD format, again separated by a comma.

Well, I would guess this is very easy to do then. First, I need to get the holidays you already have into an array. The date format might be easier so I don’t have to convert the MM/DD ones to the current year.
Can you figure that part out? Is it just inside one of the ->mDoSettings->GetSetting(???) table or can you run a query to pull them out? We would need a list of them in a simple array.

I can easily get them via mDoSettings, eg, they would then be listed as 2020/08/18,2020/08/19 etc. Currently there is code as follows that is using them (hope this helps!)

  **** Delivery counter code start ****
	require_once FILE_ROOT . '/data_objects/do_settings.php';
    $doSettings = new DoSettings();
	date_default_timezone_set('Europe/London');
	$currentTime = date('H:i:s');
	$currentDay = date('w');
	$delTimeStart = '00:00:00';
	$delTimeEnd = $doSettings->GetSetting(49);


    // Holidays that happen every year in MM-DD format. 
	$holidays_every_year=($doSettings->GetSetting(51));
	$date_array=  explode(',',$holidays_every_year);
	$holidays_every_year=  array();
	foreach ($date_array as $value) {
    trim($value);
    if (strtotime($value)=== false ){
       //bad date
    }else{
         $holidays_every_year[] = date('m-d',strtotime($value));
    }
};

   // Holidays that happen only ONCE in YYYY-MM-DD format.
	$holidays_by_year=($doSettings->GetSetting(50));
	$date_array=  explode(',',$holidays_by_year);
	$holidays_by_year=  array();
	foreach ($date_array as $value) {
    trim($value);
    if (strtotime($value)=== false ){
       //bad date
    }else{
         $holidays_by_year[] = date('Y-m-d',strtotime($value));
    }
};
	$MMDD=date("m-d");
	$YYYYMMDD=date("Y-m-d");
	if ($currentTime >= $delTimeStart && $currentTime < $delTimeEnd && $currentDay > 0 && $currentDay < 6 && !(in_array($MMDD, $holidays_every_year)) && !(in_array($YYYYMMDD, $holidays_by_year))){
	$countdowndisplay = 'block;';
	$timeremaining = strtotime($delTimeEnd) - strtotime($currentTime);
	} else{
	$countdowndisplay="";
} 
	
	// **** Delivery counter code end ****
	
	//Delivery Estimate-Displays on WEEKDAYS unless specified in the OFF dates
	if ($currentDay > 0 && $currentDay < 6 && !(in_array($MMDD, $holidays_every_year)) && !(in_array($YYYYMMDD, $holidays_by_year))){
	$deliveryestimate = 'block;';
	$timeremaining = strtotime($delTimeEnd) - strtotime($currentTime);
	} else{
	$deliveryestimate="";	
} 
	//End Delivery Estimate

Thanks a lot for your help, I have to go out now but will get back on this ASAP :wink: Again, many thanks.

I couldn’t find my post as it was uncategorized, hopefully you can still see it now it’s in a category?

I was extremely busy. But, I will work on it today for you.

No problem, your help is much appreciated :slight_smile:

Oh, I took a quick look at where I left off last night before I leave for awhile. Will be back in a few hours and can help you then. But, in the code you showed, I found a function that is called. Since I need to see all parts of your code to solve it, can you please look thru all the system and find this routine and list it for me?

EstimatedDays() You call it twice in the code you posted. I see that whoever wrote this did not place comments in it and some of the code is convoluted a bit. Just minor things, but, with that routine, I think I can drum up a working version for you. Post it when you can and I will review it when I get back. Thanks.

Hi, I can only see reference to it in two files, first one is called bo_delivery.php : -

<?php

	class BoDelivery {
		
		public function EstimatedDays($off, $standard_days, $saturday, $delay) {
            
            $today = date("N"); // Weekday - number 1-7
            $now = strtotime("now"); // Unix
            
            $off_array = explode(":", str_replace(".", ":", $off));
            
            $off_unix = mktime($off_array[0], $off_array[1], "00", date("n"), date("j"), date("Y"));

            $sending_days_from_now = 0;
            if ($now > $off_unix) {
                $sending_days_from_now++;
            }
            $sending_day = $today + $sending_days_from_now;
            switch ($sending_day) {
                case 6:
                    $sending_days_from_now++;
                    $sending_days_from_now++;
                    break;
                case 7:
                    $sending_days_from_now++;
                    break;
            }
            $sending_day = $today + $sending_days_from_now;

            // Estimated delivery time
            $delivery_days = $standard_days;
            $over_weekends = 0;

            // Add Delay
            if ($delay) {
                $delivery_days = $delivery_days + $delay;
            }
            
            if ($sending_day == 5 && !$saturday) {
                $delivery_days++;
                $delivery_days++;
                $over_weekends++;
            }
            
            $delivery_day = $sending_day + $delivery_days;

            switch ($delivery_day) {
                case 6:
                    if (!$saturday) {
                        $delivery_days++;
                        $delivery_days++;
                        $over_weekends++;
                    }
                    break;
                case 7:
                    $delivery_days++;
                    $over_weekends++;
                    break;
            }
            
            if ($over_weekends == 0 && $delivery_days > 5) {
                $delivery_days++;
                $delivery_days++;
            }
            
            $delivery_day = $sending_day + $delivery_days;
            
            switch ($delivery_day) {
                case 13:
                    $delivery_days++;
                    $delivery_days++;
                    $over_weekends++;
                    break;
                case 14:
                    $delivery_days++;
                    $over_weekends++;
                    break;
            }
            
            $delivery_day = $sending_day + $delivery_days;
            
            $delivery_days = $delivery_day - $today;
            			
			return $delivery_days;
			
		}
		
	}
	
?>

And then in a function file where it is used : -

// Est Delivery
$today = date(“N”); // Weekday - number 1-7
$now = strtotime(“now”); // Unix

                if ($this->mCart['deliveryoption'] == '0') {
                    $standard_days = $this->mDoSettings->GetSetting(47);
                    $saturday_del = 0;
                    $off = $this->mDoSettings->GetSetting(175);
                    $delay = $this->mDoSettings->GetSetting(176);						
                } else {
                    $delivery = $this->mDoCart->GetPremiumDeliveryInfo($this->mCart['deliveryoption']);
                    $standard_days = $delivery['standarddays'];
                    $saturday_del = $delivery['saturdaydel'];
                    switch ($today) {
                        case 1: $off = $delivery['monoff']; break;
                        case 2: $off = $delivery['tueoff']; break;
                        case 3: $off = $delivery['wedoff']; break;
                        case 4: $off = $delivery['thuroff']; break;
                        case 5: $off = $delivery['frioff']; break;
                        case 6: $off = $delivery['satoff']; break;
                        case 7: $off = $delivery['sunoff']; break;
                    }
                    $delay = $delivery['days_upper'];						
                }
                
                $delivery_days = $this->mBoDelivery->EstimatedDays($off, $standard_days, $saturday_del, "");
  			$delivery_days_upper = $this->mBoDelivery->EstimatedDays($off, $standard_days, $saturday_del, $delay);
                $this->mCart['est_delivery'] = date("D jS F", strtotime("+" . $delivery_days . " days"));
  			$this->mCart['est_delivery_upper'] = date("D jS F", strtotime("+" . $delivery_days_upper . " days"));

Thanks, the first part is what I needed. It uses some code that is out-dated now. And, it is a “class” and is called by only one routine. so, it is silly to do it that way. Just a waste of time having the server jump back and forth. I think it would be best to just combine it all into one smaller routine similar to the one I showed.

I think I can do this for you, but, short on time today. I will attempt to get to it in a few hours from now.
Thanks again for finding the part I needed… More later today…

Well, I have about 30 minutes free until tomorrow. So, I am drumming up a quick file for you to play with.
This previous code is crazy complex to accomplish this simple task. I am making a small one page PHP file you can run to play with to see how simple it should be. I do not have the time to create a routine to match your other code today. But, this one should give you a starting point. I will send it soon.

So, I have to leave for the evening. I will be gone many hours. Here is some code I created for you to test with. It is messy with a lot of comments so you can understand the logic to it all. You can copy this into a file and name it test-date.php or something. Then run the file and see the results. You can set the holidays to any you want for testing and select the starting day by changing the NOW line. I have not 100% tested it as I am out of time. But, I think it is a lot less code than the mess you started with. Let me know what you think and I will look at it about midnight EST when I get back.
<?PHP
// Adjusting the new routine posted above. Using your needs, the logic of the estimated delivery is based on these items:
// Today’s date/time ( If after 4pm, add a day )
// Normal shipping time is one day delivery
// Holidays are not counted as a shipping day
// Saturday and Sunday are not counted as a shipping day. ( Note Saturday can be a shipping day optionally )

//  Get company holidays  ( This is for testing! Use live ones later! This one has a holiday tomorrow and two others over the next week.)
$holidayDates = array(
    '2020-08-20',
    '2020-08-22',
    '2020-08-25',
);

//  Calculate today
$now = strtotime("now");    //  For testing picking a day with the upcoming three holidays and weekend, you can use this:   $now = strtotime("2020-08-24 00:00:00"); or some other date and time..

//  Set variables for testing.  Since it is all automatic, the only option is if they requested Saturday delivery or not.
$mCart = array(); //  Just to use the same mCart option layout
$mCart['deliveryoption'] = '0';     //  Set this for testing, Saturday delivery = 1 !

//  This routine calculates the estimated delivery date using the logic you needed
//  Set the default delivery time to 1 day
$days = 1;

//  If past 4PM, add one to the days
if (date('H') > 16) { $days = $days + 1; }

//  Check if any shipping days are holidays or weekend, accounting for Saturday delivery option
//  This very short routine takes the one shipping day, adds one if after 4PM, then checks for holiday/weekend and adjusts days
$count_days = $days;    //  Starting day count is 1 day or two if past 4PM
//  Loop thru the days ( only 1 or two at this point )
$temp = $now;   //  Set up temporary pointer to today
while($count_days>0){
    $next_week_day = strtotime('+1 weekday', $temp);
    $next_week_day_date = date('Y-m-d', $next_week_day);
    //  Check if this shipping day is in holiday list, on sat without the option or on sun
    if(!in_array($next_week_day_date, $holidayDates) AND ($mCart['deliveryoption']==0 AND date("N", $next_week_day)!=6) AND date("N", $next_week_day)!=7 ){
        $days++;    //  This day is a valid week day not a holiday and not on Saturday (or with Saturday option selected)
    }
    $count_days = $count_days - 1;
    $temp = $next_week_day;
}
$estimated_delivery_date = $next_week_day_date;
echo $estimated_delivery_date;
?>

Just wrote this off the top of my head with some refreshers on how date functions work. I can adjust it for you if it is not working as you wish it to. Let me know…

Sponsor our Newsletter | Privacy Policy | Terms of Service