PHP DateTime sub produces unexpected results - php

I have the following example of me subtracting the DateInterval from DateTimeImmutable
$dateA = new DateTimeImmutable('2016-06-30');
$dateB = new DateTimeImmutable('2016-05-31');
$dateInterval = new DateInterval('P3M');
// print 2016-03-30 as expected
echo $dateA->sub($dateInterval)->format('Y-m-d');
// print 2016-03-02 which i would expect 2016-02-29
echo $dateB->sub($dateInterval)->format('Y-m-d');
When I set the period to 'P8M' it works as expected. How it comes, it dosent works for february?

Ok, it's really simple (kind of). Each 'month' interval evaluates to the prior (or X number of prior) month's equivalent day. If there are more days in the current month, than the month being landed on, the excess overflows to the following month.
So if you have a date which is May 31, 2016 and want to subtract 3 month intervals, it will:
Go back 3 months (in the list of months, don't think days yet), resulting in 'February'
Then look for February 31st. This doesn't exist so bleed over to following month 2 days (2016 Febuary has 29 days, so 2 extra days)
Viola! March 2nd.
Go forward, lets say you're in May 31, 2016 and want to add one month
Go forward one month to June.
Look for June 31st, nope, 1 extra day, bleed over to July.
As expected, July 1st is the answer.
The lesson in this: Adding and Subtracting Month intervals sucks, is confusing, and can lead to non-intuitive results unless you've got your month calculation rosetta stone with you.

Explanation from the PHP Docs
Note:
Relative month values are calculated based on the length of months that they pass through. An example would be "+2 month 2011-11-30", which would produce "2012-01-30". This is due to November being 30 days in length, and December being 31 days in length, producing a total of 61 days.

Related

PHP datetime->format : Week of Year and Year result in numbers that don't make sense together

I am try to get the year and week of year off of a given date in my code:
$dueDate->format('W , Y');
In the code above, duedate is a datetime object with this date value:
December 31, 2018
When I output the format I specified above, I get this:
01 , 2018
Looking at each value separately the function is correct. However, together it is confusing.
It seems to be reading December 31st as the first week because it falls on a Monday, so technically it is right, it is the first week of 2019. In that case though, I would want the year to roll over and read 2019.
How can I resolve this to roll over the year in this case only? Any help is appreciated.
You need to use the ISO-8601 week numbering year which is o if you want the year for the ISO-8601 week. From the docs:
ISO-8601 week-numbering year. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0)
$dueDate->format('W , o');

Calculating first day Date of a week from a given week number

I have a question and can't find that specific answer.
In my application, my work year start on july 1 every years. So 52 weeks later, I start again on july first. What I want is a field that I enter let say week 32. I want to have the answer of what is the first day of that specified week and show it as date format. So in other words, if I put week 1 in the field, it will give me the result = July 1 and if I put Week 2 in the field, it will gives me july 8, etc. But I think that every year could change. So I need a calculation that will do this. Is it possible in php?
Tks for help
Seby
strtotime("July 1 2013 +".$monthNum." months");
something like this?

How to get year-sensitive calendar week ? date("W") gives back 52 for 1st of January

