Andrei RRR
Andrei RRR

Reputation: 3162

Add X Days To Date, Excluding Weekends

I'm currently developing an online subscription application. I'm having some challenges on the part where the user will select the Number of Days to subscribe and then the Start Date. The PHP application should then be able to calculate the End Date excluding weekends.

<form method="post">
    <input name="startdate" type="text" />
    <input name="numberofdays" type="text" />
</form>

Someone can help me with this?

Upvotes: 4

Views: 15440

Answers (6)

Vincent L&#39;Ecluse
Vincent L&#39;Ecluse

Reputation: 96

I came to this looking for the solution. In the end I found a one line solution.

<?php 
    echo date('d/m/Y',strtotime("+10 weekdays"));
?>

Upvotes: 4

Enessari
Enessari

Reputation: 125

My turn to propose a function that fix this problem :)

public static function addDaysToDate($startDate, $daysToAdd)
    {
        // loop for X days
        for ($i = 0; $i < $daysToAdd; $i++) {
            // get what day it is next day
            $nextDay = $startDate->modify('+1 day');
            
            // if it's Saturday or Sunday get $i-1
            if (in_array($nextDay->format('w'), [0, 6]) || self::isHoliday($nextDay->getTimestamp())) {
                $i--;
            }
        }
    return $nextDay;
}

isHoliday function is determining whether the day is a public holiday or not :

/**
 * Fonction permettant de retour la date de Pâques au format timestamp
 */
public static function easterDate($year)
{
    $a = $year % 4;
    $b = $year % 7;
    $c = $year % 19;
    $m = 24;
    $n = 5;
    $d = (19 * $c + $m ) % 30;
    $e = (2 * $a + 4 * $b + 6 * $d + $n) % 7;
    $easterdate = 22 + $d + $e;

    if ($easterdate > 31) {
        $day = $d + $e - 9;
        $month = 4;
    } else {
        $day = 22 + $d + $e;
        $month = 3;
    }

    if ($d == 29 && $e == 6) {
        $day = 10;
        $month = 04;
    } elseif ($d == 28 && $e == 6) {
        $day = 18;
        $month = 04;
    }
    return mktime(0, 0, 0, $month, $day, $year);
}

/**
* Fonction permettant de retourner les jours fériés d'une année passée en paramètre
*/
public static function publicHolidays($year)
{
    if ($year === null) {
        $year = intval(strftime('%Y'));
    }

    $easterDate = self::easterDate($year);
    $easterDay = date('j', $easterDate);
    $easterMonth = date('n', $easterDate);
    $easterYear = date('Y', $easterDate);
    $holidays = array(
        // Jours fériés fixes
        mktime(0, 0, 0, 1, 1, $year),// 1er janvier
        mktime(0, 0, 0, 5, 1, $year),// Fête du travail
        mktime(0, 0, 0, 5, 9, $year),// Fête de l'Europe
        mktime(0, 0, 0, 6, 23, $year),// Fête nationale
        mktime(0, 0, 0, 8, 15, $year),// Assomption
        mktime(0, 0, 0, 11, 1, $year),// Toussaint
        mktime(0, 0, 0, 12, 25, $year),// Noël
        mktime(0, 0, 0, 12, 26, $year),// Saint-Etienne

        // Jour fériés qui dépendent de Pâques
        mktime(0, 0, 0, $easterMonth, $easterDay + 1, $easterYear),// Lundi de Pâques
        mktime(0, 0, 0, $easterMonth, $easterDay + 39, $easterYear),// Ascension
        mktime(0, 0, 0, $easterMonth, $easterDay + 50, $easterYear), // Pentecôte
    );

    sort($holidays);

    return $holidays;
}

/**
 * Fonction permettant de savoir si un timestamp d'une date passée en paramètre est férié ou pas
 */
