Date range array excluding the Sunday & the holiday in PHP - php

I have a function which returns all the dates between two dates in an array, But I need to exclude Sundays in that array.
public function dateRange($first, $last, $step = '+1 day', $format = 'd/m/Y' ) {
$dates = array();
$current = strtotime($first);
$last = strtotime($last);
while( $current <= $last ) {
$dates[] = date($format, $current);
$current = strtotime($step, $current);
}
return $dates;
}
After excluding the Sundays, I have a table where I will be storing some dates, I need to exclude those dates from the array too.
like, If I enter the date range as 01-05-2012(DD-MM-YYYY) to 10-05-2012,
The 06-05-2012 will be Sunday & the date 01-05-2012 & 08-05-2012 will be in the table which I mentioned above,
The final out put should be like,
02-05-2012
03-05-2012
04-05-2012
05-05-2012
07-05-2012
09-05-2012
10-05-2012
How to do this in PHP ?
I tried some but couldn't find the right way to do it.

For the Sundays part:
public function dateRange($first, $last, $step = '+1 day', $format = 'd/m/Y' ) {
$dates = array();
$current = strtotime($first);
$last = strtotime($last);
while( $current <= $last ) {
if (date("D", $current) != "Sun")
$dates[] = date($format, $current);
$current = strtotime($step, $current);
}
return $dates;
}
For the holidays part:
First you need to load the dates into some kind of array and then loop through the array for each of your dates and check if they match.

I found the answer for my question, Thanks for the people who helped me.
public function dateRange($first, $last, $step = '+1 day', $format = 'd/m/Y' ) {
$dates = array();
$current = strtotime($first);
$last = strtotime($last);
while( $current <= $last ) {
$sql = "SELECT * FROM ost_holidays where holiday_date='".date('Y-m-d', $current)."' LIMIT 1";
$sql = db_query($sql);
$sql = db_fetch_array($sql);
if($sql['holiday_date'] != date('Y-m-d',$current))
if (date('w', $current) != 0)
$dates[] = date($format, $current);
$current = strtotime($step, $current);
}
return $dates;
}
The above code is for removing the holidays & the Sundays in the given range.

I did this same above method in Jquery
//Convert dates into desired formatt
function convertDates(str) {
var date = new Date(str),
mnth = ("0" + (date.getMonth() + 1)).slice(-2),
day = ("0" + date.getDate()).slice(-2);
return [date.getFullYear(), mnth, day].join("-");
}
// Returns an array of dates between the two dates
var getDates = function(startDate, endDate, holidays) {
var dates = [],
currentDate = startDate,
addDays = function(days) {
var date = new Date(this.valueOf());
date.setDate(date.getDate() + days);
return date;
};
while (currentDate <= endDate) {
dates.push(currentDate);
currentDate = addDays.call(currentDate, 1);
}
return dates;
};
//Indise Some Function
var datesTemp = [];
var dates = getDates(new Date(prodDet.details.date1), new Date(prodDet.details.date2));
dates.forEach(function(date) {
if (date.getDay() != 0) {
datesTemp.push(convertDates(date));
}
});
datesTemp.forEach(function(date) {
for (var j = 0; j < prodDet.holidays.length; j++) {
if ((prodDet.holidays[j] != date)) {
ideal.idates.push(date);
}
}
});
console.log(ideal.idates);
//Function Ends Here

Related

Counting the (year)quarters between two dates

