Search code examples
phpdatedatetimewoocommercestrtotime

Add local holidays to date/time calculation in PHP


I want to show an estimated delivery time in my shop. If a customer orders before 4pm, he gets his order the next day. But if the order is on a friday or on the weekend, he gets the order on the next monday.

That all works fine. But I need to add some holidays like christmas, new year and local holidays based on a state in my country.

I found a solution to identify the fixed and flexible (like easter) holidays in my state.

But I don't know how to work with them in the current function. If an order is placed before one of these holidays I need to move the future date some days ahead.

Here's the current code (based on this code):

date_default_timezone_set( 'Europe/Berlin' );

$current_year   = date('Y');
$next_year      = date('Y', strtotime( $current_year." + 1 year" ));

// Holidays
$neujahr        = date('d.m.Y',strtotime(date($next_year.'-01-01')));
$ostern         = date('d.m.Y', easter_date($current_year));
$karfreitag     = date( "l jS F", strtotime( $ostern." - 2 days" ) );
$ostermontag    = date( "l jS F", strtotime( $ostern." + 1 days" ) );
$tagderarbeit   = date('d.m.Y',strtotime(date('Y-05-01')));
$himmelfahrt    = date( "l jS F", strtotime( $ostern." + 39 days" ) );
$pfingstmontag  = date( "l jS F", strtotime( $ostern." + 50 days" ) );
$fronleichnam   = date( "l jS F", strtotime( $ostern." + 60 days" ) );
$einheit        = date('d.m.Y',strtotime(date('Y-10-03')));
$allerheiligen  = date('d.m.Y',strtotime(date('Y-11-01')));
$weihnachten1   = date('d.m.Y',strtotime(date('Y-12-25')));
$weihnachten2   = date('d.m.Y',strtotime(date('Y-12-26')));

// if FRI/SAT/SUN delivery will be MON
if ( date( 'N' ) >= 5 ) {
  $del_day = date( "l jS F", strtotime( "next monday" ) );
  $order_by = "Monday";
}

// if MON/THU after 4PM delivery will be TOMORROW
elseif ( date( 'H' ) >= 16 ) {
  $del_day = date( "l jS F", strtotime( "tomorrow" ) );
  $order_by = "tomorrow";
}

// if MON/THU before 4PM delivery will be TODAY
else {
  $del_day = date( "l jS F", strtotime( "today" ) );
  $order_by = "today";
}

$html = "<br><div class='woocommerce-message' style='clear:both'>Order by 4PM {$order_by} for delivery on {$del_day}</div>";

echo $html;

Solution

  • The working method you are currently applying can work with multiple different if / else statements to check all conditions, so I prefer a different approach.

    It goes as follows

    1. As of today date 8 possible delivery dates are generated (expl: today = 14/07/2020, possible dates are 14, 15, 16, 17, 18, 19, 20 & 21/07/2020
    2. If today is already after 4 pm, the date of today expires (14/07/2020)
    3. Less than 4 pm but a Friday, Saturday or Sunday, remove date
    4. Then remove the (other) dates from the weekend: friday, saturday & sunday (17, 18, 19/07/2020)
    5. Then all holidays (if any) are filtered out of the result
    6. In the last step, the first value in the result is used and shown in the output message.

    • Note: It is easy to test by adjusting $today = date( 'd.m.Y' ); values ​​to any date in the future, like $today = date( '25.12.2024' ); which would return
      • Delivery on Monday 30th December
      • 25, 26 = holidays. 27, 28 & 29/12/2024 = friday, saturday & sunday
    date_default_timezone_set( 'Europe/Berlin' );
    
    // Today
    $today = date( 'd.m.Y' );
    
    // Possible delivery dates from today
    for ( $i = 0; $i <= 7; $i++) {
        $possible_delivery_dates[] = date( 'd.m.Y', strtotime( $today . '+' . $i . 'days' ) ); 
    }
    
    // Today ?
    if ( date( 'H', strtotime( $today ) ) >= 16 ) {
        // Today NOT possible
        unset( $possible_delivery_dates[0] );
    } elseif ( date( 'N', strtotime( $today ) ) >= 5 ) {
        // Today (weekend) NOT possible
        unset( $possible_delivery_dates[0] );
    }
    
    // Next Fri, Sat & Sun
    $next_friday   = date( 'd.m.Y', strtotime( $today . 'next friday' ) );
    $next_saturday = date( 'd.m.Y', strtotime( $today . 'next saturday' ) );
    $next_sunday   = date( 'd.m.Y', strtotime( $today . 'next sunday' ) );
    
    // Remove next fri, sat & sun
    $possible_delivery_dates = array_diff( $possible_delivery_dates, [ $next_friday, $next_saturday, $next_sunday ] );
    
    // Current & next year
    $current_year  = date( 'Y', strtotime( $today ) );
    $next_year     = date( 'Y', strtotime( $today . '+ 1 year' ));
    
    // Holidays
    $neujahr       = date( 'd.m.Y', strtotime( date( $next_year . '-01-01' ) ) );
    $ostern        = date( 'd.m.Y', easter_date( $current_year ) );
    $karfreitag    = date( 'd.m.Y', strtotime( $ostern . '- 2 days' ) );
    $ostermontag   = date( 'd.m.Y', strtotime( $ostern . '+ 1 days' ) );
    $tagderarbeit  = date( 'd.m.Y', strtotime( date( $current_year . '-05-01') ) );
    $himmelfahrt   = date( 'd.m.Y', strtotime( $ostern . '+ 39 days' ) );
    $pfingstmontag = date( 'd.m.Y', strtotime( $ostern . '+ 50 days' ) );
    $fronleichnam  = date( 'd.m.Y', strtotime( $ostern . '+ 60 days' ) );
    $einheit       = date( 'd.m.Y', strtotime( date( $current_year . '-10-03' ) ) );
    $allerheiligen = date( 'd.m.Y', strtotime( date( $current_year . '-11-01' ) ) );
    $weihnachten1  = date( 'd.m.Y', strtotime( date( $current_year . '-12-25' ) ) );
    $weihnachten2  = date( 'd.m.Y', strtotime( date( $current_year . '-12-26' ) ) );
    
    // Holidays (array)
    $holidays = array( $neujahr, $ostern, $karfreitag, $ostermontag, $tagderarbeit, $himmelfahrt, $pfingstmontag, $fronleichnam, $einheit, $allerheiligen, $weihnachten1, $weihnachten2 );
    
    // Remove holidays
    $possible_delivery_dates = array_diff( $possible_delivery_dates, $holidays );
    
    // First value
    $first_val = reset( $possible_delivery_dates );
    
    $html = 'Delivery on ' . date( 'l jS F', strtotime( $first_val ) );
    
    echo $html;