Why does my datetime have trouble with the 31st? - php

I have the following piece of code on a site I'm working on. It was written by a previous developer, so I am unsure of exactly how to deal with it:
$datetime = new DateTime("$dt_start_ymd +$gap month");
$dt_next = new DateTime("Thursday " . $datetime->format('Y-m-01'));
$dt_next_0 = $dt_next->setTime(02, 00)->getTimestamp();
$dt_next = $dt_next->getTimestamp();
The variable $gap is an incrementing count (1, 2, 3, etc). The code above should output a sequence of dates listing the first thursday for each month for a number of months as indicated by $gap
i.e. 05/07/2017, 03/08/2017, 07/09/2017 (those being the first Thursday of those respective months).
The code works fine on most dates, but for a reason I cannot fathom if it is passed a date that is the 31st of a month (i.e. 2017-05-31) the system breaks and outputs incorrect answers.

I think this code is unnecessarily complicated. In addition, you're in a situation where if the given date is the 31st and you attempt to add 1 month, you're ending up in a situation where it's attempting to find the 31st of the next month, which does not exist except in December->January.
You might want to consider something like
$currentDate = new \DateTime();
$iterations = 10;
for ($i = 0; $i < $iterations; $i++) {
$first_thursday = new \DateTime(
sprintf('first Thursday of %s', $currentDate->format('F Y'))
);
$nextDate = new \DateTime($currentDate->format('Y-m-d'));
$nextDate->add(new \DateInterval('P1M'));
if ($nextDate->format('m') > $currentDate->format('m') + 1) {
$nextDate->sub(new \DateInterval('P1D'));
}
$currentDate = $nextDate;
echo $first_thursday->format('Y-m-d');
}
This will give you the first Thursday in every month for the next 10 months, and I think is much easier to read than the example you gave.
EDIT: I've updated the code so that it doesn't skip out months. See my comment below.

Related

php - loop through dates to predict 4on 40ff shift schedule

Gooooood evening all...
Edit:
I'm trying to write a php script to output the dates I will work for the remainder of the year if given a specific start date.
For example I start on 2020-05-14 and work for 4 days then take 4 days off.
This continues for the remainder of the year.
I would like to output the dates i will work and think it could probably be done using the php for loop, however i've been thinking of how to do this for too long and can't seem to break through the wall.
The start of my code is this:
<?php
$z = date("z",mktime(0,0,0,05,14,2020));
for($i=$z;$i<=365;$i+=4) {
echo("$i<br>");
}
?>
Any advice would be much appreciated on a possible solution.
You're computing the day of the year (e.g. for 2020-05-14, that would be 134), which is fine for letting your script know when to stop.
However, you're wanting to output dates, where you're currently outputting the day of the year instead.
I think there's an easier way to do what you're trying to accomplish.
<?php
$start = new \DateTime("2020-05-14 00:00:00");
$end = new \DateTime($start->format("Y") . "-12-31 00:00:00");
$four = new \DateInterval("P4D");
$one = new \DateInterval("P1D");
$format = 'Y-m-d';
$date = $start;
while ($date <= $end) {
echo $date->format($format), PHP_EOL;
for ($day = 2; $day <= 4; $day++) {
$date->add($one);
echo $date->format($format), PHP_EOL;
}
$date->add($four);
}

Pick a specific day of the week and start counting forward until a set limit is reached in php

