Reputation: 3162
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
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
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
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
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
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
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