Loop Year and Months - php

How can I loop the year and months with some given dates?
Below is my current code and I can't get it running
$Startdate = '2017-01';
$Enddate = '2018-06';
for($selectedDate = date("Y-m",$begin); $selectedDate <= date("Y-m",$end); $selectedDate++){
$resultY = date("Y",strtotime($selectedDate));
$resultM = date("m",strtotime($selectedDate));
echo $resultY;
$echo resulthM;
}
The output should be:
2017 1
2017 2
2017 3
2017 4
2017 5
2017 6
2017 7
2017 8
2017 9
2017 10
2017 11
2017 12
2018 1
2018 2
2018 3
2018 4
2018 5
2018 6

If I were you :) I will try with DateTime class to generate months between your $Startdate and $Enddate. See DateTime
<?php
$start = new DateTime('2017-01');
$end = new DateTime('2018-06');
$interval = DateInterval::createFromDateString('1 month');
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $dt) {
echo $dt->format("Y-m") . PHP_EOL;
}
DEMO: https://3v4l.org/FvmS4

Since you were familiar with strtotime, you can amend your code to the following to execute the desired results.
$begin = date("Y-m", strtotime("2017-01")); // Replace this with date to begin.
$end = date("Y-m"); // Replace this with date to end.
for($selectedDate = date("Y-m", strtotime($begin)); $selectedDate <= date("Y-m", strtotime($end)); $selectedDate = date("Y-m", strtotime($selectedDate . "+1 Month"))){
$resultY = date("Y", strtotime($selectedDate));
$resultM = date("m", strtotime($selectedDate));
echo $resultY;
echo $resultM;
}
However, equally, user don't angry me, has an answer that will allow you to use the DateTime objects and manipulate them instead, so if you'd rather switch over your code to something potentially more reliable, you could do that also.

$startDate = new \DateTime('2017-01-01');
$endDate = new \DateTime('2018-06-01');
for($selectedDate = $startDate; $selectedDate <= $endDate; $selectedDate->modify('+1 month')) {
// format $selectedDate;
$resultY = $selectedDate->format('Y');
$resultM = $selectedDate->format('m');
// print content
echo $resultY;
echo "\t"; // print tab
echo $resultM;
echo "\n"; // print new line
}

You can use DateTime::modify and walk the months forward
$Startdate = '2017-01';
$Enddate = '2018-06';
$DateTime = new DateTime($Startdate.'-01');
echo $DateTime->format('Y')."\t".$DateTime->format('n')."\n";
do{
$DateTime->modify('+1 months');
echo $DateTime->format('Y')."\t".$DateTime->format('n')."\n";
if($DateTime->format('Y-m') == $Enddate) break;
}while(true);
Just make sure the Enddate is a valid Year/Month and happens after Startdate or in this example you'll loop forever.
There are probably other ways to do the loop that avoids that.
But I didn't see DateTime::modify used in an answer so I thought I would throw one together.
Output:
2017 1
2017 2
2017 3
2017 4
2017 5
2017 6
2017 7
2017 8
2017 9
2017 10
2017 11
2017 12
2018 1
2018 2
2018 3
2018 4
2018 5
2018 6
Sandbox
I usually use this method when I want to make a select/option for stuff like months. But I use this format str_pad($DateTime->format('n'),2,' ',STR_PAD_LEFT).' - '.$DateTime->format('F') OR ' 2 - February', notice the space on the left ... :) ... that way they all line up nice and neat like.
Anyway, cheers!

Related

Removing months in a year [duplicate]

This question already has answers here:
Get the last 12 months in PHP
(5 answers)
Closed 3 years ago.
I'm trying to use a loop to show the last 12 months, but March appears twice.
for ($i=0; $i < 12; $i++) {
$month = date("d/m/Y", strtotime("now -$i month"));
echo "$month<br>";
}
Output:
30/01/2020
30/12/2019
30/11/2019
30/10/2019
30/09/2019
30/08/2019
30/07/2019
30/06/2019
30/05/2019
30/04/2019
30/03/2019
02/03/2019
How can I solve this?
Use the first day of the month as the basis in your script.
"first day of this month -$i month"
Use DateTime and keep track of month/year combinations that you already had:
$dt = new DateTime();
$previous = [];
for ($i=0; $i < 12; $i++) {
$month = $dt->format("d/m/Y");
echo "$month<br>".PHP_EOL;
$previous[$dt->format('Y-m')] = true;
$dt->modify('-1 month');
while (array_key_exists($dt->format('Y-m'), $previous)) {
$dt->modify('-1 day');
}
}
If this encounters a previously encountered month-year combination, it starts substracting days until it reaches the previous month.
Will produce this output:
30/01/2020
30/12/2019
30/11/2019
30/10/2019
30/09/2019
30/08/2019
30/07/2019
30/06/2019
30/05/2019
30/04/2019
30/03/2019
28/02/2019
You probably want to use DatePeriod for this task and not date. It's much simpler and more sane.
$start = new DateTime;
$start->setDate($start->format('Y'), $start->format('n'), 1); // Normalize the day to 1
$start->sub(new DateInterval('P12M'));
$interval = new DateInterval('P1M');
$recurrences = 12;
foreach (new DatePeriod($start, $interval, $recurrences, true) as $date) {
echo $date->format('F, Y'), "\n"; // attempting to make it more clear to read here
}
Output:
February, 2019
March, 2019
April, 2019
May, 2019
June, 2019
July, 2019
August, 2019
September, 2019
October, 2019
November, 2019
December, 2019
January, 2020

