I have a problem using PHP to get the finish date of course, as below:
Inputs:
Start date (e.g: 8/23/2019)
Schedule in week (e.g: Monday, Tuesday, Friday)
Total of lessons (e.g: 8)
Output: The finish date of course (is 9/9/2019 with above e.g inputs).
One lesson is one day. Input fields from end user:
Sorry for my bad English. Thank you very much!
As I see it loop while there are lessons left and subtract when it's Monday, Tuesday or Friday.
After the loop output the date.
$start = "8/23/2019";
$days = ["Monday", "Tuesday", "Friday"];
$n = 8;
$d = strtotime($start);
while($n>0){
//See if day is in days array
if(in_array(date("l", $d), $days)){
$n--;
}
$d += 86400; // go to next day
}
echo date("m/d/Y", $d-86400); //-86400 because the loop adds one at the end.
Try this code, if I understand you correctly))
<?
$startDay = '2019-09-25';
$aSchedule = array(1,2,4);
$iCntShed = count($aSchedule);
$iLessonsCnt = 8;
$iDWStartDay = date('w',strtotime($startDay));
$aDOWMap = array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
// first day according Schedule array and startDay
$aWeekDays = array_filter($aSchedule,function($iSDW) use ($iDWStartDay){
return $iSDW>= $iDWStartDay;
});
// day according of week
$nextDate = date('Y-m-d',strtotime($startDay.' next '.$aDOWMap[end($aWeekDays)]));
$i = 0;
while (count($aWeekDays)<$iLessonsCnt) {
$i = $i<$iCntShed ? $i : 0;
$aWeekDays[] = $aSchedule[$i];
$nextDate = date('Y-m-d',strtotime($nextDate.' next '.$aDOWMap[$aSchedule[$i++]]));
}
print_r($aWeekDays);
echo $nextDate;
Related
How to manipulate the date and exclude saturday and sunday?. The objective is, I need to create a cron job that will run and execute on datas that were created 5 days ago,"BUT", saturday and sunday shouldn't be included in that 5 days period.
here's what I have so far
$numdays = 5;
$today = strtotime('now');
$start = date('Y-m-d',strtotime('-'.$numdays.' day',$today));
echo $start;
if you try to run my code snippet above, it will show you the exact date 5 days ago 2016-02-10. But that one doesn't "exclude" saturday and sunday in the computation. it should be be 2016-02-08. So how to do that?
You can use PHP's date week of day, there are several versions, here is one using N:
<?php
$current = new DateTime();
$interval = new DateInterval('P1D');
$x = 5;
while ($x > 1) {
// Check if day of week is not saturday/sunday (1 => Monday ... 7 -> Sunday)
if ($current->format('N') >= 6) {
$x++;
}
$current->sub($interval);
$x--;
}
echo $current->format('Y-m-d') . PHP_EOL;
Example Run.
You can get a whole week and discard the weekends, keeping the furthest element in the array as a result.
$days = array_filter(array_map(function ($daysBack) {
$date = new \DateTimeImmutable("$daysBack days ago", new \DateTimeZone('UTC'));
return (!in_array($date->format('N'), [6, 7])) ? $date : null;
}, Range(1, 7)));
$fiveWorkingDaysAgo = end($days);
I am using the following lines to calculate a 4-week interval in PHP.
This uses a fixed date ($calStart) as basis for the calculation and ends the interval with the last day of the selected year ($rangeEnd / $selYear) which works well so far.
Example: If the selected year is 2015 than the first date in my range here should be 2015-01-16 as the first interval date in the selected year.
Can someone here tell me how I can set this so that $rangeDays only starts with the first interval date in the selected year instead of returning all intervals since the $calStart date (which is what it does at the moment) ?
$calStart = new DateTime('2014-01-17');
$interval = DateInterval::createFromDateString('4 weeks');
$rangeEnd = new DateTime($selYear . '-12-31');
$rangeDays = new DatePeriod($calStart, $interval, $rangeEnd);
Many thanks for any help with this, Tim.
You can try with:
$calStart = new \DateTime('2014-01-17');
$calYear = $calStart->format('Y');
if ($selYear !== $calYear) {
$day = (int) $calStart->format('z') + date('z', mktime(0,0,0,12,31,$calYear)) * ($selYear - $calYear);
$calStart->setDate($selYear, 1, $day % 28);
}
Edit:
This one is more complex:
$calStart = new \DateTime('2014-01-17');
$calYear = (int) $calStart->format('Y');
if ($selYear !== $calYear) {
$days = (int) $calStart->format('z') + 1; // get day in a year. +1 is because it starts with 0
for ($i = $calYear; $i < $selYear; $i++) {
$days -= (date('z', mktime(0,0,0,12,31,$i)) + 1) % 28; // remove from start day a modulo of 28 days, every year the date is lower
}
if ($days < 0) {
$days += 28; // if we will finish with value under 0, just add 4 weeks
}
$calStart->setDate($selYear, 1, $days);
}
If you're looking to get $calStart to start in the same year as $rangeEnd this should do it for you:
$calStart = new DateTime('2014-01-17');
if ($selYear !== $calStart->format('Y')) {
$calStart->setDate($selYear , $calStart->format('n'), $calStart->format('j'));
}
$interval = DateInterval::createFromDateString('4 weeks');
$rangeEnd = new DateTime($selYear . '-12-31');
$rangeDays = new DatePeriod($calStart, $interval, $rangeEnd);
I'm not sure how you get 2015-01-16 as the new start date so I wasn't able to address that directly.
I'm working with dates in php and I'm a little stumped
This is my code:
$day = 'Thursday';
$i = 0;
$o_date = new DateTime("2012-09-12 20:56:43 +18 hours");
$date = date_format($o_date, 'l');
$full = date_format($o_date, 'd-m-Y');
if($day!=$date) {
$date = new DateTime($date . " +1 days");
$i++;
}
$order_day = new DateTime($full . " +".$i." days");$order_day = date_format($order_day, 'D, d M');
return $order_day;
This is what I want it to do:
1) I've got a day in string format (eg. Thursday)
2) I've got an order date (eg. 2012-09-12 20:56:43)
I want to get that date and turn the day into a string (eg date_format($o_date, 'l'); so that will return Wednesday, I then want to count how many days until the next Thursday after the order date, which I've built an if loop for. I then want to get the original date and add the amount of days the $i has accumulated, then return the date in day format (eg. Thursday, 13 September, but for some reason my code isn't working. Can someone see what I've done wrong here?
Instead of a loop, why don't you use the numeric day and calculate:
$day = 'Thursday';
$day_names = array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
$day_num = array_search($day, $day_names);
$o_date = new DateTime("2012-09-12 20:56:43 +18 hours");
$o_day_num = $o_date->format('w');
$day_diff = ($day_num - $o_day_num) % 7;
if ($day_diff == 0) { $day_diff = 7; }
$order_day = clone $o_date;
$order_day->add(new DateInterval("P".$day_diff."D"));
So I actually like #Barmar's idea better, but this is what I came up with... there were a number of issues in your original code, including formatting of the initial date, and the fact that you are creating a new object each time instead of working with the same one.
If you choose to use a loop, and I'm not sure you should... try this:
<?php
$day = 'Thursday';
$i = 0;
// Order Date
$o_date = new DateTime("2012-09-12 20:56:43 +18");
$date = $o_date->format('l');
while($day!==$date) {
$date = $o_date->add(new DateInterval('P1D'))->format('l');
$i++;
}
$order_day = $o_date->format('D, d M');
I'm trying many approaches but then I get stuck half way.
Let's say order was created today. I need to display when the next recurring order will happen. So I have order created June 13, 2012. Then I have set the schedule to bimonthly recurring order, every 1st of month. How to calculate when the next recurring order will happen? The answer is August 1st.
If someone can outline an approach it would be very useful, it doesn't have to be code. This is what I have so far...
// first, get starting date
$start_date_month = date('m', strtotime($start_date));
// get this year
$this_year = date('Y');
// if this month is december, next month is january
$this_month = date('m', $timestamp_month);
if($this_month == 12){
$next_month = 1;
// if this month is not december add 1 to get next month
}else{
$next_month = $this_month + 1;
}
// get array of months where recurring orders will happen
$months = array();
for ($i=1; $i<=6; $i++) {
$add_month = $start_date_month+(2*$i); // 2, 4, 6, 8, 10, 12
if($add_month == 13){$add_month = 1;$year = $this_year+1;}
elseif($add_month == 14){$add_month = 2;$year = $this_year+1;}
elseif($add_month == 15){$add_month = 3;$year = $this_year+1;}
elseif($add_month == 16){$add_month = 4;$year = $this_year+1;}
elseif($add_month == 17){$add_month = 5;$year = $this_year+1;}
elseif($add_month == 18){$add_month = 6;$year = $this_year+1;}
elseif($add_month == 19){$add_month = 7;$year = $this_year+1;}
elseif($add_month == 20){$add_month = 8;$year = $this_year+1;}
else{$year = $this_year;}
echo $what_day.'-'.$add_month.'-'.$year.'<br />';
$months[] = $add_month;
}
echo '<pre>';
print_r($months);
echo '</pre>';
I don't want to simply find what's the date in two months from now. Let's say order created June 1. Next recurring order is August 1. Then let's say now, today is September 1st, but next recurring order is October 1st. See my dilemma?
Just take the current month, so since it's June, we get 6. 6 mod 2 == 0. Next month is July, we get 7. 7 mod 2 == 1.
So just check if current month % 2 == (first month % 2).
Then just check if it's the 1st of the month.
In PHP modulus is defined with the percentage symbol.
$month = date('n');
$createdMonth = 6;
if($month % 2 == $createdMonth % 2){
// stuff
}
You might find the library called When useful for this (I'm the author).
Here is code which will get you the next 2 recurring monthly dates (from todays date):
include 'When.php';
$r = new When();
$r->recur(new DateTime(), 'monthly')
->count(2)
->interval(2) // every other month
->bymonthday(array(1));
while($result = $r->next())
{
echo $result->format('c') . '<br />';
}
// output
// 2012-08-01T13:33:33-04:00
// 2012-10-01T13:33:33-04:00
Taking this a step further, you likely only want to find the 2 first business days:
include 'When.php';
$r = new When();
$r->recur(new DateTime(), 'monthly')
->count(2)
->interval(2) // every other month
->byday(array('MO', 'TU', 'WE', 'TH', 'FR')) // week days only
->bymonthday(array(1, 2, 3)) // the first weekday will fall on one of these days
->bysetpos(array(1)); // only return one per month
while($result = $r->next())
{
echo $result->format('c') . '<br />';
}
// output
// 2012-08-01T13:33:33-04:00
// 2012-10-01T13:33:33-04:00
Also note, the code is currently under a rewrite -- it works well but it is a little confusing and not well documented.
strtotime to the rescue:
<?php
date_default_timezone_set('Europe/London');
$d = new DateTime('2012-01-31');
$d->modify('first day of +2 months');
echo $d->format('r'), "\n";
?>
Let's say you want the next six orders:
$order_date = '6/13/2012';
$start = date('Y-m-01', strtotime($order_date));
$order_count = 6;
$future_orders = array();
$next = strtotime('+2 months', strtotime($start));
while(count($future_orders) < $order_count){
$future_orders[] = date('m/d/Y',$next);
$next = strtotime('+2 months', $next);
}
This can, obviously, be improved upon, but it should get you started ...
I got this:
$today = new DateTime();
$target_date = $today->modify("first day of +2 months");
echo "Your event is on " . $target_date->format("d/m/Y") . "!";
How do I go about getting all the work days (mon-fri) in a given time period (let's say, today till the end of the next month) ?
If you're using PHP 5.2+ you can use the library I wrote in order to handle date recursion in PHP called When.
With the library, the code would be something like:
$r = new When();
$r->recur(<start date here>, 'weekly')
->until(<end date here>)
->wkst('SU')
->byday(array('MO', 'TU', 'WE', 'TH', 'FR'));
while($result = $r->next())
{
echo $result->format('c') . '<br />';
}
This sample does exactly what you need, in an quick and efficient way.
It doesn't do nested loops and uses the totally awesome DateTime object.
$oDateTime = new DateTime();
$oDayIncrease = new DateInterval("P1D");
$aWeekDays = array();
$sStart = $oDateTime->format("m-Y");
while($oDateTime->format("m-Y") == $sStart) {
$iDayInWeek = $oDateTime->format("w");
if ($iDayInWeek > 0 && $iDayInWeek < 6) {
$aWeekDays[] = clone $oDateTime;
}
$oDateTime->add($oDayIncrease);
}
Try it here: http://codepad.org/wuAyAqnF
To use it, simply pass a timestamp to get_weekdays. You'll get back an array of all the weekdays, as timestamps, for the rest of the current month. Optionally, you can pass a $to argument - you will get all weekdays between $from and $to.
function get_weekdays ($from, $to=false) {
if ($to == false)
$to = last_day_of_month($from);
$days = array();
for ($x = $from; $x < $to; $x+=86400 ) {
if (date('w', $x) > 0 && date('w', $x) < 6)
$days[] = $x;
}
return $days;
}
function last_day_of_month($ts=false) {
$m = date('m', $ts);
$y = date('y', $ts);
return mktime(23, 59, 59, ($m+1), 0, $y);
}
I suppose you could loop through the dates and check the day for each one, and increment a counter.
Can't think of anything else off the top of my head.
Pseudocode coming your way:
Calculate the number of days between now and the last day of the month
Get the current day of the week (i.e. Wednesday)
Based on the current day of the week, and the number of days left in the month, it's simple calculation to figure out how many weekend days are left in the month - it's going to be the number of days remaining in the month, minus the number of Sundays/Saturdays left in the month.
I would write a function, something like:
daysLeftInMonth(daysLeftInMonth, startingDayOfWeek, dayOfWeekToCalculate)
where:
daysLeftInMonth is last day of the month (30), minus the current date (15)
startingDayOfWeek is the day of the week you want to start on (for today it would be Wednesday)
dayOfWeekToCalculate is the day of the week you want to count, e.g. Saturday or Sunday. June 2011 currently has 2 Sunday, and 2 Saturdays left 'til the end of the month
So, your algorithm becomes something like:
getWeekdaysLeft(todaysDate)
...getWeekdaysLeft is something like:
sundaysLeft = daysLeftInMonth(lastDayOfMonth - todaysDate, "Wednesday", "Sunday");
saturdaysLeft = daysLeftInMonth(lastDayOfMonth - todaysDate, "Wednesday", "Saturday");
return ((lastDayOfMonth - todaysDate) - (sundaysLeft + saturdaysLeft));
This code does at least one part you ask for. Instead of "end of next month" it simply works with a given number of days.
$dfrom = time();
$fourweeks = 7 * 4;
for ($i = 0; $i < $fourweeks; $i ++) {
$stamp = $dfrom + ($i * 24 * 60 * 60);
$weekday = date("D", $stamp);
if (in_array($weekday, array("Mon", "Tue", "Wed", "Thu", "Fri"))) {
print date(DATE_RSS, $stamp) . "\n";
}
}
// Find today's day of the month (i.e. 15)
$today = intval(date('d'));
// Define the array that will hold the work days.
$work_days = array()
// Find this month's last day. (i.e. 30)
$last = intval(date('d', strtotime('last day of this month')));
// Loop through all of the days between today and the last day of the month (i.e. 15 through 30)
for ( $i = $today; $i <= $last; $i++ )
{
// Create a timestamp.
$timestamp = mktime(null, null, null, null, $i);
// If the day of the week is greater than Sunday (0) but less than Saturday (6), add the timestamp to an array.
if ( intval(date('w', $timestamp)) > 0 && intval(date('w', $timestamp)) < 6 )
$work_days[] = mktime($timestamp);
}
The $work_days array will contain timestamps which you could use this way:
echo date('Y-m-d', $work_days[0]);
The code above with work in PHP 4 as well as PHP 5. It does not rely on the functionality of the DateTime class which was not available until PHP 5.2 and does not require the use of "libraries" created by other people.