I have project built using laravel and a I have to build a function that counts all the complete quarters that are in the selected date range - the dates used are inserted via input.
Here are the quarters(i used numerical representations for the months)
01 - 03 first quarter
04 - 06 second quarter
07 - 09 third quarter
10 - 12 forth quarter
I would really appreciate your help,because I've been at it for an entire day now and basically have nothing to show for it,i thing I've been trying so hard i'm actually at the point where i'm so tired, i can t think straight.
I do have some code but it;s worthless, because it doesn't work, and any kind of idea or snippet of code is welcomed.
Thanks for your help in advance.
I managed to do this using multiple functions; basically, if this is needed for chart statistics, then a more specific approach might be the case.
I have done this in Laravel with timestamp dates as input (this code can be adapted for getting semesters also :) , it works and is already tested):
public static function getQuartersBetween($start_ts, $end_ts)
{
$quarters = [];
$months_per_year = [];
$years = self::getYearsBetween($start_ts, $end_ts);
$months = self::getMonthsBetween($start_ts, $end_ts);
foreach ($years as $year) {
foreach ($months as $month) {
if ($year->format('Y') == $month->format('Y')) {
$months_per_year[$year->format('Y')][] = $month;
}
}
}
foreach ($months_per_year as $year => $months) {
$january = new Date('01-01-' . $year);
$march = new Date('01-03-' . $year);
$april = new Date('01-04-' . $year);
$june = new Date('01-06-' . $year);
$july = new Date('01-07-' . $year);
$september = new Date('01-09-' . $year);
$october = new Date('01-10-' . $year);
$december = new Date('01-12-' . $year);
if (in_array($january, $months) && in_array($march, $months)) {
$quarter_per_year['label'] = 'T1 / ' . $year;
$quarter_per_year['start_day'] = $january->startOfMonth();
$quarter_per_year['end_day'] = $march->endOfMonth()->endOfDay();
array_push($quarters, $quarter_per_year);
}
if (in_array($april, $months) && in_array($june, $months)) {
$quarter_per_year['label'] = 'T2 / ' . $year;
$quarter_per_year['start_day'] = $april->startOfMonth();
$quarter_per_year['end_day'] = $june->endOfMonth()->endOfDay();
array_push($quarters, $quarter_per_year);
}
if (in_array($july, $months) && in_array($september, $months)) {
$quarter_per_year['label'] = 'T3 / ' . $year;
$quarter_per_year['start_day'] = $july->startOfMonth();
$quarter_per_year['end_day'] = $september->endOfMonth()->endOfDay();
array_push($quarters, $quarter_per_year);
}
if (in_array($october, $months) && in_array($december, $months)) {
$quarter_per_year['label'] = 'T4 / ' . $year;
$quarter_per_year['start_day'] = $october->startOfMonth();
$quarter_per_year['end_day'] = $december->endOfMonth()->endOfDay();
array_push($quarters, $quarter_per_year);
}
}
return $quarters;
}
and getting the years between:
public static function getYearsBetween($start_ts, $end_ts, $full_period = false)
{
$return_data = [];
$current = mktime(0, 0, 0, date('m', $start_ts), date('d', $start_ts), date('Y', $start_ts));
while ($current < $end_ts) {
$temp_date = $current;
$year = new Date($temp_date);
$return_data[] = $year;
$current = strtotime("+1 year", $current); // add a year
}
if ($full_period) {
$return_data[] = $end_ts;
}
return $return_data;
}
, also getting the months needed
public static function getMonthsBetween($start_ts, $end_ts, $full_period = false)
{
$return_data = $month_list = [];
$current = mktime(0, 0, 0, date('m', $start_ts), date('d', $start_ts), date('Y', $start_ts));
while ($current <= $end_ts) {
$temp_date = $current;
$date = new Date($temp_date);
$month_list[] = $date;
$current = strtotime("+1 month", $current); // add a month
}
$start_date_last_month = new Date(array_first($month_list));
$start_date_last_month = $start_date_last_month->startOfMonth()->format('m-d');
$temp_end_date = new Date($start_ts);
$temp_end_date = $temp_end_date->format('m-d');
if ($start_date_last_month < $temp_end_date) {
array_shift($month_list);
}
$end_date_last_month = new Date(end($month_list));
$current_day_month = $end_date_last_month->endOfMonth()->format('m-d');
$temp_end_date = new Date($end_ts);
$end_day_of_month = $temp_end_date->format('m-d');
if ($end_day_of_month < $current_day_month) {
array_pop($month_list);
}
if (count($month_list) == 0) {
$month_list[] = $end_date_last_month->subMonth();
}
$return_data = $month_list;
if ($full_period) {
$return_data[] = $end_ts;
}
return $return_data;
}
You can do something like in this example:
$February = 2;
$October = 10;
$completedQuarters = ceil($October/3) - ceil($February/3); // = 3
What about the quarter in which the date range starts, should it also count? If it should only count if it begins in the first month of a quarter you can check for it like this:
$completedQuarters = ceil($October/3) - ceil($February/3) -1; // = 2
if($February-1%3 == 0) $completedQuarters += 1;
You´re description is not very clear, let me know if that´s what you had in mind.
Not sure if the following is what you are meaning but might be useful
$date_start='2015/03/12';
$date_end='2017/11/14';
$timezone=new DateTimeZone('Europe/London');
$start=new DateTime( $date_start, $timezone );
$end=new DateTime( $date_end, $timezone );
$difference = $end->diff( $start );
$months = ( ( $difference->format('%y') * 12 ) + $difference->format('%m') );
$quarters = intval( $months / 3 );
printf( 'Quarters between %s and %s is %d covering %d months', $start->format('l, jS F Y'), $end->format('l, jS F Y'), $quarters, $months );
/*
This will output
----------------
Quarters between Thursday, 12th March 2015 and Tuesday, 14th November 2017 is 10 covering 32 months
*/
Something like this in the function and you should be set.
use Carbon\Carbon;
$first = Carbon::parse('2012-1-1'); //first param
$second = Carbon::parse('2014-9-15'); //second param
$fY = $first->year; //2012
$fQ = $first->quarter; //1
$sY = $second->year; //2014
$sQ = $second->quarter; //3
$n = 0; //the number of quarters we have counted
$i = 0; //an iterator we will use to determine if we are in the first year
for ($y=$fY; $y < $sY; $y++, $i++) { //for each year less than the second year (if any)
$s = ($i > 0) ? 1 : $fQ; //determine the starting quarter
for ($q=$s; $q <= 4; $q++) { //for each quarter
$n++; //count it
}
}
if ($sY > $fY) { //if both dates are not in the same year
$n = $n + $sQ; //total is the number of quarters we've counted plus the second quarter value
} else {
for ($q=$fQ; $q <= $sQ; $q++) { //for each quarter between the first quarter and second
$n++; //count it
}
}
print $n; //the value to return (11)