How to loop through months that have been already passed

I have the following to loop through each month of the year. However, it seems to skip February.
$start = new DateTime('2015-01-01');
$start->modify('last day of this month');
$current = new DateTime('now');
$end = new DateTime('2018-01-01');
$interval = DateInterval::createFromDateString('1 month');
$period = new DatePeriod($start, $interval, $end);
$timestamps = array();
foreach ($period as $dt) {
$dt->modify('last day of this month');
echo 'C:' . $current->format('d F Y') . '<br>';
echo 'S:' . $start->format('d F Y') . '<br>';
echo 'D:' . $dt->format('d F Y') . '<br>';
echo '<br><br>';
}
However, the above outputs:
C:17 March 2015
S:31 January 2015
D:31 January 2015
C: 17 March 2015
S:31 January 2015
D:31 March 2015
C: 17 March 2015
S:31 January 2015
D:30 April 2015
Can anyone spot my mistake? I expected the second D to have a value of the 28 February 2015.
I just want a list of months that have already been passed.
Update
The problem highlighted by MLeFevre in the comments is that working with date intervals can be tricky. See Example #3 Beware when adding months http://php.net/manual/en/datetime.add.php.
Rather than use a DatePeriod, why not just use the modify method slightly differently like this:
$current = new DateTime('now');
$end = new DateTime('2018-01-01');
while($current < $end) {
$current->modify('last day of next month');
echo 'C:' . $current->format('d F Y') . '<br>';
}
In your question, you're firstly adding a month, then going to the end of that month. This doesn't work, as the length of each month varies.
Sample output:
C:30 April 2015
C:31 May 2015
C:30 June 2015
C:31 July 2015
C:31 August 2015
C:30 September 2015
C:31 October 2015
C:30 November 2015
C:31 December 2015
C:31 January 2016
C:29 February 2016
C:31 March 2016
// etc.
To loop from $start to $current, you could change the logic slightly like this:
$start = new DateTime('2015-01-31'); // start from end of month
$current = new DateTime('now');
do {
echo 'C:' . $start->format('d F Y') . '<br>';
} while($start->modify('last day of next month') < $current);
Output:
C:31 January 2015
C:28 February 2015
It happen because February has 28 days and your interval is 1 month (30 days). So it skips 30 days from 30 January to 2 March. Then it move to last day of March.
Change
$start->modify('last day of this month');
to
$start->modify('first day of this month');
Your first date is 31-Jan-2015. Since February has no 31st, it's going to March 3rd. Then you are telling it to go to the end of that month which is why you are getting the end of March after January and not February.

How to list the last N months with PHP DateTime

I want to populate a select input with the last 5 months (including the current month). So on 09/04/2014 the options should look something like this:
April 2014
March 2014
February 2014
January 2014
December 2013
My first solution with PHP DateTime seemed to work:
for ($i = 0; $i < 5; $i++) {
$date = new DateTime($i.' months ago');
echo $date->format('F Y'); //populate select input
}
But in fact it doesn't handle edge cases. For example, on 31/03/2014 it produces:
March 2014
March 2014
January 2014
December 2013
December 2013
What is the correct way to list the last 10 months with PHP DateTime?
$date = new DateTime("2014/03/31");
for ($i = 0; $i <= 9; $i++) {
$date->modify("-1 month");
echo $date->format('F Y'); //populate select input
}
You are getting same 2 months, because when you are subtracting 1 month from date, that has more days than previous month. Example: when you subtract 1 month from 31.3. you will get 3.3. (same month), and not 28.2. as you might expect...
My suggestion is to get first day of current month, and then do your logic:
$dt = new DateTime('first day of this month');
for ($i = 1; $i <= 10; $i++) {
echo $dt->format('F Y'), "\n";
$dt->modify('-1 month');
}
demo

How to get current date in next month [duplicate]