Having a php challenge with dynamically setting a day of the week say -thursday, to a function and have the function loop through each day (friday, saturday.. tuesday until a condition i>0 is met.
I have searched thoroughly but could not finding a matching solution.
Please see break down below:
My form:
showing user input that's counted
My code:
//get duraiton in days
function durationInDays($data) {
$startDate = date_create($data['startdate']);
$endDate = date_create($data['enddate']);
$dateDiff = date_diff($startDate,$endDate);
$durationInDays = $dateDiff -> format("%a");
return $durationInDays;
}
//pass duration in days to function to break into weeks and days
function numberOfWeeksAndDays($durationInDays) {
$days = $numdays%7;
$weeks = floor($numdays/7);
return [$weeks, $days];
}
//parse in number of days into function to loop starting from
//day of week of start date say --thursday for example with a
//condition to stop loop once numberOfDays is zero.
function extraMassCount($dayOfWeek, $numberOfDays, $countMass) {
$daysOfTheWeek = [
'monday','tuesday','wednesday','thursday','friday','saturday','sunday',
'monday','tuesday','wednesday','thursday','friday','saturday','sunday'
];
$massCountForExtraDays = 0;
for ($i = $numberOfDays; $i > 0; $i--) {
if($daysOfTheWeek[$i]=='monday')
$massCountForExtraDays += $countMass[0];
if($daysOfTheWeek[$i]=='tuesday')
$massCountForExtraDays += $countMass[1];
if($daysOfTheWeek[$i]=='wednesday')
$massCountForExtraDays += $countMass[2];
if($daysOfTheWeek[$i]=='thursday')
$massCountForExtraDays += $countMass[3];
if($daysOfTheWeek[$i]=='friday')
$massCountForExtraDays += $countMass[4];
if($daysOfTheWeek[$i]=='saturday')
$massCountForExtraDays += $countMass[5];
if($daysOfTheWeek[$i]=='sunday')
$massCountForExtraDays += $countMass[6];
}
return $massCountForExtraDays;
}
The problem: If $numberOfDays=6,then i=6 and the function starts with friday which is not the start date.
My question: how do I implement dayOfWeek parameter so the function extraMassCount will start counting dynamically e.g thursday if startdate is thursday and not the way it is hardcoded to start? I hope my question is clear.
That is, as shown in the form, the function is supposed count the number of masses checked per day and add them together. Starting from startdate to the enddate. Once the durationInDays is broken down to weeks and days I need the function to start at the startdate say --wednesday and add countMass(which is a count of the Masses checked by the user with datatype int) for each day onward.. thursday, friday, etc. – I appreciate the help!
Like this, with relative date formats and DateTime class
$Date = new DateTime;
//go to monday
$Date->modify('monday this week');
for($i=0;$i<10;++$i){
echo $Date->format('l')."\n";
$Date->modify('+1 days'); //go to next day
}
Output
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Monday
Tuesday
Wednesday
Sandbox
I think the rest you can work out as I am not really sure what that does.
If you want a number for the day, for like this part $countMass[0]; you can use the w format. But 0 is Sunday, if I remember correctly.
for($i=0;$i<10;++$i){
$Date->modify('+1 days');
//dont assume your inputs will be correct
if(isset($countMass[$Date->format('w')])){
$massCountForExtraDays += $countMass[$Date->format('w')];
}
}
PS. instead of all these separate if statements, which is bad performance wise. I would just use a switch statement, but at the very least connect those with else so they don't all evaluate every time, needlessly. $daysOfTheWeek[$i] can only equal one of those. If you notice above, I just optimized them out.

How to loop over weeks and find the exact date of some days?

I'm working on a website where the user can create some events every X days (where X is the name of a day in the week). Then, he needs to enter the number of events he wants to create in the future.
For example, the user selects every Monday and Tuesday and decides to create 150 events.
Here is the code I have made until now :
// Init the date counter
$cpt_date_found = 0;
// Number of date to find
$rec_occ = 150;
// Init an ending date far in the future
$endDate = strtotime('+10 years', time());
// Loop over the weeks
for($i = strtotime('Monday', strtotime(date("d.m.Y"))); $i <= $endDate; $i = strtotime('+1 week', $i)) {
// -- Monday date found, create the event in the database
$cpt_date_found++;
// Break the loop if we have enough dates found
if($cpt_date_found == $rec_occ) {
break;
}
}
This code finds the date of every Monday in the future and breaks the loop once we have reached the number of occurrences the user specified.
I have entered an ending date far in the future to make sure I can break the loop before the end of the occurrences count specified by the user.
First I'm not sure about the "quality" of my code... I know that breaking the loop is not the best idea and am wondering if another solution would better fit my needs.
Then, instead of repeating the loop more times if the user specified several days (let's say, Monday, Tuesday and Friday), is there a way to loop one time for every provided days?
Thanks!
The following code will loop over a period of 5 years. For each week in those 5 years it will generate a DatePeriod containing each day of that week. It will compare each of those days to your preset array with days you are looking for. You can then generate your event after which the code will countdown for a certain amount of times. If the counter hits zero, you are done.
$searchDates = array('Mon', 'Tue', 'Fri');
$amountOfTimes = 27;
$startDate = new DateTime();
$endDate = new DateTime('next monday');
$endDate->modify('+5 years');
$interval = new DateInterval('P1W');
$dateRange = new DatePeriod($startDate, $interval ,$endDate);
// Loop through the weeks
foreach ($dateRange as $weekStart) {
$weekEnd = clone $weekStart;
$weekEnd->modify('+6 days');
$subInterval = new DateInterval('P1D');
// Generate a DatePeriod for the current week
$subRange = new DatePeriod($weekStart, $subInterval ,$weekEnd);
foreach ($subRange as $weekday) {
if (in_array($weekday, array('Mon', 'Fri', 'Sun'))) {
// Create event
// Countdown
$amountOfTimes--;
}
if ($amountOfTimes == 0) {
break;
}
}
}

PHP Incrementing date with day name

I am currently attempting to get a list of dates from a current date using the following format so that I can process it and stick it in my database
Saturday/02-05-2015
So far, i've managed to get the system to output the date correctly, but can not get it to increment in single day values.
My current code to attempt to increment this is the following
$tempStartDateN = ("$splode[0]/$splode[1]/$splode[2]/$splode[3]");
echo $tempStartDateN;
$tempStartDateN = date('l/d/m/Y', strtotime($tempStartDateN . ' + 1 day'));
echo $tempStartDateN;
I am currently using explode to process the data after the increment, which works fine, but can not get the date itself to increment as long as the day name is included.
Currently, the time is got using this code, which is processed afterwords using explode
$OldDateArray = date("Y/m/d/l");
So to keep a long question short, what is the best way to increment a date that requires the day name, day, month then year?
EDIT:
Heres my current code, managed to get this far thanks to SamV
$date = date("l/d/m/Y");
echo $date;
echo ('</br>');
list($weekdayName, $dateString) = explode("/", $date, 2);
$dateObj = new \DateTime($dateString);
for($i=0; $i<=5; $i++){
$dateObj->add(new \DateInterval("P1D")); // P1D stands for "Period 1 Day"
echo $dateObj->format("l/d/m/Y"); // Sunday/03/05/2015
echo ('</br>');
}
What this does however is:
Friday/01/05/2015
Tuesday/06/01/2015
Wednesday/07/01/2015
Thursday/08/01/2015
Friday/09/01/2015
Saturday/10/01/2015
Sunday/11/01/2015
this means that date and month are swapping around, what is causing this?
You don't need to parse the week day name to add days onto a date.
$date = "Saturday/02-05-2015";
list($weekdayName, $dateString) = explode("/", $date, 2); // Parse "02-05-2015"
$dateObj = new \DateTime($dateString);
$dateObj->add(new \DateInterval("P1D")); // P1D stands for "Period 1 Day"
echo $dateObj->format("l/d/m/Y"); // Sunday/03/05/2015
I used the DateTime class, here is the documentation.
I wrote out what you are trying to do yourself, not sure what is causing your issue. This code works though.
$date = "Friday/01-05-2015";
list($weekdayName, $dateString) = explode("/", $date, 2); // Parse "01-05-2015"
$dateObj = new \DateTime($dateString);
for($i = 0; $i < 5; $i++) {
$dateObj->add(new \DateInterval("P1D")); // P1D stands for "Period 1 Day"
echo $dateObj->format("l/d/m/Y") . '<br>';
}
Outputs:
Saturday/02/05/2015
Sunday/03/05/2015
Monday/04/05/2015
Tuesday/05/05/2015
Wednesday/06/05/2015
If strtotime is able to parse a date it returns the timestamp. Why not add to it the number of seconds in a day? Smth. like $timestamp += 24 * 3600;
P.S. As far as I can understand, strtotime may accept timestamp as second argument (http://us2.php.net/manual/en/function.strtotime.php) smth. like $timestamp = strtotime('+1 day', $timestamp);

recurring date functions in php

Good Day,
I am trying to create a recurring date system that has the following:
nth day of nth month (2nd day of every 3rd month)
$this_months_friday = strtotime('+3 days +4 months');
the output of that will always be current day + 3 days of the 4th month.
how do I get it to display the nth day of the nth month?
since i also tried
$this_months_friday = strtotime('every 3 days +4 months');
and it did not return any result. Should i stick with strtotime on this one or move to DateTime function of php. though i wont still be able to formulate the proper argument for that kind of date sequence.
Any help would be greatly appreciated.
Thank You.
Probably better off using DateTime with a couple intervals:
$d = new DateTime();
$d->add(new DateInterVal('P' . $days . 'D'))->add('new DateInterVal('P' . $months . 'M'));
not sure what youre two example intervals are wanting.
You want an internval to start in 4 months, which then repeats every 3 days?
That'd be something more like
$d = new DateTime();
$d->add(new DateInterval('P4M')); // jump ahead 4 months immediately
$day3 = new DateInterval('P3D');
for ($i = 0; $i < 100; $i++) {
$d->add($day3); // jump ahead 3 days
... do something with this new date
}
for a basic recurring event, +4 months + 3 days, you'd simply have one interval:
$interval = new DateInteval('P4M3D'); // +4 months +3 days
$date = new DateTime();
while($some_condition) {
$date->add($interval);
do_something();
}
You can do this by saving the values in variables like that :
$day=3;
$month=4;
echo date("d-m-y",strtotime('+'.$day .'days' .'+'.$month.'months'));
Explanation:
7(july)+4 months = 11 month(November)
8 july+ 3 days = 11 july
Output:
11-11-13
NOTE: just for the example I have put the values hard coded, You can make them dynamic.

Categories