As the headline says, PHP's date("W") function gives back the calendar week (for the current day). Unfortunatly it gives back 52 or 53 for the first day(s) of most years. This is, in a simple thinking way, correct, but very annoying, as January 1st of 2012 is not calendar week 52, it's NOT a calendar week of the current year. Most calendars define this as week 0 or week 52 of the previous year.
This is very tricky when you group each day of the year by their calendar week: 1st of January 2012 and 31st of December 2012 are both put into the same calendar week group.
So my question is: Is there a (native) year-sensitive alternative to PHP's date("W") ?
EDIT: I think I wrote the first version of this question in a very unclear way, so this is my edit: I'm searching for a function that gives back the correct calendar week for the first day(s) of the year. PHP's date("W") gives back 52 for the 1st of January 2012, which is "wrong". It should be 0 or null. According to official sources, the first calendar week of a year starts on the first monday of the year. So, if the first day of a year is not a monday, it's not week 1 ! It's week 0. The wikipedia article says
If 1 January is on a Monday, Tuesday, Wednesday or Thursday, it is in week 01. If 1 January is on a Friday, Saturday or Sunday, it is in week 52 or 53 of the previous year.
This becomes tricky as the last days of the year are also in week 52/53. date("W") does not divide into current year and previous year.
This solution converts the excess of december to week 53 and everything in january prior to week 1 to week 0.
$w=(int)date('W');
$m=(int)date('n');
$w=$w==1?($m==12?53:1):($w>=51?($m==1?0:$w):$w);
echo "week $w in ".date('Y');
2013-12-31 ==> week 53 in 2013
2014-01-01 ==> week 1 in 2014
2015-12-31 ==> week 52 in 2015
2016-01-01 ==> week 0 in 2016
And a small test run, so you can see for yourself ;-)
$id=array(25,26,27,28,29,30,31,1,2,3,4,5,6,7,8);
for($iy=2013;$iy<2067;++$iy){foreach($id as $k=>$v){if($k<7){$im=12;}else{$im=1;}
if($k==7){++$iy;echo '====<br>';}$tme=strtotime("$im/$v/$iy");
echo date('d-m-Y',$tme),' * * ';
//THE ACTUAL CODE =================
$w=(int)date('W',$tme);
$m=(int)date('n',$tme);
$w=$w==1?($m==12?53:1):($w>=51?($m==1?0:$w):$w);
//THE ACTUAL CODE =================
echo '<b>WEEK: ',$w,' --- ','YEAR: ',date('Y',$tme),'</b><br>';}--$iy;
echo '----------------------------------<br>';}
Is there a (native) year-sensitive alternative to PHP's date("W") ?
No, there isn't.
According to official sources, the first calendar week of a year starts on the first monday of the year.
I'm not sure what official sources you're referring to.
PHP's date("W") returns the week number according to ISO 8601. As an international standard, ISO 8601 counts as one of possibly many "official sources". If its definition of week numbers doesn't fit your application, you're free to use anything else you like.
If you use a non-standard definition of "first week of the year", or if you use an official source that's not widely recognized, expect to have to write your own function to replace date("W"). (I'm pretty sure you'll need to write a function.)
The date 2012-01-01 was a Sunday. ISO 8601, Wikipedia, and php agree that the ISO week number for 2012-01-01 is 52.
ISO 8601 doesn't define a week 0.
So, if the first day of a year is not a monday, it's not week 1 !
Neither ISO nor Wikipedia say that. ISO 8601 defines week number 1 as the week that has the year's first Thursday in it. For 2012, the first Thursday was on Jan 5, so week number 1 was Jan 2 to Jan 8. 2012-01-01 was in the final week of the previous year, in terms of ISO weeks.
If you want something different, you can play with arithmetic, division, and so on. (Try dividing date("z") by 7, for example.) Or you can store that data in a database, and have your weeks any way you like.
If you're dealing with accounting periods, I'd almost certainly store that data in a table in a database. It's pretty easy to generate that kind of data with a spreadsheet.
The text of data in a table is much easier to audit than the text of a php function, no matter how simple that function is. And the data is certain to be the same for any program that accesses it, no matter what language it's written in. (So if your database someday has programs written in 5 different languages accessing it, you don't have to write, test, and maintain 5 different functions to get the week number.)
$d = new DateTime('first monday january '.date('Y'));
echo $d->format("W");
Google brought me here, and I wanted to post the following to help others like me...
I am in the US, and use DayPilot, and it works as follows:
Week starts on Sun, not Mon.
Jan 1st is always Week 1.
If Jan 1st is not a Sunday, Week 1 is less than 7 days.
This all makes a lot of since to me!
Here is my PHP function to copy that behavior:
function ProperWeekNum($inDate)
{$outNum = $inDate->format('W');
//Make week start on Sunday
if ($inDate->format('D') == 'Sun') {$outNum++;}
//Fix begining of year
if (($outNum >= 52) && ($inDate->format('M') == 'Jan')) {$outNum = 1;}
//Fix WEEK #1 is 1-day (Sat)
else //...without this 2022 was off by 1 all year
{$jan1st = new DateTime($inDate->format('Y').'-01-01');
if ($jan1st->format('D') == 'Sat') {$outNum++;}
}
//Return without leading zero
return ltrim($outNum, '0');
}
I use the function as follows, so when I click on DayPilot, my custom popup's Week # always matches DayPilot's Week #:
$weeknum = ProperWeekNum($startdate);
if ($weeknum != ProperWeekNum($enddate))
{$weeknum .= '-'.ProperWeekNum($enddate);}
Probably won't help the OP, but hopefully it helps someone.

How to repeat a meeting or event same day of every month

