I want to calculate days excluding holidays stored in my database.
But I am getting error:
Fatal error: Call to undefined function isWithinDates()
$holidays = array();
$get_holidays = mysqli_query($link,"SELECT * FROM holiday_list");
while ($fetch_holidays = mysqli_fetch_array($get_holidays)){
$holidays[]=$fetch_holidays['holiday_date'];
}
echo $begin_date = $emp_leave_data['startDate'];
echo $end_date = $last_date;
$exclude = 0;
// Loop over holidays to find out whether they are in the range
foreach ($holidays as $holiday) {
if (isWithinDates(new DateTime($holiday), $begin_date, $end_date)) {
$exclude++;
}
}
function isWithinDates(DateTime $date, DateTime $start, DateTime $end)
{
return $date >= $start && $date <= $end;
}
// Get the amount of days between our start date and the end date
// and subtract the amount of days that we know to be holidays
echo $duration = $begin_date->diff($end_date)->days - $exclude + 1;
Related
I'm solving following task>
I have two dates - $start and $end and target year as $year.
dates are php DateTime objects, year is string.
add:dates comes acutaly from MySql field from this format 2017-02-01 15:00:00 ...
add2: if end date is null, I use todays date ...
I need to figure out how many days are between these two dates for specific year.
Also I need to round it for whole days, even if one minute in day should be counted as whole day ...
I can solve it by many many following ifs.
Expected results for values I used in example are
2016 is 0 days
2017 is 31 days
2018 is 32 days
2019 is 0 days
But are there any elegant php functions which can help me with this ?
What I did it seems to be wrong way and giving bad results - seems it counts full days only ...
Please see my code here >
<?php
$diff = True;
$start = DateTime::createFromFormat('Y-m-d H:i:s','2017-12-01 23:05:00');
$end = DateTime::createFromFormat('Y-m-d H:i:s','2017-12-03 00:05:00');
$year = '2017';
// start date
if ($start->format('Y')<$year)
{
$newstart = new DateTime('first day of January '. $year);
}
if ($start->format('Y')==$year)
{
$newstart = $start;
}
if ($start->format('Y')>$year)
{
$result = 0;
$diff = False;
}
// end date
if ($end->format('Y')>$year)
{
$newend = new DateTime('last day of December '. $year);
}
if ($end->format('Y')==$year)
{
$newend = $end;
}
if ($end->format('Y')<$year)
{
$result = 0;
$diff = False;
}
// count if diff is applicable
if ($diff)
{
$result = $newend->diff($newstart)->format("%a");
}
echo $result;
?>
But are there any elegant php functions which can help me with this ?
Read about DateTime::diff(). It returns a DateInterval object that contains the number of days (in $days) and by inspecting the values of $h, $i and $s you can tell if you have to increment it to round the result. You can also use min() and max() to crop the time interval to the desired year.
function getDays(DateTimeInterface $start, DateTimeInterface $end, $year)
{
// Extend the start date and end date to include the entire day
$s = clone $start; // Don't modify $start and $end, use duplicates
$s->setTime(0, 0, 0);
$e = clone $end;
$e->setTime(0, 0, 0)->add(new DateInterval('P1D')); // start of the next day
// Crop the input interval to the desired year
$s = min($s, new DateTime("$year-01-01 00:00:00"));
$year ++;
$e = max(new DateTime("$year-01-01 00:00:00"), $end); // start of the next year
if ($e <= $s) {
// The input interval does not span across the desired year
return 0;
}
// Compute the difference and return the number of days
$diff = $e->diff($s);
return $diff->days;
}
$d1 = strtotime('2017-05-15');
$d2 = strtotime('2017-05-31');
$div = 24 * 3600;
echo abs(($d2 - $d1) / $div); // 16 days
Just make sure and ONLY have the date part and you shouldn't have to deal with rounding.
I have code to select SQL Server :
<?php
include "koneksi.php";
$sql = odbc_exec($koneksi, "select * from trip");
while (odbc_fetch_row($sql)) {
$no = odbc_result($sql, "number");
$start = odbc_result($sql, "start");
$finish = odbc_result($sql,"finish");
}
?>
This loop contains the following data :
|No| Start | Finish |
|1 |2018-01-01|2018-01-05|
|2 |2018-01-10|2018-01-13|
I want to make array like this :
array(
"2018-01-01",
"2018-01-02",
"2018-01-03",
"2018-01-04",
"2018-01-05",
"2018-01-10",
"2018-01-11",
"2018-01-12",
"2018-01-13"
);
How can I create an array from this date range?
NB : looping can be more than 2 lines
For each row of the results, you can use a while() loop to add each date inside your array.
To manage date, you can use strtotime:
while (odbc_fetch_row($sql))
{
// grab our results
$start = odbc_result($sql, 'start');
$finish = odbc_result($sql, 'finish');
// convert date to timestamp
$start_tm = strtotime($start);
$finish_tm = strtotime($finish);
// add dates until $finish_tm reached
while ($start_tm < $finish_tm)
{
// push new date
$dates[] = date('Y-m-d', $start_tm);
// move date marker to next day
$start_tm = strtotime('+1 day', $start_tm);
}
}
This is an example on how to get the array:
<?php
while(odbc_fetch_row($sql)){
$no=odbc_result($sql,"number");
$start=odbc_result($sql,"start");
$finish=odbc_result($sql,"finish");
$arr = addIntoArray($arr, $start, $finish);
}
function addIntoArray($arr, $start, $end) {
$ts1 = strtotime($start);
$ts2 = strtotime($end);
for($ts=$ts1; $ts<=$ts2; $ts=$ts+86400) {
$arr[] = date('Y-m-d', $ts);
}
sort($arr);
return $arr;
}
two dates 13-10-2017 and 13-02-2018. I want to separate this period in months like 13-10-2017 to 31-10-2-17, 01-11-2017 to 30-11-2017, 01-12-2017 to 31-12-2017, 01-01-2018 to 31-01-2018 and 01-02-2018 to 13-02-2018. What I did I can get the month names in the date period but not in the format I want.
Here is my code:
$start_date = new DateTime('13-10-2017');
$end_date = new DateTime('13-02-2018');
$date_interval = new DateInterval('P1M');
$date_period = new DatePeriod($start_date, $date_interval, $end_date);
# calculating number of days in the interval
$interval = $start_date->diff( $end_date );
$days = $interval->days;
# getting names of the months in the interval
$month_count = 0;
$month_names = array();
foreach ($date_period as $date) {
$month_names[] = $date->format('F');
$month_count++;
}
$month_name_string = implode(',', $month_names);
echo $start_date->format('d-m-Y').' to '.$end_date->format('d-m-Y'). ' is ' .$days.' days and month names are: '.$month_name_string;
The output I get :
13-10-2017 to 13-02-2018 is 123 days and month names are: October,November,December,January
You can, while iterating, do the following checks:
If the current month is in $start_date, use its day for the start date
If the current month is in $end_date, use its day for the last day
Else, use the 1 and maximum day of each month (using the t format character)
Also, you need to set the time to 00:00:01 in the final day in order to have it considered in the DateInterval:
<?php
$start_date = new DateTime('13-10-2017');
$end_date = new DateTime('13-02-2018');
$end_date->setTime(0, 0, 1); // important, to consider the last day!
$date_interval = new DateInterval('P1M');
$date_period = new DatePeriod($start_date, $date_interval, $end_date);
# calculating number of days in the interval
$interval = $start_date->diff( $end_date );
$days = $interval->days;
# getting names of the months in the interval
$dates = [];
foreach ($date_period as $date) {
$dateArr = [];
if ($date->format("Y-m") === $start_date->format("Y-m")) {
$dateArr["start"] = $start_date->format("d-m-Y");
}
else {
$dateArr["start"] = $date->format("01-m-Y");
}
if ($date->format("Y-m") === $end_date->format("Y-m")) {
$dateArr["end"] = $end_date->format("d-m-Y");
}
else {
$dateArr["end"] = $date->format("t-m-Y"); // last day of the month
}
$dates[] = $dateArr;
}
foreach ($dates as $date) {
echo $date["start"]." to ".$date["end"].PHP_EOL;
}
Demo
You can employ DateTime::modify function. E.g.:
$month_intervals = [];
foreach ($date_period as $date) {
$start = $date == $start_date ? $start_date : $date->modify('first day of this month');
$month_intervals[] = join([
$start->format('d-m-Y'),
$date->modify('last day of this month')->format('d-m-Y')
], ' to ');
}
$month_intervals[] = join([
(clone $end_date)->modify('first day of this month')->format('d-m-Y'),
$end_date->format('d-m-Y')
], ' to ');
echo implode(',', $month_intervals);
I would like to get the first date in the future from an array of dates. I've tried to write my own function, which did not get the job done.
private static function getClosestDate($date, array $dates, $last) {
$interval = array();
$now = strtotime(date('Y-m-d'));
foreach ($dates as $d) {
$dateTime = strtotime($date);
$toTime = strtotime($d);
// Do not parse dates older than today
if (strtotime($d) < $now) { continue 1; }
// Only do dates in the future
if ($toTime < $dateTime) { continue 1; }
$interval[] = abs($dateTime - $toTime);
}
// If there is no interval, use the latest date
if (!count($interval)) {
return $last;
}
asort($interval);
$closest = key($interval);
return $dates[$closest];
}
This code works but it also works the other way around. Next to that, when there is no last date, it should use the $last parameter which is the last date.
The reason I want to know this is because I have a range of dates on which people can book nights on. I want to know the difference in days to calculate the amount of nights they can book. Calculating a difference in dates is easy, but I do need to know when the next booking appears. These are provided in the $dates parameter.
What should I change in my code to get it fixed (I've tried to continue the foreach whenever the date was in the past based on the $date parameter) or can someone provide me the code?
I've fixed the problem myself. Instead of using the dates array I'm now using the date as key in the interval to retrieve the date again. This does work properly.
private static function getClosestDate($date, array $dates, $last) {
$interval = array();
$now = strtotime(date('Y-m-d'));
foreach ($dates as $d) {
$dateTime = strtotime($date);
$toTime = strtotime($d);
// Do not parse dates older than today
if (strtotime($d) < $now) { continue 1; }
// Only do dates in the future
if ($toTime < $dateTime) { continue 1; }
$interval[$d] = abs($dateTime - $toTime);
}
// If there is no interval, use the latest date
if (!count($interval)) {
return $last;
}
asort($interval);
$closest = key($interval);
return $closest;
}
I'm trying to calculate the number of weekend days between dates from the array below:
$dates[] = array ( 'DateFrom' => '2015-07-10', 'DateTo' => '2015-07-10', 'DateFrom' => '2015-07-12', 'DateTo' => '2015-07-12', 'DateFrom'=> '2015-07-17', 'DateTo'=> '2015-07-19') ;
The result must return number of weekend days between these dates
Between these dates are 3 days of weekend (2015-07-12, 2015-07-18, and 2015-07-19).
Anyone have any idea?
You need to loop through from start date to end date and in each iteration need to check for day (sat/sun)
Algo :
$weekends = 0;
$startDate = strtotime($startDate);
$endDate = strtotime($endDate);
while($startDate<$endDate) {
//"N" gives ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0)
$day = date("N",$startDate);
if($day == 6 || $day == 7) {
$weekends++;
}
$startDate = 24*60*60 ; //1 day
}
Firstly, if you're defining your array exactly as written, you're duplicating keys and four of those items will be overwritten. But assuming we're just looking at the pairs. Pass the FromDate and ToDate from each pair to this function and add up all the return values.
function getWeekends ($fromDate, $toDate) {
$from = strtotime($fromDate);
$to = strtotime($toDate);
$diff = floor(abs($to-$from)/(60*60*24)); // total days betwixt
$num = floor($diff/7) * 2; // number of weeks * 2
$fromNum = date("N", $from);
$toNum = date("N", $to);
if ($toNum < $fromNum)
$toNum += 7;
// get range of day numbers
$dayarr = range($fromNum, $toNum);
// check if there are any weekdays in that range
$num += count(array_intersect($dayarr, array(6, 7, 13)));
return $num;
}
There may be a more elegant solution.
To be used on each pair of dates:
function getWeekendDays($startDate, $endDate)
{
$weekendDays = array(6, 7);
$period = new DatePeriod(
new DateTime($startDate),
new DateInterval('P1D'),
new DateTime($endDate)
);
$weekendDaysCount = 0;
foreach ($period as $day) {
if (in_array($day->format('N'), $weekendDays)) {
$weekendDaysCount++;
}
}
return $weekendDaysCount;
}