PHP: DatePeriod with last day of month - php

I'd like to work with PHP DateInterval to iterate through months:
$from = new DateTime();
$from->setDate(2014, 1, 31);
$period = new DatePeriod($from, new DateInterval('P1M'), 12);
I'd expect it to returns 31 January, 28 February (as the DateInterval is 1 month), but it actually returns 31 January, 3 March, 3 of April... hence skipping February.
Is there any way to do this simply?
Thanks!
EDIT : as a refernece, here is a solution that seems to cover most use cases:
$date = new DateTime('2014-01-31');
$start = $date->format('n');
for ($i = 0; $i < 28; $i++) {
$current = clone $date;
$current->modify('+'.$i.' month');
if ($current->format('n') > ($start % 12) && $start !== 12) {
$current->modify('last day of last month');
}
$start++;
echo $current->format('Y-m-d').PHP_EOL;
}

You can use DateTime::modify():
$date = new DateTime('last day of january');
echo $date->format('Y-m-d').PHP_EOL;
for ($i = 1; $i < 12; $i++) {
$date->modify('last day of next month');
echo $date->format('Y-m-d').PHP_EOL;
}
EDIT: I think I didn't understand your question clearly. Here is a new version:
$date = new DateTime('2014-01-31');
for ($i = 0; $i < 12; $i++) {
$current = clone $date;
$current->modify('+'.$i.' month');
if ($current->format('n') > $i + 1) {
$current->modify('last day of last month');
}
echo $current->format('Y-m-d').PHP_EOL;
}

The issue is cause by the variance between the last day in each of the months within the range. ie. February ending on 28 instead of 31 and the addition of 1 month from the last day 2014-01-31 + 1 month = 2014-03-03 https://3v4l.org/Y42QJ
To resolve the issue with DatePeriod and simplify it a bit, adjust the initial date by resetting the specified date to the first day of the specified month, by using first day of this month.
Then during iteration, you can modify the date period date by using last day of this month to retrieve the bounds of the currently iterated month.
Example: https://3v4l.org/889mB
$from = new DateTime('2014-01-31');
$from->modify('first day of this month'); //2014-01-01
$period = new DatePeriod($from, new DateInterval('P1M'), 12);
foreach ($period as $date) {
echo $date->modify('last day of this month')->format('Y-m-d');
}
Result:
2014-01-31
2014-02-28
2014-03-31
2014-04-30
2014-05-31
2014-06-30
2014-07-31
2014-08-31
2014-09-30
2014-10-31
2014-11-30
2014-12-31
2015-01-31
Then to expand on this approach, in order to retrieve the desired day from the specified date, such as the 29th. You can extract the specified day and adjust the currently iterated month as needed when the day is out of bounds for that month.
Example: https://3v4l.org/SlEJc
$from = new DateTime('2014-01-29');
$day = $from->format('j');
$from->modify('first day of this month'); //2014-01-01
$period = new DatePeriod($from, new DateInterval('P1M'), 12);
foreach ($period as $date) {
$lastDay = clone $date;
$lastDay->modify('last day of this month');
$date->setDate($date->format('Y'), $date->format('n'), $day);
if ($date > $lastDay) {
$date = $lastDay;
}
echo $date->format('Y-m-d');
}
Result:
2014-01-29
2014-02-28 #notice the last day of february is used
2014-03-29
2014-04-29
2014-05-29
2014-06-29
2014-07-29
2014-08-29
2014-09-29
2014-10-29
2014-11-29
2014-12-29
2015-01-29

You may try like this:
$date = new DateTime();
$lastDayOfMonth = $date->modify(
sprintf('+%d days', $date->format('t') - $date->format('j'))
);

I would do it probably like this
$max = array (
31,28,31,30,31,30,31,31,30,31,30,31
); //days in month
$month = 1;
$year = 2014;
$day = 31;
$iterate = 12;
$dates = array();
for ($i = 0;$i < $iterate;$i++) {
$tmp_month = ($month + $i) % 12;
$tmp_year = $year + floor($month+$i)/12;
$tmp_day = min($day, $max[$tmp_month]);
$tmp = new DateTime();
$tmp->setDate($tmp_year, $tmp_month + 1, $tmp_day);
$dates[] = $tmp;
}
var_dump($dates);
This keeps to the same day each month if possible

Related

get the date of Monday of the week having the year and the week number of the year

