Carbon PHP Loop through each month on date range [duplicate] - php

I failed to find a proper solution to this issue. As you see in Example #3 in the PHP documentation, they state that one must beware when adding months using the DateInterval in DateTime::add.
There's not really any explanation for why the method's behavior is as such and what I can do to avoid this, which I find to be an error at first sight.
Anyone have some insight into this?

The issue is that each month can have a different number of days in them. The question is what you're doing when you want to increment a date by 1 month. Per the PHP documentation if you're on January 31st (or 30th) and you add 1 month, what is the expected behavior?
February only has 29 days in it. Do you want to be set to the last day of the month? You're generally safer incrementing by a set number of days if that's what you're looking for, or a static date based on the current date. Without knowing what you're trying to accomplish when you increment your month, it's tough to say how to watch for an error.
EDIT:
As someone mentions in the similar post commented by Mike B above, you probably want to do something where you (in pseudocode):
1) Use cal_days_in_month() for the next month and save that number to a variable x
2) If x >= current billing DOB, increment and be done
3) DateTime::modify('last day') (havent used this before but something along these lines) to set the date to the last date of the next month (set it to the 1st of the next month, then last day?)
Worth noting is that if you use the variable here as the new billing value, you'll wipe out your original value. I would save an extra DB value that's "first billing date" or just "billing_day_of_month" or something, and use that to figure out the day of month that you should be looking at

If your goal is to strictly increment by user-friendly months (thus, 3 months from January 21st should be April 21st), with the exception that shorter membership months get shortened (thus, 1 month from January 31st is February 28th/29th), then you only need to go back a few days if you crossed over into the next month:
function addMonths($date,$months) {
$orig_day = $date->format("d");
$date->modify("+".$months." months");
while ($date->format("d")<$orig_day && $date->format("d")<5)
$date->modify("-1 day");
}
$d = new DateTime("2000-01-31");
addMonths($d,1);
echo $d->format("Y-m-d"); // 2000-02-29

Related

Retrieve remaining time between two dates considering business hours

I am trying to write a php solution to calculate the planned end time considering the target in business hours.
It shouldn't consider some days (retrieved from setting saved in db) such as holidays.
Also business hours are retrieved from db (morning_from (8:30am), morning_to (1:00pm), evening_from (2:30pm), evening_to (6:30pm)).
I want to develop this script because I want that my page shows the remaining time for technical resolution of an opened ticket every day.
For example:
customer having contract with 10 working hours SLA opens a ticket
today (friday) 31/01/2020 16:00:00, considering that in the
noBusinessDays = array("saturday", "sunday") and businessHours set as mentioned before(8:30-13:00/14:30-18:30), the result will have to
be monday 3/02/2020 17:30:00.
Code example:
$noBusinessDays = array("saturday", "sunday");
$businessHours = array("morning_from" => "8:30", "morning_to" => "13:00", "evening_from" => "14:30", "evening_to" => "18:30");
$SLA = "10"; //hours
$ticketDate = new DateTime();
$ticketDate->setTimestamp(strtotime("31/01/2020 16:00:00"));
// I don't know how to use my arrays to say in this calculation how to use them
$maximumLimit = $ticketDate->add(new DateInterval("PT" . $SLA ."H"));
Thank you in advance.
You may use the following function
// intersection of 2 time intervals
// input - Unix timestamps (start,end)
// output - intersection in seconds
function time_union($b_1,$e_1,$b_2,$e_2)
{
return max(0,$e_1-$b_1 - max(0,$e_1-$e_2) - max(0,$b_2-$b_1));
}
You will start with an empty time interval [X, Y) where X is the timestamp of the ticket creation and Y initially is equal to X.
Then you start adding days to Y - one by one. Each time you expand the time interval to contain another day - you use the above function to check how much of the SLA hours are covered (i.e. overlapping) with the working hours in the day you have just added. If the day is marked as a holiday - you simple skip it and continue with the next date.
If you find out that SLA hours are partially covered with either the morning or evening business hours - you should simply subtract the extra hours.
In the end Y will be equal to the timestamp that you want to show in your application.
I think I'd break down the problem into pieces. After calculating the total number of days in the interval, first dispose of the trivial case that it's all happening in one week.
begin by calculating the number of "whole weeks." Each "whole week" is five business days. Subtract the corresponding interval of time and proceed. Now, look at the day-of-the-week of the start-date: each day adds a certain number of days. Then the day-of-week of the end date, likewise. You can then consider hour-of-the-day as needed.
Holidays are a simple table: if the day falls within the range, subtract one day.
Now ... having said all of that, the very first thing that I would do is to search GitHub and SourceForge! Because I am extremely sure that somebody out there has already done this. :-D
"Actum Ne Agas: Do Not Do A Thing Already Done."

Use date and/or strtotime() functions in php to identify complex date patterns