how to get sunday date between two date

I try this
<?php
$startdate = '2016-07-15';
$enddate = '2016-07-17';
$sundays = [];
$startweek=date("W",strtotime($startdate));
$endweek=date("W",strtotime($enddate));
$year=date("Y",strtotime($startdate));
for($i=$startweek;$i<=$endweek;$i++) {
$result=$this->getWeek($i,$year);
if($result>$startdate && $result<$enddate) {
$sundays[] = $result;
}
}
print_r($sundays);
public function getWeek($week, $year)
{
$dto = new \DateTime();
$result = $dto->setISODate($year, $week, 0)->format('Y-m-d');
return $result;
}
?>
this return blank array. but in between two dates 2016-07-17 is Sunday.
I get output as 2016-07-17
I refer this here
But in this link return output as no of sunday not date.
Give this a try:
$startDate = new DateTime('2016-07-15');
$endDate = new DateTime('2016-07-17');
$sundays = array();
while ($startDate <= $endDate) {
if ($startDate->format('w') == 0) {
$sundays[] = $startDate->format('Y-m-d');
}
$startDate->modify('+1 day');
}
var_dump($sundays);
If you want later to use the DateTime objects instead of the formatted date, then you must use DateTimeImmutable for the $startDate variable:
$startDate = new DateTimeImmutable('2016-07-15');
$endDate = new DateTimeImmutable('2016-07-17');
$sundays = array();
while ($startDate <= $endDate) {
if ($startDate->format('w') == 0) {
$sundays[] = $startDate;
}
$startDate->modify('+1 day');
}
var_dump($sundays);
function getDateForSpecificDayBetweenDates($startDate, $endDate, $weekdayNumber)
{
$startDate = strtotime($startDate);
$endDate = strtotime($endDate);
$dateArr = array();
do
{
if(date("w", $startDate) != $weekdayNumber)
{
$startDate += (24 * 3600); // add 1 day
}
} while(date("w", $startDate) != $weekdayNumber);
while($startDate <= $endDate)
{
$dateArr[] = date('Y-m-d', $startDate);
$startDate += (7 * 24 * 3600); // add 7 days
}
return($dateArr);
}
$dateArr = getDateForSpecificDayBetweenDates('2010-01-01', '2010-12-31', 0);
print "<pre>";
print_r($dateArr);
Try out this code..
Try this
$start = new DateTime($startDate);
$end = new DateTime($endDate);
$sundays = [];
while ($start->getTimestamp() != $end->getTimestamp()) {
if ($start->format('w') == 0) {
$sundays[] = $start->format('Y-m-d');
}
$start->add('+1 DAY');
}
This will return you all sundays between two dates.
$startdate = '2016-05-1';
$enddate = '2016-05-20';
function getSundays($start, $end) {
$timestamp1 = strtotime($start);
$timestamp2 = strtotime($end);
$sundays = array();
$oneDay = 60*60*24;
for($i = $timestamp1; $i <= $timestamp2; $i += $oneDay) {
$day = date('N', $i);
// If sunday
if($day == 7) {
// Save sunday in format YYYY-MM-DD, if you need just timestamp
// save only $i
$sundays[] = date('Y-m-d', $i);
// Since we know it is sunday, we can simply skip
// next 6 days so we get right to next sunday
$i += 6 * $oneDay;
}
}
return $sundays;
}
var_dump(getSundays($startdate, $enddate));
Use Carbon
$arrayOfDate = [];
$startDate = Carbon::parse($startDate)->modify('this sunday');
$endDate = Carbon::parse($endDate);
for ($date = $startDate; $date->lte($endDate); $date->addWeek()) {
$arrayOfDate[] = $date->format('Y-m-d');
}
return $arrayOfDate;

