I have a DatePeriod
$start = new DateTime('2016-03-01');
$end = new DateTime('2016-03-01 + 1 month');
$period = new DatePeriod($start, new DateInterval('P1D'), $end);
and a SQL Table with workdates:
Now i need a SQL Query to Count every day in the table between the Dateperiod by tagnr (daynumber)
The following code doesn't work for me.
SELECT
COUNT(tagnr) AS totalsumme
FROM
arbeitszeiten
WHERE
tagnr IN ($period)
If you are trying to count the number of dates in the table that are contained in the php date period specified, then the only insurmountable problem I see is that your posted data doesn't contain actual dates. In particular tagnr appears to be the number day of the week (Mon-Sun : 1-7).
Let's presume for a minute your table data does contain actual dates as such:
id tag tagnr ddate
1 Montag 1 2016-03-07
2 Dienstag 2 2016-03-08
3 Mittwoche 3 2016-03-09
4 Donnerstag 4 2016-03-10
5 Freitag 5 2016-03-11
Then the task isn't too difficult to build a sql query using the dates in the range:
<?php
$start = new DateTime('2016-03-01');
$end = new DateTime('2016-03-01 + 1 month');
$period = new DatePeriod($start, new DateInterval('P1D'), $end);
$date_range = "";
// iterate through date range and build up sql string
foreach($period as $date){
// append each date, comma delimited
$date_range .= "'" . $date->format("Y-m-d") . "',";
}
// trim trailing comma
$date_range = rtrim($date_range,",");
$sql = "
SELECT
COUNT(tagnr) AS totalsumme
FROM
arbeitszeiten
WHERE
ddate IN ($date_range)
";
// execute $sql with mysqli, PDO, etc.
Curiously, since your table only shows exactly 5 typical working days, then I wonder if your intention is to actually count how many working days (from the table) exist in the php date interval you've specified. In which case, you can use the php date format after translating from German. Moreover, unless your work days of the week are changing, then it doesn't make sense to hit a database for that info, so you should just hard code it in an array. If they are changing, then you will also need the actual dates for context per my other answer. But, to find the number of working days in the specified php date range, the following code will do this:
<?php
$working_days = array(
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday"
);
$start = new DateTime('2016-03-01');
$end = new DateTime('2016-03-01 + 1 month');
$period = new DatePeriod($start, new DateInterval('P1D'), $end);
$count_of_working_days = 0;
// iterate through date range and count each instance
foreach($period as $date){
// get the day of the week from the date
$day_of_week = $date->format('l');
// check if it's one of the working days
if (in_array($day_of_week,$working_days))
$count_of_working_days++;
}
echo $count_of_working_days;
Output:
23
Related
i would like to calculate the number of free days (absences) in a specific week. I use an API that returns the following data:
{
"count": 1,
"data": [
{
"id": "11ec62ff1df2654d8bd6f1d234a6c496",
"type": "HOLIDAY",
"from": "2021-12-22",
"to": "2021-12-23",
"resourceId": "11ec46d6547a00728be3e1ed8ff29535",
"createdAt": "2021-12-22T08:14:00"
}
],
"success": true
}
These are vacation and sickness data. I have a weekly report where I need to calculate the number of absence days during that week. I need to find an easy way to calculate the number of absence days during the week.
I have tried by using https://www.php.net/manual/de/datetime.format.php and convert it into "z" format, but it doesn't look elegant and from performance perspective i think it's not best.
//The week range
$weekStart = new DateTime("2021-12-20");
$weekEnd = new DateTime("2021-12-24");
//The Planned absence
$absenceStart = new DateTime("2021-12-22");
$absenceEnd = new DateTime("2021-12-23");
//Specify the DateInterval for calculating the period
$interval = DateInterval::createFromDateString('1 day');
//Need to add the interval to the end date in order to consider the end as well
$weekEnd->add($interval);
$absenceEnd->add($interval);
//Getting the 2 periods week and absence
$weekPeriod = new DatePeriod($weekStart, $interval, $weekEnd);
$absencePeriod = new DatePeriod($absenceStart, $interval, $absenceEnd);
$weekArray = array();
$absenceArray = array();
//put the day number format('z') into an array of the week
foreach ($weekPeriod as $i => $dt) {
$weekArray[$i] = $dt->format('z');
}
//put the day number format('z') into an array of the absence
foreach ($absencePeriod as $i => $dt) {
$absenceArray[$i] = $dt->format('z');
}
//get the intersection between both arrays
$ergebnis = array_intersect($weekArray, $absenceArray);
//calculate the number of entries
echo "The employee has <b>".count($ergebnis)."</b> free days in the week from 2021-12-20 until 2021-12-24";
This is returning the right information.
The employee has 2 free days in the week from 2021-12-20 until 2021-12-24
Can anyone please suggest if there is a better way or if i can tweak at least to make it more elegant and performat?
Thanks alot
Instead of looping over the week array, absence array, then week array again (array_intersect), you could eliminate looping through the absence array and the second week array loop if that information is not needed. So you only have 1 loop instead of 3 should be more performant.
//The week range
$weekStart = new DateTime("2021-12-20 00:00:00");
$weekEnd = new DateTime("2021-12-24 23:59:59");
//The Planned absence
$absenceStart = new DateTime("2021-12-22 00:00:00");
$absenceEnd = new DateTime("2021-12-23 23:59:59");
//Specify the DateInterval for calculating the period
$interval = DateInterval::createFromDateString('1 day');
//Getting the available period
$weekPeriod = new DatePeriod($weekStart, $interval, $weekEnd);
$availableDayCount = 0;
//put the day number format('z') into an array of the week
foreach ($weekPeriod as $dt) {
// Filter out days the employee is absent.
if($dt < $absenceStart || $dt > $absenceEnd) {
$availableDayCount += 1;
}
}
//calculate the number of entries
echo "The employee has <b>" . $availableDayCount . "</b> free days in the week from " . $weekStart->format('Y-m-d') . " until " . $weekEnd->format('Y-m-d');
Additionally, it didn't appear to be providing the correct information (maybe I am wrong?). Your question states it came to 2 days but I count 3:
2021-12-20
2021-12-21
2021-12-24
(The 22nd and 23rd being absent)
If that is incorrect, you can change $weekEnd to 2021-12-23 (I added start/end of day times so the entire day is counted).
I'm trying to create a range of dates between 2 dates, but the max amount of steps is 10. I tried using the range() function but that only works for alphabetical and numeric steps as far as I figured out.
So for example I have a date 03/07/2018 and a date 23/04/2015, I'd like to get 10 steps in between from the start till the end.
Simple example would be 01/01/2018 till 01/12/2018 you'd get start and end steps + the 10 steps added (01/02/2018, 01/03/2018) etc. And ofcourse if you have less then 10 steps (days) in between, create less steps.
What I have now is:
$begin = new DateTime( '2012-08-01' );
$end = new DateTime( '2012-08-31' );
$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);
foreach($daterange as $date){
echo $date->format("Y/m/d") . "<br>";
}
But instead of having the P1D interval for DateInterval(), I want it to be 10 steps.
Please help me out, thanks!
Get the amout of days inbetween the start and end date like here. Then divide the amount by n. Then loop i=1 to n and add the result to the start-date every time you run through the loop (Datetime::modify). Cache the results in an array. Done.
You should try following code to find 10 intervals between given dates:
$begin = new DateTime('2012-08-01');
$end = new DateTime('2012-08-31');
$x = $end->diff($begin);
$intervalDays = ceil($x->days / 10);
$interval = new DateInterval('P' . $intervalDays . 'D');
$daterange = new DatePeriod($begin, $interval, $end);
foreach ($daterange as $date) {
echo $date->format("Y/m/d") . "<br>";
}
Your code is almost near to the solution and a little effort make it as you required.
I'm working on a website where the user can create some events every X days (where X is the name of a day in the week). Then, he needs to enter the number of events he wants to create in the future.
For example, the user selects every Monday and Tuesday and decides to create 150 events.
Here is the code I have made until now :
// Init the date counter
$cpt_date_found = 0;
// Number of date to find
$rec_occ = 150;
// Init an ending date far in the future
$endDate = strtotime('+10 years', time());
// Loop over the weeks
for($i = strtotime('Monday', strtotime(date("d.m.Y"))); $i <= $endDate; $i = strtotime('+1 week', $i)) {
// -- Monday date found, create the event in the database
$cpt_date_found++;
// Break the loop if we have enough dates found
if($cpt_date_found == $rec_occ) {
break;
}
}
This code finds the date of every Monday in the future and breaks the loop once we have reached the number of occurrences the user specified.
I have entered an ending date far in the future to make sure I can break the loop before the end of the occurrences count specified by the user.
First I'm not sure about the "quality" of my code... I know that breaking the loop is not the best idea and am wondering if another solution would better fit my needs.
Then, instead of repeating the loop more times if the user specified several days (let's say, Monday, Tuesday and Friday), is there a way to loop one time for every provided days?
Thanks!
The following code will loop over a period of 5 years. For each week in those 5 years it will generate a DatePeriod containing each day of that week. It will compare each of those days to your preset array with days you are looking for. You can then generate your event after which the code will countdown for a certain amount of times. If the counter hits zero, you are done.
$searchDates = array('Mon', 'Tue', 'Fri');
$amountOfTimes = 27;
$startDate = new DateTime();
$endDate = new DateTime('next monday');
$endDate->modify('+5 years');
$interval = new DateInterval('P1W');
$dateRange = new DatePeriod($startDate, $interval ,$endDate);
// Loop through the weeks
foreach ($dateRange as $weekStart) {
$weekEnd = clone $weekStart;
$weekEnd->modify('+6 days');
$subInterval = new DateInterval('P1D');
// Generate a DatePeriod for the current week
$subRange = new DatePeriod($weekStart, $subInterval ,$weekEnd);
foreach ($subRange as $weekday) {
if (in_array($weekday, array('Mon', 'Fri', 'Sun'))) {
// Create event
// Countdown
$amountOfTimes--;
}
if ($amountOfTimes == 0) {
break;
}
}
}
I run an automated process via PHP cron job every day which generates a report for a number of months. The date generation section of the code is below with a bit of hard coding for simplicity.
On the last day of the month it appears to repeat the months; i.e., rather than a series of from-to date pairs that make sense, I get the same date pairs repeated. All I can think of is that I'm doing something in the start and end count calculations that's only an issue on the last day of a month.
Results expected are:
From 2013-10-01 to 2013-10-31
From 2013-11-01 to 2013-11-30
...
From 2016-09-01 to 2016-09-30
The results I get when running the report on the last day of the month are a bit random. Here are the dates produced on 31/05 (note that only the TO date is displayed for simplicity. I know the FROM date is the first of the relevant month because the report data is correct).
31/10/2013
31/10/2013
31/12/2013
31/12/2013
31/01/2014
31/03/2014
31/03/2014
31/05/2014
31/05/2014
31/07/2014
31/07/2014
31/08/2014
31/10/2014
31/10/2014
31/12/2014
31/12/2014
31/01/2015
31/03/2015
31/03/2015
31/05/2015
31/05/2015
31/07/2015
31/07/2015
31/08/2015
31/10/2015
31/10/2015
31/12/2015
31/12/2015
31/01/2016
31/03/2016
31/03/2016
31/05/2016
31/05/2016
31/07/2016
31/07/2016
31/08/2016
31/10/2016
Here's the code:
<?php
$reportBegin = new DateTime("2013-10-01"); // start of report
$reportEnd = new DateTime("2016-09-01"); // end of report
$nowRef = new DateTime();
$startCount = (($nowRef->diff($reportBegin)->m) + ($nowRef->diff($reportBegin)->y*12)) * -1; // int * -1 to make neg
$endCount = (($nowRef->diff($reportEnd)->m) + ($nowRef->diff($reportEnd) ->y*12)) + 1; // int and add 1
$count = $startCount;
// do all months
while($count <= $endCount){
$from = date('Y-m-1', strtotime("$count months"));
$to = date('Y-m-t', strtotime($from));
print ("From $from to $to<br />");
$count++;
} // done all months
?>
Can anyone give me a steer? I'm not really sure how to test it.
Edit: The reason I'm mixing DateTime() and date() is because in production, there's a section of code that in some environments replaces the DateTime() section. This replacement code sets $startCount and $endCount as integers.
Here's a PHP5.3 version of the answer in my comment:
<?php
$reportBegin = new DateTime("2013-10-01"); // start of report
$reportEnd = new DateTime("2016-09-01");
$reportEnd->add(new DateInterval('P1M')); // end of report
$interval = new DateInterval('P1M');
$period = new DatePeriod($reportBegin, $interval, $reportEnd);
foreach ($period as $month) {
printf("From %s to %s<br />",
$month->format('Y-m-01'),
$month->format('Y-m-t')
);
}
?>
Demo
PHP 5.4+
<?php
$reportBegin = new DateTime("2013-10-01"); // start of report
$reportEnd = (new DateTime("2016-09-01"))->add(new DateInterval('P1M')); // end of report
$interval = new DateInterval('P1M');
$period = new DatePeriod($reportBegin, $interval, $reportEnd);
foreach ($period as $month) {
printf("From %s to %s<br />",
$month->format('Y-m-01'),
$month->format('Y-m-t')
);
}
?>
Demo
I am trying to get the difference between 2 dates, and get the last day of each month to insert data do the database. So if there is 3 months on this data range, it will insert 3 row to the database (one for each month).
What I have now is:
$jj = '2007-12-31';
$kk = '2009-12-31';
$begin = new DateTime($jj);
$end = new DateTime($kk);
$interval = DateInterval::createFromDateString('last thursday of next month');
$period = new DatePeriod($begin, $interval, $end, DatePeriod::EXCLUDE_START_DATE);
foreach ( $period as $dt ) {
echo $dt->format( "Y-m-t\n" );
}
This will output the last day of each month of this date range, but now I need to add a new row on a mysql table for each month.
If it helps to understand better my question, this is to save monthly payments, but there will be payments for 3 months, 6 months, 1 year, .... and all the payments will be stored in a monthly basis.
Thank you in advance!
If I understand correctly ... would you not do something like:
$insertSQL = "INSERT INTO <table> (%d, '%s', ...);";
foreach ( $period as $dt ) {
$payDate = $dt->format( "Y-m-t\n" );
mysql_query(sprintf($insertSQL, $custID, $payDate, ...);
}