I am trying to create new module for my application where the user can repeat event monthly when they create an event. The problem I am facing is to repeat the event same day of every month. What I wrote works fine. But does not repeat same day of that week of every month. For an example if I create an event 7 July 2011 which is Thursday and If I choose repeat monthly it should create next event on 4 Aug 2011 which is also Thursday. But I could not find a way to repeat at the same day of month. It only works for the same date such as if I choose 7 July, it will create 7th August. I can't think of a way to check the day. Please find my query below.
INSERT INTO EVENT(name, date, usr, repfrequent) VALUES ('test', DATE_ADD('2011-8-7', INTERVAL 1 MONTH), 'Monthly');
The above query repeats the event same date of next month instead of the day such as Thursday of next month. Please let me know if any one knows the answer.
First, IMHO, You'll have to answer the "most important question".
What You mean by monthly?
Sounds maybe stupid, but, I think, You don't want Your events to be repeated monthly.
Scenario1
You want them repeated every 4 weeks.
Why do I think so? Because the WEEKDAY is so important for You, not the DAY OF MONTH, and because this is the fastest solution. If this is the case, don't use months, use 4 weeks as a month interval:
<?php
//
date_default_timezone_set('Europe/Berlin');
//
$startDate = '2012-01-06'; // friday
// Show event dates for next 2 years
// one year = ~52 weeks
// 52 weeks / 4 weeks intervals = 13 months ;)
// So, two years = 26 intervals with 4 weeks
//
for($i = 1; $i <= 26; $i++)
{
$weekOffset = $i * 4;
$nextDate = strtotime("{$startDate} +{$weekOffset} weeks");
echo date('Y-m-d l', $nextDate) . PHP_EOL;
}
?>
Result:
2012-02-03 Friday
2012-03-02 Friday *
2012-03-30 Friday *
2012-04-27 Friday
2012-05-25 Friday
2012-06-22 Friday
2012-07-20 Friday
2012-08-17 Friday
2012-09-14 Friday
2012-10-12 Friday
2012-11-09 Friday
2012-12-07 Friday
2013-01-04 Friday
2013-02-01 Friday
2013-03-01 Friday *
2013-03-29 Friday *
2013-04-26 Friday
2013-05-24 Friday
2013-06-21 Friday
2013-07-19 Friday
2013-08-16 Friday
2013-09-13 Friday
2013-10-11 Friday
2013-11-08 Friday
2013-12-06 Friday
As You can see, there will be situations, when an event will occur twice a month (marked with *).
If You can accept this, do it this way.
Scenario 2
You want them repeated every 1st|2nd|3rd|4th monday|tuesday|wednesday|thursday|friday|saturday of month, and the day of month (1-31) is unimportant.
And that's why I suggest to use the first scenario. Because, what if I create an event on the 5th (!) Sunday of a month? Just take a look in Your calendar, for example January 2012 has 5 sundays, what do You want Your app to do, when I create an "monthly" event on 2012-01-29, it's a sunday, The 5th sunday of January 2012. February 2012 has only four sundays...
That's why I don't think You want your events to be repeated MONTHLY. Wrong naming brings often to a wrong direction ;-)
Threre are a lot more scenarios, but they will need coding, date calculations etc. and my opinion is, You should first answer the "most important question" to Yourself :-)
It would be much easier to repeat every n weeks rather than n months.
I know that you can determine day of the week for any given date using Zeller's Congruence. Hope this helps.
Something I'm trying:
$dow = date('w',mktime(0, 0, 0, date("m",strtotime($date)), date("d",strtotime($date)), date("y",strtotime($date))));
$weeknum = date ("W",mktime(0, 0, 0, date("m",strtotime($date)), date("d",strtotime($date)), date("y",strtotime($date)))) - date("W",strtotime(date("Y-m-01")));
$nextMonthDay = date ("m/d/y",strtotime("$weeknum week $dow", strtotime("2011-09-01")));
You'll need to replace $dow in the last line with Monday->Sunday, which can be done through an associate Array. You can then iterate into the future on the year/month values in the last line, and create rows in your table.
It's not very difficult. If you have a date, calculate which week it is (1-7 day is in first week, 8-14 is 2nd week, etc.)
Then add 28 days. Calculate again. If it falls in the same week, keep it. If not add 7 more days.
You should use PHP to put the days into an array.
Find the number of days in the month: http://php.net/manual/en/function.cal-days-in-month.php
Associate a day to each date using an array, you may also want to associate a date stamp to each instance.
$nextmonth[7]['day']='Thursday';
$nextmonth[8]['day']='Friday';
...
Run through the array and search for the first, second, third of forth instance of the day according to the date specified by the user earlier. For example: 7 July 2011 is the first Thursday of July, find next month's first Thursday in the array.

PHP date() returns odd values

echo date("W",strtotime('2010-01-01'));
This outputs 53. I would expect it to output 1. Can anyone else confirm this behavior, or maybe explain why? I couldn't find a bug report on it.
This isn't a bug at all, it's expected behaviour. From PHP's Date Page:
W: ISO-8601 week number of year, weeks starting on Monday
Jan 1, 2010, fell on a Friday, so its week number would belong to 2009, making it part of the 53rd week of 2009. Jan 4, 2010 would be week 1.
A week which begins in December and ends in January the following year belongs to the year where most of its days lie. Therefore a week with at least 4 days in December is the last week of that year and a week with at least 4 days in January is the first week in the new year.
So... the last week of a year always contains the 28th day of December. If you take date("W") on that day of a given year you always get the correct number of weeks for that year. The other end of that definition is that the 4th day of January always lies in the first week of a year.
It returns the ISO-8601 week number of year.
From Wikipedia:
There are mutually equivalent descriptions of week 01:
the week with the year's first Thursday in it (the formal ISO definition),
the week with 4 January in it,
the first week with the majority (four or more) of its days in the starting year, and
the week starting with the Monday in the period 29 December – 4 January.
Since 2010-01-01 was a Friday, non of the conditions is met.

Categories