Php add 5 working days to current date excluding weekends (sat-sun) and excluding (multiple) holidays

For delivery of our webshop, we need to calculate 5 working days from the current date in php.
Our working days are from monday to friday and we have several closing days (holidays) which cannot be included either.
I've found this script, but this doesn't include holidays.
<?php
$_POST['startdate'] = date("Y-m-d");
$_POST['numberofdays'] = 5;
$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";
?>
You can use the "while statement", looping until get enough 5 days. Each time looping get & check one next day is in the holiday list or not.
Here is the the example:
$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
A function based on Tinh Dang's answer:
function getFutureBusinessDay($num_business_days, $today_ymd = null, $holiday_dates_ymd = []) {
$num_business_days = min($num_business_days, 1000);
$business_day_count = 0;
$current_timestamp = empty($today_ymd) ? time() : strtotime($today_ymd);
while ($business_day_count < $num_business_days) {
$next1WD = strtotime('+1 weekday', $current_timestamp);
$next1WDDate = date('Y-m-d', $next1WD);
if (!in_array($next1WDDate, $holiday_dates_ymd)) {
$business_day_count++;
}
$current_timestamp = $next1WD;
}
return date('Y-m-d', $current_timestamp);
}
I made it limit the loop to 1000 business days. There could be no limit if desired.
Based on Luke's answer:
Difference is that this one generates holidays for every year
<?php
class DateHelper
{
//change at will
const HOLIDAY_DATES = [
['day' => 25, 'month' => 12],//christimas
['day' => 1, 'month' => 1],//new year
['day' => 13, 'month' => 4]//easter
];
/**
* #param int $numBusinessDays
* #param \DateTimeInterface $date
* #return \DateTime
*/
public static function getFutureBusinessDay(int $numBusinessDays, \DateTimeInterface $date)
{
$numBusinessDays = min($numBusinessDays, 1000);
$businessDayCount = 0;
$currentTimestamp = strtotime($date->format('Y-m-d'));
$holidayDates = self::getHolidayDates();
while ($businessDayCount < $numBusinessDays) {
$next1WD = strtotime('+1 weekday', $currentTimestamp);
$next1WDDate = date('Y-m-d', $next1WD);
if (!in_array($next1WDDate, $holidayDates)) {
$businessDayCount++;
}
$currentTimestamp = $next1WD;
}
return (new \DateTime())->setTimestamp($currentTimestamp);
}
/**
* #return array
*/
private static function getHolidayDates()
{
$holidays = [];
foreach (self::HOLIDAY_DATES as $holidayDate) {
$date = new \DateTime();
$date->setDate($date->format('Y'), $holidayDate['month'], $holidayDate['day']);
$holidays[] = $date->format('Y-m-d');
}
return $holidays;
}
}

How to calculate working hours between two dates excluding holidays and weekends?