This question already has answers here:
PHP: Populating an array with the names of the next 12 months
(8 answers)
Closed 8 years ago.
I've done some research over internet and i cannot find the right answer that closes to my problem. I'm just beginner in php and I don't understand advance programming yet. My problem is I want to get the current date in next month.
suppose:
today is Feb 15 2014,
I want to get date
March 15 2014,
April 15 2014,
May 15 2014,
June 15 2014,
July 15 2014,
August 15 2014,
September 15 2014,
October 15 2014,
November 15 2014,
December 15 2014,
January 15 2015, and so on and so fourth.
$new_date = date('F d Y', strtotime('+1 month'));
or
for ($i = 1; $i <12; $i++){
$new_date = date('F d Y', strtotime("+$i month"));
echo $new_date;
}
Here is another solution:
$begin = new DateTime( '2014-03-15' );
$end = new DateTime( '2015-01-31' );
$interval = new DateInterval('P1M');
$daterange = new DatePeriod($begin, $interval ,$end);
foreach($daterange as $date){
echo $date->format("F d Y") . "<br>";
}
or this if you want to do it without a end date:
$date= new DateTime( '2014-03-15' );
for($i = 1; $i < 10; $i++) {
$date = $date->modify( '+1 month' );
echo $date->format("F d Y") . "<br>";
}

Find weekly periods (starting on a Monday) for a month

