so I'm trying to build a function that allows me to calculate the dates of the next x days of the week, for example, I want 5 dates from now (02/08/2022), of Tuesday and Thursday, so the output should be:
02/08/2022 - Tu
02/10/2022 - Th
02/15/2022 - Tu
02/17/2022 - Th
02/22/2022 - Tu
I'm not sure how to achieve that, since I want to be able to input various days of the week... Is there a way to do this? My current aproach is this one, but of couse, is not doing what I want, I kinda blocked right now, sorry if the answer is too obvious:
function getNextDates($start_date, $range_weeks, $how_many_dates, $days)
{
$range = 'P' . ($range_weeks * 7) . 'D';
$sd = new DateTimeImmutable($start_date);
$nd = new DateTime($start_date);
$dates_list = array();
$dates_list[0] = array('day' => $sd->format('D'), 'date' => $sd->format('d-m-Y'));
$ind = 1;
$how_many_dates--;
while ($how_many_dates > 0) {
// This in case I want a n weeks space
$nd->add(new DateInterval($range));
for ($i = 0; $i < count($days); $i++) {
if (($i + 1) < count($days)) {
if ($sd->modify($this->nextDay($days[$i])) < $sd->modify($this->nextDay($days[$i + 1]))) {
$nextDate = $nd->modify($this->nextDay($days[$i]));
$dates_list[$ind] = array('day' => $nextDate->format('D'), 'date' => $nextDate->format('d-m-Y'));
$how_many_dates--;
$ind++;
}
} else {
$nextDate = $nd->modify($this->nextDay($days[$i]));
// $sd = $nextDate;
$dates_list[$ind] = array('day' => $nextDate->format('D'), 'date' => $nextDate->format('d-m-Y'));
$how_many_dates--;
$ind++;
}
if ($how_many_dates <= 0) break;
}
}
return $dates_list;
}
Being:
$start_date: 02/08/2022
$range_weeks: 0 // Meaning every week
$how_many_dates: 5
$days: Tuesday, Thursday
I got:
02/08/2022
02/10/2022
02/17/2022
02/24/2022
03/03/2022
Sorry for my amateur code...
Starting from the start date, a day is always added and it is checked whether it is the desired day of the week.
$start = "02/08/2022";
$number = 5;
$weekDays = ['Tue','Thu'];
$dates = [];
for($date = date_create($start); $number > 0; $date->modify('+1 Day')){
$day = $date->format('D');
if(in_array($day,$weekDays)) {
$dates[] = $date->format("d/m/Y");
$number--;
}
}
Result $dates
array (
0 => "08/02/2022",
1 => "10/02/2022",
2 => "15/02/2022",
3 => "17/02/2022",
4 => "22/02/2022",
)
Related
This is a Laravel project that I'm attempting to get a random collection of dates between the two dates below. The random dates chosen need to be 6 years apart and be on either a Monday, Thursday or Sunday. I have the dates function below that works for another date range I need however with this situation there's the additional factor of 6 years so I need the additional modification for it and not sure what I need to do to account for it on this situation.
$start = Carbon::parse('First Monday of January 2000');
$nextMonth = Carbon::now()->addMonth();
collect([
'monday' => false,
'thursday' => false,
'sunday' => true
])->flatMap(function ($bool, $day) use ($start, $nextMonth) {
return dates($start, $nextMonth, $day, $bool);
})->sort(function ($a, $b) {
return strtotime($a) - strtotime($b);
})->values()->map(function ($date, $key) {
return factory(Event::class)->create([
'name' => 'Event ' . ($key + 1),
'date' => $date
]);
})->filter(function ($event) {
return $event->date->lt(Carbon::today());
function dates(Carbon $from, Carbon $to, $day, $last = false)
{
$step = $from->copy()->startOfMonth();
$modification = sprintf($last ? 'last %s of next month' : 'next %s', $day);
$dates = [];
while ($step->modify($modification)->lte($to)) {
if ($step->lt($from)) {
continue;
}
$dates[$step->timestamp] = $step->copy();
}
return $dates;
}
If the second date is exactly six years from the first random date:
$second_date = $first_date->diffInYears($first_date->copy()->addYears(6))
From Carbon docs: http://carbon.nesbot.com/docs/#api-difference
Update:
Here is a way of creating an array of dates, based on a start date, where each date is at least six years apart and either a Monday, Thursday or Sunday.
I've left the loop at 20 iterations so you can see the dates generated are different days over the years.
$start = Carbon::parse('First Monday of January 2000');
$dates = array();
for ($i = 1; $i < 20; $i ++)
{
$interval = $i * 6;
if ($start->copy()->addYears($interval)->dayOfWeek === Carbon::MONDAY OR $start->copy()->addYears($interval)->dayOfWeek === Carbon::THURSDAY OR $start->copy()->addYears($interval)->dayOfWeek === Carbon::SUNDAY)
{
$dates[] = $start->copy()->addYears($interval);
} else
{
$dates[] = $start->copy()->addYears($interval)->modify('next monday');
}
}
I'm trying to calculate the number of weekend days between dates from the array below:
$dates[] = array ( 'DateFrom' => '2015-07-10', 'DateTo' => '2015-07-10', 'DateFrom' => '2015-07-12', 'DateTo' => '2015-07-12', 'DateFrom'=> '2015-07-17', 'DateTo'=> '2015-07-19') ;
The result must return number of weekend days between these dates
Between these dates are 3 days of weekend (2015-07-12, 2015-07-18, and 2015-07-19).
Anyone have any idea?
You need to loop through from start date to end date and in each iteration need to check for day (sat/sun)
Algo :
$weekends = 0;
$startDate = strtotime($startDate);
$endDate = strtotime($endDate);
while($startDate<$endDate) {
//"N" gives ISO-8601 numeric representation of the day of the week (added in PHP 5.1.0)
$day = date("N",$startDate);
if($day == 6 || $day == 7) {
$weekends++;
}
$startDate = 24*60*60 ; //1 day
}
Firstly, if you're defining your array exactly as written, you're duplicating keys and four of those items will be overwritten. But assuming we're just looking at the pairs. Pass the FromDate and ToDate from each pair to this function and add up all the return values.
function getWeekends ($fromDate, $toDate) {
$from = strtotime($fromDate);
$to = strtotime($toDate);
$diff = floor(abs($to-$from)/(60*60*24)); // total days betwixt
$num = floor($diff/7) * 2; // number of weeks * 2
$fromNum = date("N", $from);
$toNum = date("N", $to);
if ($toNum < $fromNum)
$toNum += 7;
// get range of day numbers
$dayarr = range($fromNum, $toNum);
// check if there are any weekdays in that range
$num += count(array_intersect($dayarr, array(6, 7, 13)));
return $num;
}
There may be a more elegant solution.
To be used on each pair of dates:
function getWeekendDays($startDate, $endDate)
{
$weekendDays = array(6, 7);
$period = new DatePeriod(
new DateTime($startDate),
new DateInterval('P1D'),
new DateTime($endDate)
);
$weekendDaysCount = 0;
foreach ($period as $day) {
if (in_array($day->format('N'), $weekendDays)) {
$weekendDaysCount++;
}
}
return $weekendDaysCount;
}
This question already has answers here:
I have 2 dates in PHP, how can I run a foreach loop to go through all of those days?
(13 answers)
Closed 1 year ago.
I want to fill an array with values. The keys of this array should be readable dates in the format 'YEAR-MONTH-DAY'. Starting point is '2010-5-25'.
The process should abort on the current date. Obviously, all dates should be valid dates.
I thought about doing this loop. But it seems that PHP is not able to check the condition of more than one in a 'for' loop. It does not give me any warnings or errors, though.
for ($d = 25, $m = 5, $y = 2010,
$this_day = date('j'),
$this_month = date('n'),
$this_year = date('Y');
($y <= $this_year) && ($m <= $this_month) && ($d <= $this_day);
$d++)
{
$values[$y.'-'.$m.'-'.$d] = 0; //fill array
$d++;
if(!checkdate($m, $d, $y)){
$d = 1;
$m++;
if($m > 12) { $m = 1; $y++; }
}
}
Doing this with nested loops would be rather painful.
One solution would be to use integer times as keys and then convert them later in another loop into the readable dates.
Is there a more efficient way?
Here is code that does some error checking, for example, valid dates provided and start date cannot be bigger than end date:
function arrayKeyDates($start, $end='now') {
// can use DateTime::createFromFormat() instead
$startDate = new DateTime($start);
$endDate = new DateTime($end);
if ($startDate === false) {
// invalid start date.
return;
}
if ($endDate === false) {
// invalid end date.
return;
}
if ($startDate > $endDate) {
// start date cannot be greater than end date.
return;
}
$dates = array();
while($startDate <= $endDate) {
$dates[$startDate->format('Y-n-j')] = 0;
$startDate->modify('+1 day');
}
return $dates;
}
print_r(arrayKeyDate('2014-11-30'));
I get the following output:
Array
(
[2014-11-30] => 0
[2014-12-1] => 0
[2014-12-2] => 0
[2014-12-3] => 0
[2014-12-4] => 0
[2014-12-5] => 0
[2014-12-6] => 0
[2014-12-7] => 0
)
Error handling code is left to you.
UPDATE (DateTime::createFromFormat)
If you want to create the DateTime objects using a custom format you can, in my function, you can do something like this:
$startDate = DateTime::createFromFormat('Y-n-j', $start);
Where $start would have the value 2010-5-25.
For more information, see: http://php.net/manual/en/datetime.createfromformat.php
$startDate = new \DateTime('2010-05-25');
$endDate = new \DateTime();
$interval = new \DateInterval('P1D');
$period = new \DatePeriod ($startDate, $interval, $endDate);
$dates = array();
foreach ($period as $key => $date) {
$dates[$date->format('Y-m-d')] = null;
}
var_dump($dates);
Simply you can try using strtotime(). Example:
$values = array();
$oldDate = strtotime('2010-05-25');
while($oldDate <= time()){
$values[date('Y-m-d', $oldDate)] = 'Your value';
$oldDate += 86400;
//Other codes
}
I know this is an old question, but might be helpful for new viewers a shorter version
$dummyArray = array_fill(1, 7, 0);
$dates = array_flip(array_map(function($val, $idx) {
return date_create('2010-5-25')->modify('-' . $idx . ' days')->format('Y-m-d');
}, $dummyArray, array_keys($dummyArray)));
I'm basically generating a dummy array which is going to have the numbers of days I want to extract as index, and then converting those to dates with array_map, after which I just flip the array to have the dates as keys instead of values
I took the liberty to clean up your code a little to make it readable:
<?php
$this_day = date('j');
$this_month = date('n');
$this_year = date('Y');
echo sprintf("Today: d-m-y: %s-%s-%s\n", $this_day, $this_month, $this_year);
for ($d = 25, $m = 5, $y = 2010;
($y <= $this_year) && ($m <= $this_month) && ($d <= $this_day);
$d++) {
echo sprintf("Date: d-m-y: %s-%s-%s\n", $d, $m, $y);
$values[$y.'-'.$m.'-'.$d] = 0; //fill array
$d++;
if(!checkdate($m, $d, $y)){
$d = 1;
$m++;
if($m > 12) { $m = 1; $y++; }
}
}
This shows that the code works perfectly well. That is if you chose the correct condition!
Today is the 07th, but your initial values start with the 25th which falsifies the condition. To verify chose a start day of '02' and see the output...
I guess you want to re-check your condition. Most likely it is something else you want to express...
First of all; the loop doesn't execute because you are checking separately if year number is lower then current year number, etc. But today is the 7th, and you start at the 25th of may 2010:
$d = 25;
$this_day = date('j'); // today: 7
$loop = $d <= $this_day; // evaluates to false
Because the 'day-check' evaluates to false, the whole expression evaluates to false. So the loop will only start to run on december the 25th.
You can better use the DateTime object to construct the dates and perform modifications on the created object. This will also safe you a lot of sweat with stuff like leap years etc. Example:
for (
$start = new DateTime('2010-05-25'),
$today = new DateTime('now') ;
$start->diff($today)->format('%a') >= 0 ;
$start->modify('+1 day')
) {
$values[$start->format('Y-m-d')] = 0;
}
easy does it!
I have written a round robin tournament generator in PHP for an online electronic sports league, and I need to calculate the dates for each game in the tournament. Games are played every Thursday and Sunday over the course of many weeks (the number of weeks is dependent on how many teams are participating). Given the starting week and the number of weeks what would be the best way to calculate those dates?
I'm guessing it requires using some combination of DateTime, DateInterval, and DatePeriod; but I am having trouble figuring out how it would be done.
Update:
Apologies for not providing the code before. Here is the solution I had originally come up with. I didn't know if there was a simpler way of doing it. The function was called submitSchedule where the dates were generated.
<html>
<body>
<?php
function roundRobin($teams) {
$len = count($teams);
$schedule = array();
for ($i = 0; $i < $len - 1; $i++)
{
$home = array_slice($teams, 0, $len / 2);
$away = array_slice($teams, $len / 2);
$day = array();
for ($j = 0; $j < $len / 2; $j++)
{
array_push($day, array($home[$j], $away[$j]));
}
array_push($schedule, $day);
$temp = $away[0];
for ($j = 0; $j < count($away) - 1; $j++)
{
$away[$j] = $away[$j + 1];
}
$away[count($away) - 1] = $home[count($home) - 1];
for ($j = count($home) - 1; $j > 1; $j--)
{
$home[$j] = $home[$j - 1];
}
$home[1] = $temp;
$teams = array_merge($home, $away);
}
return $schedule;
}
function roundRobinBalanced($teams)
{
$schedule = roundRobin($teams);
for ($i = 1; $i < count($schedule); $i+=2)
{
$schedule[$i][0] = array_reverse($schedule[$i][0]);
}
return $schedule;
}
function doubleRoundRobinBalanced($teams)
{
$sched2 = roundRobinBalanced($teams);
for ($i = 0; $i < count($sched2); $i++)
{
$sched2[$i] = array_reverse($sched2[$i]);
}
return array_merge(roundRobinBalanced($teams), $sched2);
}
function tripleRoundRobinBalanced($teams)
{
return array_merge(doubleRoundRobinBalanced($teams), roundRobinBalanced($teams));
}
function submitSchedule($schedule, $start, $intervals, &$con)
{
mysqli_query($con, "TRUNCATE TABLE matches");
$curDate = $start;
echo "<pre>";
for ($i = 0; $i < count($schedule); $i++)
{
for ($j = 0; $j < count($schedule[$i]); $j++)
{
$temp0 = $schedule[$i][$j][0];
$temp1 = $schedule[$i][$j][1];
$temp2 = date_format($curDate, "Y-m-d");
mysqli_query($con,"INSERT INTO matches (T1ID, T2ID, gameDate) VALUES ('$temp0', '$temp1', '$temp2')");
echo "<span style=\"background:lightblue;\">( " . date_format(new DateTime(), "Y-m-d H:i:s") . " )</span>" . "> INSERT INTO matches (T1ID, T2ID, gameDate) VALUES (". $schedule[$i][$j][0] . ", " . $schedule[$i][$j][1] . ", \"" . date_format($curDate, "Y-m-d") . "\")<br>";
}
date_add($curDate, date_interval_create_from_date_string($intervals[$i % count($intervals)]));
}
echo "</pre>";
}
$teams = array();
$con=mysqli_connect("localhost:3306","root","REMOVED","schedule");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
//Select all items from the 'teams' table and order them first in descending order by points, then in ascending order by 'teamName'
$result = mysqli_query($con,"SELECT * FROM teams");
while($row = mysqli_fetch_array($result))
{
array_push($teams, $row['TID']);
}
if (count($teams) % 2 == 1)
{
array_push($teams, null);
}
shuffle($teams);
$schedule = tripleRoundRobinBalanced($teams);
// echo "<pre>" . json_encode($schedule, JSON_PRETTY_PRINT, JSON_FORCE_OBJECT) . "</pre>";
echo "<pre>";
print_r($schedule);
echo "</pre>";
// ---- List of possible DateTime expressions ----
// thursday
// next thursday
// YYYY-MM-DD
// DD/MM/yy
// thursday + 1 day
$start = new DateTime("thursday"); // Indicates the date of the first game
$jump = array("3 days", "4 days"); // Indicates the time intervals of each game (e.g. If day 1 starts on thursday, day 2 starts on sunday, day 3 starts on thursday, etc.)
submitSchedule($schedule, $start, $jump, $con);
mysqli_close($con);
?>
</body>
</html>
The way to achieve this is by using PHP's DateTime classes as you guessed. They are really very useful. I would suggest a little function something like this:-
/**
* #param $startWeek ISO week number of the first week
* #param $numWeeks The number of weeks to run including the first
*
* #return \DateTime[] An array of DateTime objects
*/
function getPlayDays($startWeek, $numWeeks)
{
$numWeeks --;
$result = [];
$currYear = (int)(new \DateTime())->format('Y');
$oneDay = new \DateInterval('P1D');
// Start on the first Thursday of the given week.
$startDate = (new \DateTime())->setISODate($currYear, $startWeek, 4);
$endDate = clone $startDate;
$endDate->add(new \DateInterval("P{$numWeeks}W"));
// End on the Sunday of the last week.
$endDate->setISODate((int)$endDate->format('o'), (int)$endDate->format('W'), 7);
$period = new \DatePeriod($startDate, $oneDay, $endDate->add($oneDay));
foreach($period as $day){
if(4 === (int)$day->format('N') || 7 === (int)$day->format('N') ){
$result[] = $day;
}
}
return $result;
}
foreach(getPlayDays(1, 3) as $playDay){
var_dump($playDay->format('D m-d-Y'));
}
You didn't specify how you would identify the starting week, so I have assumed an ISO week number.
Output:-
string 'Thu 01-02-2014' (length=14)
string 'Sun 01-05-2014' (length=14)
string 'Thu 01-09-2014' (length=14)
string 'Sun 01-12-2014' (length=14)
string 'Thu 01-16-2014' (length=14)
string 'Sun 01-19-2014' (length=14)
See it working.
DateTime manual.
This function will quite happily cope with DST changes, leap years and weeks close to the start and end of the year thanks to the built in magic of the DateTime classes :)
proof or STFU.
Have a look at the strtotime function:
http://www.php.net/manual/en/function.strtotime.php
You could do something like this:
$startDate = "May 15, 2014";
$startDate = strtotime($startDate);
And you could get the start of the Sunday match by simply adding three days:
$nextDate = strtotime("+3 day", $startDate);
Your question is a bit vague, but I think this is what you were asking.
Let's say you have the timestamps of the day of starting week (06:00 AM time)...
Every other date will be 7 days (86400 seconds * 7) in the future.
Let's assume you will run this for 52 weeks (1 year)
$firstThu = 1234567890;
$firstSun = 9876543210;
$nextThus = array();
$nextSuns = array();
for($i=0; $i<52; $i++) {
$nextThus[] = date('d/m/Y', $firstThu + (86400 * 7 * $i));
$nextSuns[] = date('d/m/Y', $firstSun + (86400 * 7 * $i));
}
At the end of the loop you will have two arrays with all the 52 weeks dates.
I would like to know if there is some easy way to identify seasons: spring, summer, autumn or winter. I'm have to generate a 'resume' and I'd like that if a period of working roughly fits a season (not exactly but with a +/-10 days error for example) it returns spring, summer, autumn or winter.
Like:
input: 25/06/2010, 30/09/2010
output: Summer 2010
(In Spain summer is between 21 July and 20 September)
input: 02/02/2009, 30/04/2010
output: 2009-2010
Any idea how to do this?
As you are looking for a season for a period I quickly wrote this function, it can be improve and may have some bugs but you have somewhere to start.
function season($period)
{
$seasons = array(
'spring' => array('March 21' , 'June 20'),
'summer' => array('June 21' , 'September 22'),
'fall' => array('September 23' , 'December 20'),
'winter' => array('December 21' , 'March 20')
);
$seasonsYear = array();
$start = strtotime($period[0]);
$end = strtotime($period[1]);
$seasonsYear[date('Y', $start)] = array();
if (key(current($seasonsYear)) != date('Y', $end))
$seasonsYear[date('Y', $end)] = array();
foreach ($seasonsYear as $year => &$seasonYear)
foreach ($seasons as $season => $period)
$seasonYear[$season] = array(strtotime($period[0].' '.$year), strtotime($period[1].' '.($season != 'winter' ? $year : ($year+1))));
foreach ($seasonsYear as $year => &$seasons) {
foreach ($seasons as $season => &$period) {
if ($start >= $period[0] && $end <= $period[1])
return ucFirst($season).' '.$year;
if ($start >= $period[0] && $start <= $period[1]) {
if (date('Y', $end) != $year)
$seasons = $seasonsYear[date('Y', $end)];
$year = date('Y', $end);
$nextSeason = key($seasons);
$nextPeriod = current($seasons);
do {
$findNext = ($end >= $nextPeriod[0] && $end <= $nextPeriod[1]);
$nextSeason = key($seasons);
$nextPeriod = current($seasons);
} while ($findNext = False);
$diffCurr = $period[1]-$start;
$diffNext = $end-$nextPeriod[0];
if ($diffCurr > $diffNext)
return ucFirst($season).' '.$year;
else {
return ucFirst($nextSeason).' '.$year;
}
}
}
}
}
echo season(array('07/20/2010', '08/20/2010'));
echo "\n";
echo season(array('06/25/2010', '09/30/2010'));
echo "\n";
echo season(array('08/25/2010', '11/30/2010'));
echo "\n";
echo season(array('12/21/2010', '01/01/2011'));
echo "\n";
echo season(array('12/21/2010', '03/25/2011'));
Result:
/*
Summer 2010
Summer 2010
Fall 2010
Winter 2010
Winter 2011
*/
And the except you want for "season year overflow":
if (date('Y', $end) != $year)
return $year.'-'.date('Y', $end);
Instead of:
if (date('Y', $end) != $year)
$seasons = $seasonsYear[date('Y', $end)];
$year = date('Y', $end);
Note: Winter is coming.
Looks like this guy got that function written already : http://biostall.com/get-the-current-season-using-php
Even has hemisphere support !
But this should do the trick :
<?php
function getSeason($date) {
$season_names = array('Winter', 'Spring', 'Summer', 'Fall');
if (strtotime($date) < strtotime($date_year.'-03-21') || strtotime($date) >= strtotime($date_year.'-12-21')) {
return $season_names[0]; // Must be in Winter
} elseif (strtotime($date) >= strtotime($date_year.'-09-23')) {
return $season_names[3]; // Must be in Fall
} elseif (strtotime($date) >= strtotime($date_year.'-06-21')) {
return $season_names[2]; // Must be in Summer
} elseif (strtotime($date) >= strtotime($date_year.'-03-21')) {
return $season_names[1]; // Must be in Spring
}
}
Here's a rough overview of what you need to do:
Work out when your season boundarys are going to be. This is a big, big job if you're going to do this for an international scope!
When presented with a date range, first work out exactly how many days of that range are in each season.
You want output like:
Range | Days | complete?
Su10 | 12 | 0
A10 | 90 | 1
W10 | 02 | 0
Once this is done you'll need to identify if a single season is 'complete', which should be 1 if the whole season is worked, within 10 days. If it is, choose that season. If more are complete, or none, return false.