1

I'm trying to implement this algorithm for generating 3 possible delivery dates for an order on my store, which the user can choose during checkout: The store delivers Monday to Friday. The server must return an array with 3 possible delivery dates according to these rules: 2) the store delivers from Monday to Friday

  1. if the order is made by 10 am then this can also be delivered on the same day (if from Monday to Friday)

I have tried several paths and this is what I have come up to now. Could anyone help me complete this algorithm?

public function test(){
    $today = date();
    $todayCode = date('N'); 
    $possibleShippingDates = [];
    while(count($possibleShippingDates)<3) {
        if($todayCode <6) {
            array_push($possibleShippingDates, $today);
            //go to next day?
            // $today = today + 1 day
            // $todayCode = date('N', $today)
        }
    }
    return $possibleShippingDates;
}
  • I think you first have to decide whether you want the user to select the delivery or the shipping date. The two clearly aren't the same, yet you use them as if they are. – KIKO Software Jul 10 '20 at 07:17
  • 1
    just get the order time, if hour is greater than or equal to 10, then adjust the starting point, much easier to do with `DateTime` classes. – Kevin Jul 10 '20 at 07:17
  • Does this answer your question? [Adding one day to a date](https://stackoverflow.com/questions/1394791/adding-one-day-to-a-date) – Cid Jul 10 '20 at 07:17
  • @Kevin: At the time the user can select the delivery date, the order time might not be known. Also, time goes on while the user decides when to get the order delivered. – KIKO Software Jul 10 '20 at 07:21
  • @KIKOSoftware i stand corrected current time – Kevin Jul 10 '20 at 07:23

3 Answers3

2

If the current time while the user goes to checkout goes beyond 10 AM, then adjust the starting point basis (current time) accordingly.

Use DateTime classes for this. You can use ->add() or ->modify() to do this:

$order_date = '2020-07-09 10:23:00'; // if no argument, it will use the current time
function getPossibleShippingDates($order_date = 'now', $num_day = 3) {
    $dates = [];
    $dt = new DateTime($order_date);
    
    if ($dt->format('H') >= 10) { // if ordered on or after 10, move to next day
        $dt->modify('+1 day');
    }
    if (in_array($dt->format('N'), [6, 7])) { // if ordered weekend, adjust to start monday
        $dt->modify('next monday');
    }
    
    $i = 1;
    while ($i <= $num_day) {
        if (!in_array($dt->format('N'), [6, 7])) { // skip shipping day on weekends
            $dates[$i++] = $dt->format('Y-m-d');
        }
        $dt->modify('+1 day');
    }

    return $dates;
}

$dates = getPossibleShippingDates($order_date);
print_r($dates);

Sidenote: The sample input above is ordered the 9th but goes beyond 10 AM. So it'll move to the next to which is 10th. So it should yield the 10th, skip weekends, then 13th and the 14th.

Array
(
    [1] => 2020-07-10
    [2] => 2020-07-13
    [3] => 2020-07-14
)

Sample Fiddle

Kevin
  • 41,694
  • 12
  • 53
  • 70
0

well, probably it's easier using DateTime class, but here's a solution without it, combining date() with strtotime() functions:

/*
    store delivers from monday to friday
    if hour is within 10 in the morning, order can be shipped same day
    gives 3 possible dates
*/

public function test(){
    // order date time, to fill with the real one (this is current datetime)
    $orderDateTime = date("Y/m/d H:i");
    // flag to control the first day to check:
    // only first day has hour condition (until 10 in the morning)
    $firstDay = true;
    // setting up empty array for shipping dates
    $possibleShippingDates = [];
    // do it until it reaches 3 dates in the array
    while(count($possibleShippingDates) < 3) {
        // it gets day of week and hour from the order date time
        $todayCode = date('N', strtotime($orderDateTime)); 
        $todayHour = date("H", strtotime($orderDateTime));
        // if code is 1-5 (monday-friday) but not 6-7 (saturday-sunday)
        if($todayCode <6) {
            // if hour (24h format) is within 10 in the morning
            // or if current check day isn't the first one
            if($todayHour < 10 || !$firstDay) {
                // add the date as possible shipping date (reformatting it in d/m/Y)
                array_push($possibleShippingDates, date("d/m/Y", strtotime($orderDateTime)));
            }
        }
        // let's increment the date to the next one:
        //  - strtotime can add 1 day to order date time
        //  - save it using date still considering hour
        $orderDateTime = date("Y/m/d H:i", strtotime($orderDateTime . " + 1 day"));
        // if that was the first day,
        if ($firstDay) {
            // next ones won't be
            $firstDay = false;
        }
    }
    // at the end, return all possible shipping dates
    return $possibleShippingDates;
}

hope it helps... have a nice weekend! :)

blurstream
  • 429
  • 3
  • 13
0

Other solution that uses recursion:

//returns by default 3 possible shippingdates
function getShippingDates(DateTime $date , int $number = 3 , array $shippingDates = []): array
{
    if($number !== 0) {
        //if before 10 o'clock on working day and on weekday the supplied date is allowed as shipping date
        if($date->format('H') < 10 && in_array($date->format('N'), [1,2,3,4,5])){
            $shippingDates[] = $date->format('Y-m-d');
            $number--;
        }
        //create new DateTime object from original DateTime object without time
        $newDate = $date->modify('+1 day');
        return getShippingDates(new DateTime($newDate->format('Y-m-d')), $number, $shippingDates);
    }
    return $shippingDates;
}

print_r(getShippingDates(new DateTime('2020-07-10T09:59:59')));

//get 2 possible shippingdate based on current date and time
print_r(getShippingDates(new DateTime($time = "now"),2));
hans
  • 1
  • 2