public static function isHoliday($timestamp)
{
    $iYear = strftime('%Y', $timestamp);
    $aHolidays = self::publicHolidays($iYear);
    /*
    * On est obligé de convertir les timestamps en string à cause des décalages horaires.
    */
    $aHolidaysString = array_map(function ($value) {
        return strftime('%Y-%m-%d', $value);
    }, $aHolidays);

    if (in_array(strftime('%Y-%m-%d', $timestamp), $aHolidaysString)) {
        return true;
    }

    return false;
}

Hope that would help you !

Best regards,

Enes

Upvotes: 3

CodingEra
CodingEra

Reputation: 1807

I have developed a new function today that can help people who have this type of problem. Depends how the startdate is sent, but assuming you are using Y-m-d you can use DateTime

<?php  public static function getOrderEndDate( $start_date, $orderDaysCode ){

    $saturday_off = false;
    if( $orderDaysCode == 'meal_monthly_6' ) { $orderDays = 24; }
    elseif( $orderDaysCode == 'meal_monthly_5' ) {
        $orderDays = 20;
        $saturday_off = true;
    }elseif( $orderDaysCode == 'meal_weekly' ) {
        $orderDays = 5;
        $saturday_off = true;
    }
    else{ $orderDays = 1; }   // Daily Meal

    $formatted_date = new DateTime( $start_date );

    $date_timestamp = $formatted_date->getTimestamp();
    // loop for X days
    for( $i = 0; $i < ( $orderDays - 1 ); $i++ ) {
        // get what day it is next day
        $nextDay = date('w', strtotime('+1day', $date_timestamp) );
        // if it's Sunday or Saturday get $i-1
        if( $nextDay == 0 || ( $nextDay == 6 && $saturday_off ) ) { $i--; }
        // modify timestamp, add 1 day
        $date_timestamp = strtotime('+1day', $date_timestamp);
    }

    $formatted_date->setTimestamp($date_timestamp);

    return $formatted_date->format( 'Y-m-d' );
}

$orderEndDate = getOrderEndDate( '2020-06-17', 'meal_monthly_6' ); ?>

Upvotes: 1

ScottNL
ScottNL

Reputation: 83

Same result, shorter version:

function addDays($days,$format="Y-m-d"){

    for($i=0;$i<$days;$i++){
        $day = date('N',strtotime("+".($i+1)."day"));
        if($day>5)
            $days++;
    }
    return date($format,strtotime("+$i day"));
}

Upvotes: 4

Kevin Riquena
Kevin Riquena

Reputation: 11

I have developed a new function today that can help people who have this type of problem.

function sumDays($days = 0, $format = 'd/m/Y') {
    $incrementing = $days > 0;
    $days         = abs($days);
    $actualDate   = date('Y-m-d');

    while ($days > 0) {
        $tsDate    = strtotime($actualDate . ' ' . ($incrementing ? '+' : '-') . ' 1 days');
        $actualDate = date('Y-m-d', $tsDate);

        if (date('N', $tsDate) < 6) {
            $days--;
        }
    }

    return date($format, strtotime($actualDate));
}

Upvotes: 1

Mihai Iorga
Mihai Iorga

Reputation: 39704

depends how the startdate is sent, but assuming you are using Y-m-d you can use DateTime

eg:

<?php 

    $_POST['startdate'] = '2012-08-14';
    $_POST['numberofdays'] = 10;

    $d = new DateTime( $_POST['startdate'] );
    $t = $d->getTimestamp();

    // loop for X days
    for($i=0; $i<$_POST['numberofdays']; $i++){

        // add 1 day to timestamp
        $addDay = 86400;

        // get what day it is next day
        $nextDay = date('w', ($t+$addDay));

        // if it's Saturday or Sunday get $i-1
        if($nextDay == 0 || $nextDay == 6) {
            $i--;
        }

        // modify timestamp, add 1 day
        $t = $t+$addDay;
    }

    $d->setTimestamp($t);

    echo $d->format( 'Y-m-d' ). "\n";

?>

Upvotes: 23

Related Questions