How could I get the date of the day Monday of a week having the year and number of week in the year?
For example, if the year is 2020 and the number of the week in the year is 01, the expected result is 2019-12-30
Hope this is what you are looking for :)
<?php
$week_number = '06';
$year_number = '2020';
function getStartAndEndDate($week, $year) {
$date = new DateTime();
$date->setISODate($year, $week);
for ($i=0; $i < 7; $i++) {
$array[$i] = $date->format('Y-m-d');
$date->modify('+1 days');
}
return $array;
}
$week_array = getStartAndEndDate($week_number,$year_number);
echo $week_array[0]; // 0 = monday, 1 = tuesday, and so on :)
?>
<?php
$week = 1;
$year = 2020;
$dateTime = date_create(sprintf("%04dW%02d",$year,$week)); //always a monday
echo $dateTime->format('Y-m-d'); //2019-12-30

PHP How to divide weeks into days

How to enlist all the days between for instance 2018-06-04 and 2018-06-10 that between 2018-06-04 - 2018-06-10 those days will be 2018-06-05, 2018-06-06, 2018-06-07, 2018-06-08, 2018-06-09, the same goes for 2018-06-11 - 2018-06-17 and so on...
So far I'ive managed to divide month into week chunks (below), I want to further divide weeks into days...
2018-06-04 - 2018-06-10
2018-06-11 - 2018-06-17
2018-06-18 - 2018-06-24
2018-06-25 - 2018-07-01
http://zakodowac.pl/
This is my PHP code which produces week chunks above 2018-06-04 - 2018-06-10 and so on...:
function getMondays($y, $m) {
return new DatePeriod(
new DateTime("first monday of $y-$m"),
DateInterval::createFromDateString('next monday'),
new DateTime("last day of $y-$m")
);
}
function list_week_days($year, $month) {
foreach (getMondays($year, $month) as $monday) {
echo $monday->format(" Y-m-d\n");
echo '-';
$sunday = $monday->modify('next Sunday');
echo $sunday->format(" Y-m-d\n");
echo '<br>';
}
}
list_week_days(2018, 06);
could you try this:
$begin = strtotime('2018-06-04');
$end = strtotime('2018-06-10');
while($begin < $end){
$begin = $begin +84600;
echo date('Y-m-d', $begin) . ' ';
}
if correctly understood, then the following should yield the results you expect:
// set current date
$date = '04/30/2009';
// parse about any English textual datetime description into a Unix timestamp
$ts = strtotime($date);
// calculate the number of days since Monday
$dow = date('w', $ts);
$offset = $dow - 1;
if ($offset < 0) {
$offset = 6;
}
// calculate timestamp for the Monday
$ts = $ts - $offset*86400;
// loop from Monday till Sunday
for ($i = 0; $i < 7; $i++, $ts += 86400){
print date("m/d/Y l", $ts) . "\n";
}
if you want to be even more clever, you can use:
// set current date
$date = '04/30/2009';
// parse about any English textual datetime description into a Unix timestamp
$ts = strtotime($date);
// find the year (ISO-8601 year number) and the current week
$year = date('o', $ts);
$week = date('W', $ts);
// print week for the current date
for($i = 1; $i <= 7; $i++) {
// timestamp from ISO week date format
$ts = strtotime($year.'W'.$week.$i);
print date("m/d/Y l", $ts) . "\n";
}
All of which, alongside more information, can be found on this website and credit goes to the author of that post.

PHP: Loop through all weeks in a date range (last 2 years)?

I like to iterate through all weeks in a date range that spawns 2 years. Starting at the current week number two years ago, until the current week number this year.
The problem is that a year can either have 52 weeks or 53 weeks, for example:
2015 had 53 weeks (the 53th was from 2015-12-28 till 2016-01-03)
2016 had 52 weeks (the 52th was from 2016-12-26 till 2017-01-01)
So this is currently my php code:
# start with the current week 2 years ago
$year = date("Y") - 2;
$week = (int) date("W"); // (int) removes leading zero for weeks < 10
$endYear = date("Y");
$endWeek = (int) date("W");
# iterate through all weeks until now
do {
echo $week. " of ". $year;
$week++;
if ($week > 52) { // or greater 53 ?????????????
$year ++;
$week = 1;
}
}
while ($year < $endYear || $week < $endWeek);
Instead of trying to keep track of the bounds, let PHP do it for you.
$start = new DateTime('-2 years');
$end = new DateTime();
$interval = new DateInterval('P1W');
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $date) {
echo $date->format('W') . " of ". $date->format('Y') . "\n";
}
Demo
With the help of Ross Wilson and John Conde I found a solution:
$now = new DateTime;
$from = (new DateTime)->setISODate($now->format('Y') - 2, $now->format('W'));
$from->modify("thursday this week"); // see ISO 8601
while($from < $now) {
echo $from->format('W Y').'<br />';
$from->modify("+1 week");
}
It is important so set the week day to thursday, because according to ISO 8601, the week "belongs" to the year which still contains the thursday.
You could use DateTime and setISODate for something like this:
$now = new DateTime;
$from = (new DateTime)->setISODate($now->format('Y') - 2, $now->format('W'));
while($from < $now) {
echo $from->format('W Y').'<br />';
$from->modify("1 week");
}
Hope this helps!

