'PHP -> Next nearest date defined by array of days in week
please, really need help with this problem.
I have array of offerDays - $offerDays = array(1,6);
(mean Monday, and Saturday);
For example, input date is Wednesday 2014/09/03. Next available date for offers is Saturday 2014/09/06
Question: How I can in php determine nearest next day? Something like
$offerDays = array(1,6);
$inputDate = '2014/09/03'; // filled date, not actual date
$offerDate = findDate($inputDate, $offerDays); // returns 2014/09/06
function findDate($inputDate, $offerDays) {
return 'nearest next date to $inputDate defined by $offerDays';
}
Solution 1:[1]
Here you can have a variable array for the offerdays.
$offerDays = array(1,6);
$inputDate = '2014/09/04'; // filled date, not actual date
$offerDate = findDate($inputDate, $offerDays);
function findDate($inputDate, $offerDays) {
$date = DateTime::createFromFormat('Y/m/d', $inputDate);
$num = $date->format('w');
$min = 10; //initialize minimum days
foreach($offerDays as $o){ //loop through all the offerdays to find the minimum difference
$dif = $o - $num;
if($dif>0 && $dif < $min){
$min = $dif ;
}
}
// if minimum days is not changed then get the first offerday from next week
if($min == 10){
$min = 6 - $num + min($offerDays);
}
//add the days till next offerday
$add = new DateInterval('P'.$min.'D');
$nextOfferDay = $date->add($add)->format('Y/m/d');
return $nextOfferDay;
}
Solution 2:[2]
$currentDate = date('Y-m-d');
$frequencyDays = [2, 4, 5];
function nextNearestDate($currentDate, $frequencyDays) {
$weekDay = date('w', strtotime($currentDate));
$min = 10; // Some random number>7
foreach ($frequencyDays as $day) {
$dif = $day - $weekDay;
if ($dif == 0) {
$min = $dif;
break;
}else if ($dif > 0) {
$positiveDay[] = $dif;
}else if ($dif < 0) {
$negativeDay[] = $dif;
}
}
if ($min != 0) {
if (isset($positiveDay) && count($positiveDay)) {
$min = min($positiveDay);
} else {
$min = min($negativeDay);
$min = 7 + $min;
}
}
return $newDay = date('Y-m-d', strtotime('+' . $min . ' days', strtotime($currentDate)));
}
Solution 3:[3]
$date_str="2014-09-03";
if(date('l' , $date_str) != 'Saturday')
{
//Your Code Here
}
Solution 4:[4]
Simple Version
<?php
/**
* Return the next date that falls on one of the specified weekdays.
*
* @param DateTimeInterface $inputDate The search begins on the day after this
* @param array $weekdays An array of permitted weekdays
* represented by an integer (0=Sunday)
*
* @return DateTimeInterface|null The first date that meets the criteria
*/
function findDate(DateTimeInterface $inputDate, array $weekdays)
{
$weekdays = array_flip($weekdays);
foreach (new DatePeriod($inputDate, new DateInterval('P1D'), 7, DatePeriod::EXCLUDE_START_DATE) as $possibleDate)
{
if (isset($weekdays[$possibleDate->format('w')]) === true)
{
return $possibleDate;
}
}
}
// Example:
$weekdays = [1, 6];
$inputDate = '2014/09/03';
$offerDate = findDate(new DateTimeImmutable($inputDate), $weekdays);
echo $offerDate->format('Y-m-d'), "\n";
Extended Version
If you want to address this by building a slightly more abstract foundation, it may look like:
<?php
function WeekDayIterator(DateTimeInterface $inputDate, array $weekdays)
{
$weekdays = array_intersect($weekdays, range(0,6));
if (!$weekdays)
{
return;
}
$possibleDate = new DateTimeImmutable('@' . $inputDate->getTimeStamp());
$oneDay = new DateInterval('P1D');
$weekdays = array_flip($weekdays);
while (true)
{
$possibleDate = $possibleDate->add($oneDay);
if (isset($weekdays[$possibleDate->format('w')]) === true)
{
yield $possibleDate;
}
}
}
function findDate(DateTimeInterface $inputDate, array $weekdays)
{
return WeekDayIterator($inputDate, $weekdays)->current();
}
// Example:
$weekdays = [1, 6];
$inputDate = '2014/09/03';
$offerDate = findDate(new DateTimeImmutable($inputDate), $weekdays);
echo $offerDate->format('Y-m-d'), "\n";
foreach (new LimitIterator(WeekDayIterator(new DateTimeImmutable($inputDate), $weekdays), 0, 10) as $date)
{
echo $date->format('Y-m-d (w)'), "\n";
}
This longer version gives you an iterator that will infinitely loop over all valid dates. The findDate
method now just becomes a convenience method to hide away the implementation details.
But the advantage here is that you can now solve similar problems without having to write more plumbing. For example, you can compute the next 10 dates with a LimitIterator
. (Maybe you want to provide a drop down of available dates, etc.)
This second version is heavily dependent on 5.5, but the first version is not (if you replace the DateTimeImmutable
with DateTime
).
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | TeeDeJee |
Solution 2 | |
Solution 3 | Tristup |
Solution 4 |