PHP date interval - wrong month difference - php

Have a look at this code:
$first = DateTime::createFromFormat('Y-m', '2001-07');
$last = DateTime::createFromFormat('Y-m', '1998-06');
$interval = $first->diff($last);
echo "m diff: ".$interval->m." y diff: ".$interval->y."\n";
The output is m diff: 0 y diff: 3
Why does it return a wrong month difference?
Interesting that if I change dates as '2001-08' and '1998-07', it returns a correct month interval ==1.
Thanks!

PHP DateTime doesn't handle incomplete datetimes.
DateTime::createFromFormat('Y-m', '2011-07') gives a DateTime that has a year of 2011, a month of 7, and a day, hour, minute and second taken from the current time (at the moment I write this, 2011-07-31 18:05:47.
Likewise, DateTime::createFromFormat('Y-m', '1998-06') gives a DateTime that has a year of 1998, a month of 6, and a day, hour, minute, and second taken from the current time. Since June 31st is a nonexistent date, the result is 1998-07-01 18:05:47 (31 days after the day before June 1st).
The difference between those two dates is then 3 years, 0 months, and 30 days.
In your example of 2001-08 and 1998-07, both months happen to have a 31st day, so the math comes out correctly. This bug is a difficult one to pin down, because it depends on the date that the code is run even though it doesn't obviously appear to.
You could probably fix your code by using a format of "Y-m-d H:i:s" and appending "-01 00:00:00" to each date you pass to createFromFormat, which will anchor the DateTime you get back to the beginning of the month.

I know this is old, maybe this may help someone out there:
$first = DateTime::createFromFormat('Y-m', '2001-07');
$last = DateTime::createFromFormat('Y-m', '1998-06');
$interval = $first->diff($last);
$num_months = (($interval->y) * 12) + ($interval->m);
Explanation : convert $interval->y which is the year to months by multiplying it by 12 and add the succeeding months which is $interval->m

Related

How to get the first and last day of the last x (x = 3, 6 or 12) months in php?

How can I get the first and last day of the last x (I'll replace x by 3, 6 and 12) months ? I know that for the last month will be : date("Y-n-j", strtotime("first day of previous month"));
You can use the DateTime object:
$myDate = new \DateTime("last day of next month");
$format = $myDate->format("Y-n-d");
It returns: 2015-11-30
You can find Relative Formats here: http://php.net/manual/en/datetime.formats.relative.php
Hope it will help you
You already know that a month starts on the 1st. The t specifier to date gives you the number of days in a given month. You can combine the two of those for a given month to determine the first and last day. From there, you would just need to add/subtract 3, 6, and 12 months but that exercise is up to you.
<?php
$first_of_this_month = date("Y-m-01");
$last_of_this_month = date("Y-m-t")

PHP: get next date based on fixed base date

is there a way in PHP to get the next date(s) using a 4-week interval from a given date ?
Example:
My start date is Friday, Jan 03, 2014 and my interval is every 4 weeks from that date.
What I am looking for is the next date (or dates, if possible) from the current date that matches this 4-week interval.
In the above example this would be Friday, May 23, 2014 (then June 20, 2014, July 18, 2014 etc.).
I know I can get the current date as follows: $today = date('Y-m-d');
and I could probably set the start date like this: $start = date('2014-01-03');
but I don't know how to calculate the interval and how to find out the next matching date(s).
You should read up on the DateTime classes, specifically DatePeriod and DateInterval:
$start = new DateTime('2014-01-03');
$interval = DateInterval::createFromDateString('4 weeks');
$end = new DateTime('2015-12-31');
$occurrences = new DatePeriod($start, $interval, $end);
foreach ($occurrences as $occurrence) {
echo $occurrence->format('Y-m-d') . PHP_EOL;
}
DatePeriod takes a start date and a DateInterval and allows you traverse over the object to get all dates within the boundaries using the given interval. The cut off can be either a set number of cycles (so the next 10 dates) or an end date (like above), even if the end date is not one of the dates the interval falls on (it will stop below it). Or you can use an 8601 interval notation string (which sounds so much fun, huh?), but I'm pretty shaky on that.
If 4-week interval means 7 x 4 = 28 days, you can obtain the "next date" by:
$today = new DateTime();
$next_date = $today->add(new DateInterval('P28D'));
$next_next_date = $next_date->add(new DateInterval('P28D'));
$next_next_next_date = $next_next_date->add(new DateInterval('P28D'));
And if you want to calculate more "next dates", you can repeat the add() to repetitively add 28 days to your date.
Note: Beside using P28D, you can use P4W, which means 4 weeks.
While some answers may suggest using strtotime(), I find the object-oriented approach more structured. However, DateInterval is only available after PHP >= 5.3.0 (while DateTime is available after PHP >= 5.2.0)
You could use strtotime()
echo date('Y-m-d', strtotime('now +4 weeks'));
UPDATED:
$start = date('Y-m-d', strtotime('2014-01-03 +4 weeks'));
echo $start;
You could also run this in a for loop to get the next 6 or more dates. For example:
$Date = "2014-01-03";
$Int = 6;
for($i=0; $i<$Int; $i++){
$Date = date('Y-m-d', strtotime('{$Date} +4 weeks'));
echo $Date;
}

selecting a day of the week every x number of days thats not a weekend

I have code already for a date range that works fairly well (not using datetime class, I'm stuck in 5.2). But I want to modify it to select the previous day before the weekend (e.g. friday) based on every fortnight from the starting date.
I have been experimenting with finding all the saturdays and sundays then doing a array_search with a mktime as such: mktime(0, 0, 0, date("m", $date), date("d", $date)-1, date("Y", $date)
It seems to find all the Saturdays & Sundays but my code seems replace the actual dates instead of just selecting the previous day and styling it.
My code is a bit of a mess, but its pretty generic date range, e.g. while ($date1 <= $date2) { do stuff here }
So how can I select the friday before a weekend of a number of every 14 days from the start date.
Edit:
The answers below were all fine, but not quite what I was asking. Hopefully I can be more clear here.
I don't want code that finds me all the fridays in a date range specifically, I want to do a date range and every time there is a friday that is +14 days from the start date (i.e. every 2 weeks) I want to style it so it stands out. I have managed to find all the weekends every 2 weeks and style it, but I can't figure out how to select the friday's 2 weeks in advance from the start date.
You can use strtotime() to get the initial Friday and then you can either use that or your mktime function to do your 14 day adds.
$thisWeekFriday = strtotime('friday');
$twoWeeksFromFriday = $thisWeekFriday + 1209600; //1209600 seconds in two weeks
echo date('Y-m-d H:i:s', $twoWeeksFromFriday);
$number_of_dates = 10;
for ($i = 0; $i < $number_of_dates; $i++) {
echo date('Y-m-d', strtotime('Friday +' . ($i * 2) . ' weeks'));
}
today this would output
2011-08-12
2011-08-26
2011-09-09
2011-09-23
2011-10-07
2011-10-21
2011-11-04
2011-11-18
2011-12-02
2011-12-16

How to convert week number and year into unix timestamp?

I'm trying to group together dates into a week number and year, and then I want to convert that week number back into a unix timestamp. How can I go about doing this?
I assume you are using ISO 8601 week numbers, and want the first day of a ISO 8601 week so that e.g. Week 1 of 2011 returns January 3 2011.
strtotime can do this out of the box using the {YYYY}W{WW} format:
echo date("Y-m-d", strtotime("2011W01")); // 2011-01-03
Note that the week number needs to be two digits.
Shamefully, DateTime::createFromFormat, the fancy new PHP 5 way of dealing with dates, seems unable to parse this kind of information - it doesn't have a "week" placeholder.
$week: The week number
$year: The year number
Then:
$timestamp = gmmktime (0, 0 , 0 , 1, , 4 + 7*($week - 1), $year);
The 4 + 7*($week - 1) comes from the fact that according to ISO 8601, the first week of the year is the one that contains January 4th.
strtotime('1/1/2011 + 4 weeks') (1/1 ist always in week number one; this would bring me to week number five). if you want any timestamp in the week then that's all you need, else you would have to go to the monday in this week:
$t = strtotime('1/1/2011 + 4 weeks');
$t -= 24 * 60 * 60 * date('w', $t);
Update: Instead of 1/1/2011 use the first monday in 2011. The 2nd calculation is not needed anymore.

How to get previous month and year relative to today, using strtotime and date?

I need to get previous month and year, relative to current date.
However, see following example.
// Today is 2011-03-30
echo date('Y-m-d', strtotime('last month'));
// Output:
2011-03-02
This behavior is understandable (to a certain point), due to different number of days in february and march, and code in example above is what I need, but works only 100% correctly for between 1st and 28th of each month.
So, how to get last month AND year (think of date("Y-m")) in the most elegant manner as possible, which works for every day of the year? Optimal solution will be based on strtotime argument parsing.
Update. To clarify requirements a bit.
I have a piece of code that gets some statistics of last couple of months, but I first show stats from last month, and then load other months when needed. That's intended purpose. So, during THIS month, I want to find out which month-year should I pull in order to load PREVIOUS month stats.
I also have a code that is timezone-aware (not really important right now), and that accepts strtotime-compatible string as input (to initialize internal date), and then allows date/time to be adjusted, also using strtotime-compatible strings.
I know it can be done with few conditionals and basic math, but that's really messy, compared to this, for example (if it worked correctly, of course):
echo tz::date('last month')->format('Y-d')
So, I ONLY need previous month and year, in a strtotime-compatible fashion.
Answer (thanks, #dnagirl):
// Today is 2011-03-30
echo date('Y-m-d', strtotime('first day of last month')); // Output: 2011-02-01
Have a look at the DateTime class. It should do the calculations correctly and the date formats are compatible with strttotime. Something like:
$datestring='2011-03-30 first day of last month';
$dt=date_create($datestring);
echo $dt->format('Y-m'); //2011-02
if the day itself doesn't matter do this:
echo date('Y-m-d', strtotime(date('Y-m')." -1 month"));
I found an answer as I had the same issue today which is a 31st. It's not a bug in php as some would suggest, but is the expected functionality (in some since). According to this post what strtotime actually does is set the month back by one and does not modify the number of days. So in the event of today, May 31st, it's looking for April-31st which is an invalid date. So it then takes April 30 an then adds 1 day past it and yields May 1st.
In your example 2011-03-30, it would go back one month to February 30th, which is invalid since February only has 28 days. It then takes difference of those days (30-28 = 2) and then moves two days past February 28th which is March 2nd.
As others have pointed out, the best way to get "last month" is to add in either "first day of" or "last day of" using either strtotime or the DateTime object:
// Today being 2012-05-31
//All the following return 2012-04-30
echo date('Y-m-d', strtotime("last day of -1 month"));
echo date('Y-m-d', strtotime("last day of last month"));
echo date_create("last day of -1 month")->format('Y-m-d');
// All the following return 2012-04-01
echo date('Y-m-d', strtotime("first day of -1 month"));
echo date('Y-m-d', strtotime("first day of last month"));
echo date_create("first day of -1 month")->format('Y-m-d');
So using these it's possible to create a date range if your making a query etc.
If you want the previous year and month relative to a specific date and have DateTime available then you can do this:
$d = new \DateTimeImmutable('2013-01-01', new \DateTimeZone('UTC'));
$firstDay = $d->modify('first day of previous month');
$year = $firstDay->format('Y'); //2012
$month = $firstDay->format('m'); //12
date('Y-m', strtotime('first day of last month'));
strtotime have second timestamp parameter that make the first parameter relative to second parameter. So you can do this:
date('Y-m', strtotime('-1 month', time()))
if i understand the question correctly you just want last month and the year it is in:
<?php
$month = date('m');
$year = date('Y');
$last_month = $month-1%12;
echo ($last_month==0?($year-1):$year)."-".($last_month==0?'12':$last_month);
?>
Here is the example: http://codepad.org/c99nVKG8
ehh, its not a bug as one person mentioned. that is the expected behavior as the number of days in a month is often different. The easiest way to get the previous month using strtotime would probably be to use -1 month from the first of this month.
$date_string = date('Y-m', strtotime('-1 month', strtotime(date('Y-m-01'))));
I think you've found a bug in the strtotime function. Whenever I have to work around this, I always find myself doing math on the month/year values. Try something like this:
$LastMonth = (date('n') - 1) % 12;
$Year = date('Y') - !$LastMonth;
date("m-Y", strtotime("-1 months"));
would solve this
Perhaps slightly more long winded than you want, but i've used more code than maybe nescessary in order for it to be more readable.
That said, it comes out with the same result as you are getting - what is it you want/expect it to come out with?
//Today is whenever I want it to be.
$today = mktime(0,0,0,3,31,2011);
$hour = date("H",$today);
$minute = date("i",$today);
$second = date("s",$today);
$month = date("m",$today);
$day = date("d",$today);
$year = date("Y",$today);
echo "Today: ".date('Y-m-d', $today)."<br/>";
echo "Recalulated: ".date("Y-m-d",mktime($hour,$minute,$second,$month-1,$day,$year));
If you just want the month and year, then just set the day to be '01' rather than taking 'todays' day:
$day = 1;
That should give you what you need. You can just set the hour, minute and second to zero as well as you aren't interested in using those.
date("Y-m",mktime(0,0,0,$month-1,1,$year);
Cuts it down quite a bit ;-)
This is because the previous month has less days than the current month. I've fixed this by first checking if the previous month has less days that the current and changing the calculation based on it.
If it has less days get the last day of -1 month else get the current day -1 month:
if (date('d') > date('d', strtotime('last day of -1 month')))
{
$first_end = date('Y-m-d', strtotime('last day of -1 month'));
}
else
{
$first_end = date('Y-m-d', strtotime('-1 month'));
}
If a DateTime solution is acceptable this snippet returns the year of last month and month of last month avoiding the possible trap when you run this in January.
function fn_LastMonthYearNumber()
{
$now = new DateTime();
$lastMonth = $now->sub(new DateInterval('P1M'));
$lm= $lastMonth->format('m');
$ly= $lastMonth->format('Y');
return array($lm,$ly);
}
//return timestamp, use to format month, year as per requirement
function getMonthYear($beforeMonth = '') {
if($beforeMonth !="" && $beforeMonth >= 1) {
$date = date('Y')."-".date('m')."-15";
$timestamp_before = strtotime( $date . ' -'.$beforeMonth.' month' );
return $timestamp_before;
} else {
$time= time();
return $time;
}
}
//call function
$month_year = date("Y-m",getMonthYear(1));// last month before current month
$month_year = date("Y-m",getMonthYear(2)); // second last month before current month
function getOnemonthBefore($date){
$day = intval(date("t", strtotime("$date")));//get the last day of the month
$month_date = date("y-m-d",strtotime("$date -$day days"));//get the day 1 month before
return $month_date;
}
The resulting date is dependent to the number of days the input month is consist of. If input month is february (28 days), 28 days before february 5 is january 8. If input is may 17, 31 days before is april 16. Likewise, if input is may 31, resulting date will be april 30.
NOTE: the input takes complete date ('y-m-d') and outputs ('y-m-d') you can modify this code to suit your needs.

Categories