Date object start from Monday

I currently split a date range into weeks using the following function, and it works great. The only probelm is the week starts from Sunday. Is there a way of modifying it so it starts from Monday?
$start = new DateTime('2014-10-01');
$end = new DateTime('2014-10-31 23:59');
$interval = new DateInterval('P1D');
$dateRange = new DatePeriod($start, $interval, $end);
$weekNumber = 1;
$weeks = array();
foreach ($dateRange as $date) {
$weeks[$weekNumber][] = $date->format('Y-m-d');
if ($date->format('w') == 6) {
$weekNumber++;
}
}
$dow = ($date->format('w') + 6) % 7;
% gives you the remainder after dividing by 7. So, $dow will be 0 for Monday (not Sunday) and 6 will be Sunday (not Saturday).

php - for loop for each month of year

I want a loop that checks the current month, 12 months in the future and 4 months in the past.
For example: Today is 1st August 08. My loop should go through April, May, June, July, August, September, October, November, December, January, February, March, April, May, June, July, and August.
I have tried strotime but I don't know how I can loop 4 months back and 12 months in the future.
Here is my code
$i = 1;
$month = strtotime('2013-08-01');
while($i <= 12) {
$month_name = date('F', $month);
echo $month_name;
echo "<br>";
$month = strtotime('+1 month', $month);
$i++;
I think Yoshi was almost there with his answer, but using DatePeriod with DateTime is more consistent and makes for more readable code IMHO:-
$oneMonth = new \DateInterval('P1M');
$startDate = \DateTime::createFromFormat('d H:i:s', '1 00:00:00')->sub(new \DateInterval('P4M'));
$period = new \DatePeriod($startDate, $oneMonth, 16);
foreach($period as $date){
//$date is an instance of \DateTime. I'm just var_dumping it for illustration
var_dump($date);
}
See it working
This can be quite tricky, here's how I would do it:
$month = date("n", "2013-08-01") - 1; // -1 to get 0-11 so we can do modulo
// since you want to go back 4 you can't just do $month - 4, use module trick:
$start_month = $month + 8 % 12;
// +8 % 12 is the same is -4 but without negative value issues
// 2 gives you: 2+8%12 = 10 and not -2
for ($i = 0; $i < 16; $i += 1) {
$cur_month = ($start_month + $i) % 12 + 1; // +1 to get 1-12 range back
$month_name = date('F Y', strtotime($cur_month . " months"));
var_dump(month_name);
}
something like this?:
$start = -4;
$end = 12;
for($i=$start; $i<=$end;$i++) {
$month_name = date('F Y', strtotime("$i months"));
echo $month_name;
echo "<br>";
}
Your code, just slightly modified.
date_default_timezone_set('UTC');
$i = 1;
$month = strtotime('-4 month');
while($i <= 16) {
$month_name = date('F', $month);
echo $month_name;
echo "<br>";
$month = strtotime('+1 month', $month);
$i++;
}
Simplest solution:
for($i=-4; $i<=12; $i++) {
echo date("F",strtotime( ($i > 0) ? "+$i months" : "$i months") )."\n";
}
Explanation:
The loop starts at -4 and goes all the way upto 12 (total 17, including 0). The ternary statement inside strtotime() simply checks if $i is positive, and if it is, a + is inserted so that we'll get the results for strtotime("+1 months") and similar.
Ta-da!
Using DateTime is the easiest and more readable way.
I would do it like this:
$from = new DateTime('-4 month');
$to = new DateTime('+12 month');
while($from < $to){
echo $from->modify('+1 month')->format('F');
}

Categories