I am well versed in using strtotime or the date modify functions within PHP to find NEXT or PREVIOUS days/months/years, or to add or subtract dates. There is the ability to do something like strtotime("first day of last month") but there is not the ability to use a specific month name (let's say July) as a parameter...so it seems I cannot say strtotime("first day of last July").
My question is NOT
"can you write me some code to give me the date"
but rather
"is there a streamlined 1 or 2 line approach using strtotime() or
something else that will enable me to reach the same output with
something more compact, tidy, and clear?"
I am trying to streamline some fairly clunky code to figure out the date (really just the year) of July 1 two instances ago...not the most recent past July 1, but the one a year prior to that (so it could be a date almost 2 full years in the past...or could be as recent as 1 year and 1 day in the past).
For example:
Assume today is February 26, 2014. I am trying to output 2012-07-01.
However a bit later this same year... let's say on July 2 of 2014...
the output would now be 2013-07-01
So, essentially I need to figure out if July 1 has already happened in the current year...and if not then subtract 2 from the current year...and if yes, then subtract 1 from the current year.
A clunky version looks like this:
$CurrentDate = date("Y-m-d");
$CurrentYear = date("Y");
$ThisYearCutOff = $CurrentYear.'-07-01';
if($CurrentDate > $ThisYearCutOff){
echo ($CurrentYear-1).'-07-01';
}
else
{
echo ($CurrentYear-2).'-07-01';
}
Any thoughts on how to do this in a relatively streamlined bit of php code?

How to define the relative time for a repetitive yearly interval?

I'm looking for a way to filter for events that happened between previous September (1st) and upcoming September (1st) every year. Since the filter is already part of an existing piece of code I can ony add a fixed date (timestamp) or use a relative date that runs through php strtotime. I can't run any php in the field.
Because a timestamp is fixed for a set year, I want to use relative dates to make this filter work every year.
I have tried to build this filter using simple things like [last/next] September, but that is invalid strtotime syntax.
From there I tried things like 9/1 [last/this/next] year. They were valid, but since I can't use logic to determine which one to use, it is not good.
For dates prior to this year September 1st the filter would be 9/1
last year>9/1 this year
For dates past September 1st this year it is 9/1 this year>9/1 next year.
So to summarize, I am looking for a relative date string (strtotime) to get the previous September 1st and a string for the upcoming September first. (If they exist.)
i guess you want dates in between 2012/09/01 - 2013/09/01.
you can use string in link if that's what your talking about, but you need years.
$this_year='2013/09/01';
$last_year = date("Y-m-d",strtotime("$this_year - 1 year"));
$next_year = date("Y-m-d",strtotime("$this_year + 1 year"));
echo
" last year
this year
next year
";
if(strtotime($this_year) <= strtotime($filter_date) and strtotime($filter_date) >= strtotime($last_year))
{
// dates in between 2012/09/01 - 2013/09/01
}
if(strtotime($this_year) >= strtotime($filter_date) and strtotime($filter_date) <= strtotime($next_year))
{
// dates in between 2013/09/01 - 2014/09/01
}
if you are getting data from database then use code below as its easier.
SELECT * FROM table_name WHERE 'date_field' BETWEEN ('$this_year') and ('$last_year ')

PHP Number of days for billing cycle with only day of month known

I am trying to calculate number of days in a billing cycle and the number of days left in a billing cycle. I have a value (i.e., 5) which represents the billing date every month (the 5th of every month in this case). I need a way to:
a. Calculate the number of days between the 5th of this month and the 4th of the following month
b. Calculate the number of days between today and the 4th of next month
Where I keep getting stuck is, let's say, today is the 3rd of the month. Well the 5th of this month hasn't happened yet so the calculation would need to look back to the 5th of last month (which will ultimately calculate that there are two days left in the billing cycle). Can I get some help how to property calculate this? I'm sorry I don't have any code snippets to post - every combination of mktime / date / strtotime I try fails miserably so I don't have anything helpful to put up.
Thank you!
Your question is hard to understands and your explanation doesn't make it easier, but it seems that you have a certain date X (which is known completely, with day, month and year), and the next n-th day of the month.
Assume you have $d, $m, $y (day, month, year of current date) and $n (billing on n-th day). You can use "date()" to extract the parts if you don't have them split yet.
If $d > $n, then you will bill them next month. If $d < $n, it will be this month. If $d == $n billing is now or next month, your choice.
Adding a month (remember to correctly handle December) will give you the billing date, and you can calculate the difference.
Alternatively, just add a day to the current date in a loop until you hit the billing date...

What can go wrong when adding months with a DateInterval and DateTime::add?

I failed to find a proper solution to this issue. As you see in Example #3 in the PHP documentation, they state that one must beware when adding months using the DateInterval in DateTime::add.
There's not really any explanation for why the method's behavior is as such and what I can do to avoid this, which I find to be an error at first sight.
Anyone have some insight into this?
The issue is that each month can have a different number of days in them. The question is what you're doing when you want to increment a date by 1 month. Per the PHP documentation if you're on January 31st (or 30th) and you add 1 month, what is the expected behavior?
February only has 29 days in it. Do you want to be set to the last day of the month? You're generally safer incrementing by a set number of days if that's what you're looking for, or a static date based on the current date. Without knowing what you're trying to accomplish when you increment your month, it's tough to say how to watch for an error.
EDIT:
As someone mentions in the similar post commented by Mike B above, you probably want to do something where you (in pseudocode):
1) Use cal_days_in_month() for the next month and save that number to a variable x
2) If x >= current billing DOB, increment and be done
3) DateTime::modify('last day') (havent used this before but something along these lines) to set the date to the last date of the next month (set it to the 1st of the next month, then last day?)
Worth noting is that if you use the variable here as the new billing value, you'll wipe out your original value. I would save an extra DB value that's "first billing date" or just "billing_day_of_month" or something, and use that to figure out the day of month that you should be looking at
If your goal is to strictly increment by user-friendly months (thus, 3 months from January 21st should be April 21st), with the exception that shorter membership months get shortened (thus, 1 month from January 31st is February 28th/29th), then you only need to go back a few days if you crossed over into the next month:
function addMonths($date,$months) {
$orig_day = $date->format("d");
$date->modify("+".$months." months");
while ($date->format("d")<$orig_day && $date->format("d")<5)
$date->modify("-1 day");
}
$d = new DateTime("2000-01-31");
addMonths($d,1);
echo $d->format("Y-m-d"); // 2000-02-29

Categories