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
Related
How can I count occurrences of 14th of a month between two dates
For example between 07.05.2018 and 04.07.2018
I have 2 occurrences of the 14th
Try this. Note that I've changed your date format, but you can just do a createFromFormat if you're really keen on your own format.
$startDate = new DateTime('2018-05-07');
$endDate = new DateTime('2018-07-04');
$dateInterval = new DateInterval('P1D');
$datePeriod = new DatePeriod($startDate, $dateInterval, $endDate);
$fourteenths = [];
foreach ($datePeriod as $dt) {
if ($dt->format('d') == '14') { // Note this is loosely checked!
$fourteenths[] = $dt->format('Y-m-d');
}
}
echo count($fourteenths) . PHP_EOL;
var_dump($fourteenths);
See it in action here: https://3v4l.org/vPZZ0
EDIT
This is probably not an optimal solution as you loop through every day in the date period and check whether it's the fourteenth. Probably easier is to modify the start date up to the next 14th and then check with an interval of P1M.
You don't need to loop at all.
Here's a solution that does not loop at all and uses the less memory and performance hungry date opposed to DateTime.
$start = "2018-05-07";
$end = "2018-07-04";
$times = 0;
// Check if first and last month in the range has a 14th.
if(date("d", strtotime($start)) <= 14) $times++;
if(date("d", strtotime($end)) >= 14) $times++;
// Create an array with the months between start and end
$months = range(strtotime($start . "+1 month"), strtotime($end . "-1 month"), 86400*30);
// Add the count of the months
$times += count($months);
echo $times; // 2
https://3v4l.org/RevLg
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 have input as $fromweek=50 and $fromyear=2015
then $toweek=2 and $toyear=2016
How do I loop week number in a table column ?
for ($i=$fromweek; $i<=$toweek; $i++) {
echo '<th>week '.$i.'</th>';
}
You need to be careful here as according to the ISO 8601 standard some years can have 53 weeks. Your example year, 2015 is one of these.
The \DateTime classes are the way to do this. They will automatically account for leapyears etc and make date manipulation much easier. It is worth taking the time to read up on them.
Something like this should work for you:
$fromweek=50;
$fromyear=2015;
$toweek=2;
$toyear=2016;
$interval = new DateInterval('P7D');
$start = (new \DateTime())->setISODate($fromyear, $fromweek);
$end = (new \DateTime())->setISODate($toyear, $toweek);
$period = new \DatePeriod($start, $interval, $end->add($interval));
foreach($period as $date){
echo $date->format('d-m-Y') . PHP_EOL;
}
See it working.
You could create two dates using week number and year, and compare them (while $date1 exceeds $date2), and increase $date1 by week using DateInterval, see example below:
$date1 = new DateTime();
$date1->setISODate(2015, 50);
$date2 = new DateTime();
$date2->setISODate(2016, 2);
while($date1 <= $date2) {
echo $date1->format('W').PHP_EOL;
$date1->add(new DateInterval('P1W'));
}
In Action: https://eval.in/573305
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
Do you know what the problem is by looking at the code?
I would be happy if you helped me:
list($from_day,$from_month,$from_year) = explode(".","27.09.2012");
list($until_day,$until_month,$until_year) = explode(".","31.10.2012");
$iDateFrom = mktime(0,0,0,$from_month,$from_day,$from_year);
$iDateTo = mktime(0,0,0,$until_month,$until_day,$until_year);
while ($iDateFrom <= $iDateTo) {
print date('d.m.Y',$iDateFrom)."<br><br>";
$iDateFrom += 86400;
}
Date of writing the same problem 2 times
October (31) for writing 2 times in history draws the ends October 30th: (
27.09.2012
28.09.2012
...
26.10.2012
27.10.2012
[[28.10.2012]]
[[28.10.2012]]
29.10.2012
30.10.2012
Your problem is because you have set time to 00:00:00, set it to 12:00:00. That is because the Daylight saving time.
Stop using date() function, use Date and Time classes.
Solution (PHP >= 5.4):
$p = new DatePeriod(
new DateTime('2012-09-27'),
new DateInterval('P1D'),
(new DateTime('2012-10-31'))->modify('+1 day')
);
foreach ($p as $d) {
echo $d->format('d.m.Y') . "\n";
}
Solution (PHP < 5.4)
$end = new DateTime('2012-10-31');
$end->modify('+1 day');
$p = new DatePeriod(
new DateTime('2012-09-27'),
new DateInterval('P1D'),
$end
);
foreach ($p as $d) {
echo $d->format('d.m.Y') . "\n";
}
You have daylight savings time issues. Adding seconds from one timestamp to another is prone to problems around these sorts of edge conditions (leap days can be problematic is well), You should get in the habit of using PHP's DateTime and DateInterval objects. It makes working with dates a snap.
$start_date = new DateTime('2012-09-27');
$end_date = new DateTime('2012-10-31');
$current_date = clone $start_date;
$date_interval = new DateInterval('P1D');
while ($current_date < $end_date) {
// your logic here
$current_date->add($date_interval);
}
My idea for solving this would be something like this;
$firstDate = "27.09.2012";
$secondDate = "31.10.2012";
$daysDifference = (strtotime($secondDate) - strtotime($firstDate)) / (60 * 60 * 24);
$daysDifference = round($daysDifference);
for ($i = 0; $i <= $daysDifference; $i++)
{
echo date("d.m.Y", strtotime('+'.$i.' day', strtotime($firstDate))) . "<BR>";
}
This should solve your problem and be much easier to read (imho). I've just tested the code, and it outputs all dates and no doubles. It also saves you from all the daylight savings inconsistencies.
I don't know where you're from, but it's likely you're hitting daylight saving changeover in your timezone (it's Nov 4th where I live - exactly one week after Oct 28th). You can not rely on a day being exactly 86400 seconds long.
If you loop incrementing with mktime, you should be fine:
list($from_day,$from_month,$from_year) = explode(".","27.09.2012");
list($until_day,$until_month,$until_year) = explode(".","31.10.2012");
$iDateFrom = mktime(0,0,0,$from_month,$from_day,$from_year);
$iDateTo = mktime(0,0,0,$until_month,$until_day,$until_year);
while ($iDateFrom <= $iDateTo)
{
print date('d.m.Y',$iDateFrom)."<br><br>";
$from_day = $from_day + 1;
$iDateFrom = mktime(0,0,0,$from_month,$from_day,$from_year);
}
Even though $from_day will likely be going well over 31, mktime will make the math conversion for you. (ie 32 days in a 31 day month = day 1 of the next month)
EDIT: sorry, I had the incrementation in the wrong place.