I'm trying to find the weekly periods for a given month and year. Dates should start on a Monday and end on a Sunday. If the 1st of the month is a Sunday (Ex May 2011), it should be the first element.
May 2011
May 1 (Sunday)
May 2 - May 8 (Monday - Sunday)
May 9 - May 15 (Monday - Sunday)
May 17 - Ma6y 22 (Monday - Sunday)
May 23 - May 29 (Monday - Sunday)
May 30 - May 31 (Monday - Tuesday)
September 2012
September 1 - September 2
September 3 - September 9
September 10 - September 16
September 17 - September 23
September 24 - September 30
I am using this function to calculate the week numbers for two dates - I.e. the 1st day of the month and last day of the month.
public function getWeekNumbers($startDate, $endDate)
{
$p = new DatePeriod(
new DateTime($startDate),
new DateInterval('P1W'),
new DateTime($endDate)
);
$weekNumberList = array();
foreach ($p as $w)
{
$weekNumber = $w->format('W');
$weekNumberList[] = ltrim($weekNumber, '0');
}
return $weekNumberList;
}
Strangely, for the month of January, it returns week numbers of [52, 1, 2, 3, 4] when I'm expecting [1, 2, 3, 4, 5].
Once I have the week numbers, I'm using them like so:
//The following loop will populate the dataset with the entire month's durations - regardless if hours were worked or not.
$firstDayOfMonth = date('Y-m-d', strtotime("first day of {$this->year}-{$monthName}"));
$lastDayOfMonth = date('Y-m-d', strtotime("last day of {$this->year}-{$monthName}"));
foreach ($this->getWeekNumbers($firstDayOfMonth, $lastDayOfMonth) as $key => $weekId)
{
// find first mоnday of the year
$firstMon = strtotime("mon jan {$this->year}");
// calculate how many weeks to add
$weeksOffset = $weekId - date('W', $firstMon);
$beginDays = $weeksOffset * 7;
$endDays = ($weeksOffset * 7) + 6;
$searchedMon = strtotime(date('Y-m-d', $firstMon) . " +{$beginDays} days");
$searchedSun = strtotime(date('Y-m-d', $firstMon) . " +{$endDays} days");
echo date("M d", $searchedMon) . " - " . date("M d", $searchedSun);
}
Since, the getWeekNumbers function isn't returning the week numbers I'm expecting, it's not surprising that the output of the above function is
Dec 24 - Dec 30 (2012)
Jan 02 - Jan 08 (2012)
Jan 09 - Jan 15 (2012)
Jan 16 - Jan 22 (2012)
Jan 23 - Jan 29 (2012)
Note that the 1st line (Dec 24 - Dec 30) is the end of the current year (2012) and not the end of last year (2011).
Ideally, I want it to look like
Any ideas? Thanks!!
If you need all weeks for selected month, and all dates for selected week, then this is all you need:
function getWeekDays($month, $year)
{
$p = new DatePeriod(
DateTime::createFromFormat('!Y-n-d', "$year-$month-01"),
new DateInterval('P1D'),
DateTime::createFromFormat('!Y-n-d', "$year-$month-01")->add(new DateInterval('P1M'))
);
$datesByWeek = array();
foreach ($p as $d) {
$dateByWeek[ $d->format('W') ][] = $d;
}
return $dateByWeek;
}
getWeekDays() function returns multi dimension array. first key is week number. 2 level is array, that has dates saved as DateTime object.
Fetch example:
print_r( getWeekDays(5, 2011) ); # May 2011
print_r( getWeekDays(9, 2012) ); # Sep 2012
I had a little time extra, so I written an example ;-)
$datesByWeek = getWeekDays(8, 2012);
$o = '<table border="1">';
$o.= '<tr><th>Week</th><th>Monday</th><th>Tuesday</th><th>Wednesday</th><th>Thursday</th><th>Friday</th><th>Saturday</th><th>Sunday</th></tr>';
foreach ($datesByWeek as $week => $dates) {
$firstD = $dates[0];
$lastD = $dates[count($dates)-1];
$o.= "<tr>";
$o.= "<td>" . $firstD->format('M d') . ' - ' . $lastD->format('M d') . "</td>";
$N = $firstD->format('N');
for ($i = 1; $i < $N; $i++) {
$o.= "<td>-</td>";
}
foreach ($dates as $d) {
$o.= "<td>" . $d->format('d.') . " / 0.00</td>";
# for selected date do you magic
}
$N = $lastD->format('N');
for ($i = $N; $i < 7; $i++) {
$o.= "<td>-</td>";
}
$o.= "</tr>";
}
$o.= '</table>';
echo $o;
Output looks like:
The following assumes that the user can pick the month and year for which they wan to run the report (the value posted being 1-12 for month and YYYY for year). There may be a more elegant way to do it, but this seems to work for me. Also, at the top of your post, you say that you want the weeks to be Monday - Sunday. However, your example/screenshot at the bottom shows weeks being Sunday to Saturday. The below is for the originally-stated goal of Monday - Sunday.
$month = $_POST["month"];
$year = $_POST["year"];
$endDate = date("t", strtotime($year."-".$month."-01"));
$dayOfWeekOfFirstOfMonth = date("w", strtotime($year."-".$month."-01"));
$lastDayOfFirstWeek = 8 - $dayOfWeekOfFirstOfMonth;
$weeksArray = array(array("firstDay"=>1, "lastDay"=>$lastDayOfFirstWeek));
$loopDate = $lastDayOfFirstWeek + 1;
while($loopDate < $endDate)
{
$weeksArray[] = array("firstDay"=>$loopDate, "lastDay"=>($loopDate+6 > $endDate ? $endDate : $loopDate+6));
$loopDate+=7;
}
foreach($weeksArray as $week)
{
echo date("M d", strtotime($year."-".$month."-".$week["firstDay"])) . " - " . date("M d", strtotime($year."-".$month."-".$week["lastDay"])) . "\n";
}
this works perfect!!! phpfiddle here
<?php
// start and end must be timestamps !!!!
$start = 1346976000; // Thu 2012-09-06
$end = 1348704000; // Tue 2012-09-26
// generate the weeks
$weeks = generateweeks($start, $end);
// diaplay the weeks
echo 'From: '.fDate($start).'<br>';
foreach ($weeks as $week){
echo fDate($week['start']).' '.fDate($week['end']).'<br>';
}
echo 'To: '.fDate($end).'<br>';
/* outputs this:
From: Thu 2012-09-06
Thu 2012-09-06 Sun 2012-09-09
Mon 2012-09-10 Sun 2012-09-16
Mon 2012-09-17 Sun 2012-09-23
Mon 2012-09-24 Wed 2012-09-26
To: Wed 2012-09-26
*/
// $start and $end must be unix timestamps (any range)
// returns an array of arrays with 'start' and 'end' elements set
// for each week (or part of week) for the given interval
// return values are also in timestamps
function generateweeks($start,$end){
$ret = array();
$start = E2D($start);
$end = E2D($end);
$ns = nextSunday($start);
while(true){
if($ns>=$end) {insert($ret,$start,$end);return $ret;}
insert($ret,$start,$ns);
$start = $ns +1;
$ns+=7;
}
}
// helper function to append the array and convert back to unix timestamp
function insert(&$arr, $start, $end){$arr[] = array('start'=>D2E($start), 'end'=>D2E($end));}
// recives any date on CD format returns next Sunday on CD format
function nextSunday($Cdate){return $Cdate + 6 - $Cdate % 7;}
// recives any date on CD format returns previous Monday on CD format // finaly not used here
function prevMonday($Cdate){return $Cdate - $Cdate % 7;}
// recives timestamp returns CD
function E2D($what){return floor($what/86400)+2;} // floor may be optional in some circunstances
// recives CD returns timestamp
function D2E($what){return ($what-2)*86400;} // 24*60*60
// just format the timestamp for output, you can adapt it to your needs
function fDate($what){return date('D Y-m-d',$what);}

Categories