Let's say that I have two dates:
$initialDate = '08/10/2015 09:30:24 am';
$finalDate = '15/10/2015 15:47:38 pm';
$holiday = '12/10/2015';
I have to consider the hour of these days.
Hours to consider : 8 hours per day;
Start : 8 pm
End: 18 pm (24 hours format )
Lunch break start: 12:00 pm
Lunch break end: 14:00 pm
Example 1 : From 08/10/2015 10:00:00 to 09/10/2015 17:00:00 results 13 working hours. ( excludes lunch break )
Example 2 : From 08/10/2015 14:00:00 to 09/10/2015 18:00:00 results 12 working hours. ( Do not exclude 2 hours from begin date, because starts after 14:00 pm, lunch break )
Example 3 : From 08/10/2015 16:00:00 to 09/10/2015 18:00:00 results 10 working hours. ( Do not exclude 2 hours from begin date, because starts after 14:00 pmm lunch break )
Exampld 4 : From 08/10/2015 08:00:00 to 09/10/2015 11:00:00 results 14 working hours. ( Exclude 2 hours from begin date, and do not exclude 2 hours from end date, because isn't after 14:00 pm )
And I have to calculate the working hours and working days between those two dates, excluding weekends and Holidays, how can I do that ? I'm using PHP.
PS: I Already have something, but without lunch break... I made a research here on StackOverFlow.
Code:
function get_workdays($dataInicial,$dataFinal){
// arrays
$days_array = array();
$skipdays = array("Saturday", "Sunday");
$skipdates = get_feriados();
// other variables
$i = 0;
$current = $dataInicial;
if($current == $dataFinal) // same dates
{
$timestamp = strtotime($dataInicial);
if (!in_array(date("l", $timestamp), $skipdays)&&!in_array(date("Y-m-d", $timestamp), $skipdates)) {
$days_array[] = date("Y-m-d",$timestamp);
}
}
elseif($current < $dataFinal) // different dates
{
while ($current < $dataFinal) {
$timestamp = strtotime($dataInicial." +".$i." day");
if (!in_array(date("l", $timestamp), $skipdays)&&!in_array(date("Y-m-d", $timestamp), $skipdates)) {
$days_array[] = date("Y-m-d",$timestamp);
}
$current = date("Y-m-d",$timestamp);
$i++;
}
}
return $days_array;
}
function get_feriados(){
$dateAno = Date('Y');
$days_array = array(
$dateAno.'-10-12', // Padroeira do Brasil/ Dias das Crianças
$dateAno.'-11-02', // Finados
$dateAno.'-12-25' // Finados
);
return $days_array;
}
date_default_timezone_set('America/Sao_Paulo');
$dateAno = Date('Y');
$dataInicial = Date('08/10/2015 H:i');
$dataFinal = Date('13/10/2015 H:i');
// timestamps
$from_timestamp = strtotime(str_replace('/', '-', $dataInicial));
$to_timestamp = strtotime(str_replace('/', '-', $dataFinal));
// work day seconds
$workday_start_hour = 9;
$workday_end_hour = 17;
$workday_seconds = ($workday_end_hour - $workday_start_hour)*3600;
// work days beetwen dates, minus 1 day
$from_date = date('Y-m-d',$from_timestamp);
$to_date = date('Y-m-d',$to_timestamp);
$workdays_number = count(get_workdays($from_date,$to_date))-1;
$workdays_number = $workdays_number<0 ? 0 : $workdays_number;
// start and end time
$start_time_in_seconds = date("H",$from_timestamp)*3600+date("i",$from_timestamp)*60;
$end_time_in_seconds = date("H",$to_timestamp)*3600+date("i",$to_timestamp)*60;
// final calculations
$working_hours = ($workdays_number * $workday_seconds + $end_time_in_seconds - $start_time_in_seconds) / 86400 * 24;
print_r('<br/> Horas úteis '.$working_hours);
}
But don't consider two hours of break lunch. Can somebody please help me ?
If you use PHP 5.3 or higher, you can do this:
$datefrom = DateTime::createFromFormat('d/m/Y', '08/10/2015');
$dateto = DateTime::createFromFormat('d/m/Y', '15/10/2015');
$interval = $datefrom->diff($dateto);
$days = intval($interval->format('%a'));
Also you can remove holidays with if:
if ($datetime1->getTimestamp() < $holiday->getTimestamp() and $datetime2->getTimestamp() > $holiday->getTimestamp()) $days--;
Calculate hours between two days:
$datefrom = DateTime::createFromFormat('d/m/Y H:i:s', '08/10/2015 12:51:34');
$dateto = DateTime::createFromFormat('d/m/Y H:i:s', '15/10/2015 13:14:56');
$hours = intval($interval->format('%a')) * 24 + $interval->format('%h');
You can calculate hours of launches sum and then subtract it.
How to ignore weekends or calculate ignore days:
while($dateto->getTimestamp() > $datefrom->getTimestamp()) {
if (in_array($datefrom->format('w'), array('0','6'))) $ignore_days += 1;
$datefrom->modify('+1 day');
}
I expect this will do all you want. But I changed the datetime format as follows. Check it. Used less comments. If any query, please ask. Holidays are arrays, add and remove as required.
Times between 12:00 - 14:00 is handled.
Times below 08:00 is handled.
Times above 18:00 is handled.
<?php
$initialDate = '2015-10-13 08:15:00'; //start date and time in YMD format
$finalDate = '2015-10-14 11:00:00'; //end date and time in YMD format
$holiday = array('2015-10-12'); //holidays as array
$noofholiday = sizeof($holiday); //no of total holidays
//create all required date time objects
$firstdate = DateTime::createFromFormat('Y-m-d H:i:s',$initialDate);
$lastdate = DateTime::createFromFormat('Y-m-d H:i:s',$finalDate);
if($lastdate > $firstdate)
{
$first = $firstdate->format('Y-m-d');
$first = DateTime::createFromFormat('Y-m-d H:i:s',$first." 00:00:00" );
$last = $lastdate->format('Y-m-d');
$last = DateTime::createFromFormat('Y-m-d H:i:s',$last." 23:59:59" );
$workhours = 0; //working hours
for ($i = $first;$i<=$last;$i->modify('+1 day') )
{
$holiday = false;
for($k=0;$k<$noofholiday;$k++) //excluding holidays
{
if($i == $holiday[$k])
{
$holiday = true;
break;
} }
$day = $i->format('l');
if($day === 'Saturday' || $day === 'Sunday') //excluding saturday, sunday
$holiday = true;
if(!$holiday)
{
$ii = $i ->format('Y-m-d');
$f = $firstdate->format('Y-m-d');
$l = $lastdate->format('Y-m-d');
if($l ==$f )
$workhours +=sameday($firstdate,$lastdate);
else if( $ii===$f)
$workhours +=firstday($firstdate);
else if ($l ===$ii)
$workhours +=lastday($lastdate);
else
$workhours +=8;
}
}
echo $workhours; //echo the hours
}
else
echo "lastdate less than first date";
function sameday($firstdate,$lastdate)
{
$fmin = $firstdate->format('i');
$fhour = $firstdate->format('H');
$lmin = $lastdate->format('i');
$lhour = $lastdate->format('H');
if($fhour >=12 && $fhour <14)
$fhour = 14;
if($fhour <8)
$fhour =8;
if($fhour >=18)
$fhour =18;
if($lhour<8)
$lhour=8;
if($lhour>=12 && $lhour<14)
$lhour = 14;
if($lhour>=18)
$lhour = 18;
if($lmin == 0)
$min = ((60-$fmin)/60)-1;
else
$min = ($lmin-$fmin)/60;
return $lhour-$fhour + $min;
}
function firstday($firstdate) //calculation of hours of first day
{
$stmin = $firstdate->format('i');
$sthour = $firstdate->format('H');
if($sthour<8) //time before morning 8
$lochour = 8;
else if($sthour>18)
$lochour = 0;
else if($sthour >=12 && $sthour<14)
$lochour = 4;
else
{
$lochour = 18-$sthour;
if($sthour<=14)
$lochour-=2;
if($stmin == 0)
$locmin =0;
else
$locmin = 1-( (60-$stmin)/60); //in hours
$lochour -= $locmin;
}
return $lochour;
}
function lastday($lastdate) //calculation of hours of last day
{
$stmin = $lastdate->format('i');
$sthour = $lastdate->format('H');
if($sthour>=18) //time after 18
$lochour = 8;
else if($sthour<8) //time before morning 8
$lochour = 0;
else if($sthour >=12 && $sthour<14)
$lochour = 4;
else
{
$lochour = $sthour - 8;
$locmin = $stmin/60; //in hours
if($sthour>14)
$lochour-=2;
$lochour += $locmin;
}
return $lochour;
}
?>
Check the bellow code, that will return the number of Working days
function number_of_working_days($from, $to) {
$workingDays = [1, 2, 3, 4, 5];// date format = (1 = Monday,2 = Tue, ...)
$holidayDays = ['*-12-25', '*-02-14', '2015-12-23']; // variable and fixed holidays
$from = new DateTime($from);
$to = new DateTime($to);
$to->modify('+1 day');
$interval = new DateInterval('P1D');
$days = new DatePeriod($from, $interval, $to);
$no_of_working_days = 0;
foreach ($days as $day) {
if (!in_array($day->format('N'), $workingDays)||in_array($day->format('Y-m-d'), $holidayDays)||in_array($day->format('*-m-d'), $holidayDays)) {continue;}
$working_days++;
}
return $no_of_working_days;
}
echo number_of_working_days('2015-12-01', '2015-09-10');
From that you can easily calculate the Number of Working Hours.
I have created for you this nice class you can use. It requires the nesbot/carbon library (http://carbon.nesbot.com/) and you use it like so:
$calc = new HoursCalculator(
Carbon::createFromFormat("Y-m-d H:i", "2015-10-7 09:00"),
Carbon::createFromFormat("Y-m-d H:i", "2015-10-14 18:00"),
[
"2015-10-13"
]
);
echo $calc->getHours();
Heres the class:
class HoursCalculator {
const LUNCH_HOURS = 2;
protected $start;
protected $end;
protected $holidays;
protected $hoursTotal;
public function __construct(Carbon $start, Carbon $end, $holidays = [])
{
$this->start = $start;
$this->end = $end;
$this->holidays = $holidays;
}
public function getHours()
{
$dayHours = $this->getHoursInADay();
return $this->calculateHours($dayHours);
}
protected function getHoursInADay()
{
$start = $this->start;
$end = Carbon::createFromFormat("Y-m-d H:i", $this->start->format("Y-m-d") . " " . $this->end->format("H:i"));
return $start->diffInHours($end) - self::LUNCH_HOURS;
}
protected function getStartDate()
{
return $this->start->format('Y-m-d');
}
protected function calculateHours($hoursInDay)
{
$start = $this->start->copy()->startOfDay();
$end = $this->end->copy()->endOfDay();
$days = 0;
while($start->lt($end)) {
if (!$this->isHoliday($start) && !$this->isWeekend($start)) {
$days++;
}
$start->addDay(1);
}
return $days * $hoursInDay;
}
protected function isHoliday(Carbon $date)
{
$date->startOfDay();
foreach($this->holidays as $holiday) {
$holiday = Carbon::createFromFormat("Y-m-d", $holiday)->startOfDay();
if ($date->eq($holiday)) {
return true;
}
}
return false;
}
protected function isWeekend(Carbon $date)
{
return $date->isWeekend();
}
}
Hope this helps!

Average day of month from list of dates in PHP

I'm trying to find the average date and guess the next one.
The input is a list of dates that looks like this:
$completeDate = array(
'2015-04-13T00:00:00-0800',
'2015-03-20T00:00:00-0800',
'2015-02-17T00:00:00-0800',
'2015-01-10T00:00:00-0800'
);
I'm trying to scan a list of x amount of dates, and output an average of the dates overall.
So in the above example I think the output would be 2015-5-15 is expected average date.
How would I tackle this?
If you're looking for the average of those dates you can simply get the day of the year for each of those dates, average them out, and use that date:
$completeDate = array(
'2015-04-13T00:00:00-0800',
'2015-03-20T00:00:00-0800',
'2015-02-17T00:00:00-0800',
'2015-01-10T00:00:00-0800'
);
$first = null;
$last = null;
foreach($completeDate as $date) {
$dayOfYear = (new DateTime($date))->format('z');
if (is_null($first)) {
$first = $last = $dayOfYear;
}
else {
if ($dt < $first) {
$first = $dayOfYear;
}
if ($dt > $last) {
$last = $dayOfYear;
}
}
}
$avg = round(($first + $last) / 2);
$averageDate = DateTime::createFromFormat('z', $avg);
echo $averageDate->format('Y-m-d'); // 2015-02-26
Demo
If your looking for the average of the day of the month for the dates in that array and then use that day of the next month, you just need to average out the days of the month and then use that with the next month:
$completeDate = array(
'2015-04-13T00:00:00-0800',
'2015-03-20T00:00:00-0800',
'2015-02-17T00:00:00-0800',
'2015-01-10T00:00:00-0800'
);
$month = 0;
$days = 0;
foreach($completeDate as $date) {
$dt = new DateTime($date);
$month_num = $dt->format('n');
if ($month_num > $month) {
$month = $month_num;
}
$days += $dt->format('j');
}
$avg = round($days / count($completeDate));
$date = new DateTime(sprintf('%d-%01d-%01d', $dt->format('Y'), ++$month, $avg));
echo $date->format('Y-m-d'); // 2015-05-15
Demo

Categories