8

I want to calculate all Sunday's between given two dates. I tried following code. It works fine if days are less but if i enter more days. It keeps processing and Maximum execution time exceeds i changed the time but it even keeps processing even execution time is 200sec.

code is

<?php
$one="2013-01-01";
$two="2013-02-30";

$no=0;
for($i=$one;$i<=$two;$i++)
{

    $day=date("N",strtotime($i));
    if($day==7)
    {
    $no++;
    }
}
echo $no;

?>

please help.

Ritesh Kumar Gupta
  • 5,055
  • 7
  • 45
  • 71
Daman Mokha
  • 372
  • 1
  • 3
  • 16

3 Answers3

35

John Conde's answer is correct, but here is a more efficient and mathy solution:

$start = new DateTime('2013-01-06');
$end = new DateTime('2013-01-20');
$days = $start->diff($end, true)->days;

$sundays = intval($days / 7) + ($start->format('N') + $days % 7 >= 7);

echo $sundays;

Let me break it down for you.

$start = new DateTime('2013-01-06');
$end = new DateTime('2013-01-20');

First, create some DateTime objects, which are powerful built-in PHP objects meant for exactly this kind of problem.

$days = $start->diff($end, true)->days;

Next, use DateTime::diff to find the difference from $start to $end (passing true here as the second parameter ensures that this value is always positive), and get the number of days between them.

$sundays = intval($days / 7) + ($start->format('N') + $days % 7 >= 7);

Here comes the big one - but it's not so complicated, really. First, we know there is one Sunday for every week, so we have at least $days / 7 Sundays to begin with, rounded down to the nearest int with intval.

On top of that, there could be a Sunday in a span of time less than a week; for example, Friday to Monday of the next week contains 4 days; one of them is a Sunday. So, depending on when we start and end, there could be another. This is easy to account for:

  • $start->format('N') (see DateTime::format) gives us the ISO-8601 day of the week for the start date, which is a number from 1 to 7 (1 is Monday, 7 is Sunday).
  • $days % 7 gives us the number of leftover days that don't divide evenly into weeks.

If our starting day and the number of leftover days add up to 7 or more, then we reached a Sunday. Knowing that, we just have to add that expression, which will give us 1 if it's true or 0 if it's false, since we're adding it to an int value.

And there you have it! The advantage of this method is that it doesn't require iterating over every day between the given times and checking to see if it's a Sunday, which will save you a lot computation, and also it will make you look really clever. Hope that helps!

Quinn Strahl
  • 1,718
  • 1
  • 16
  • 22
  • 2
    This part isn't true `Since $days and 7 are both int values, this will always be rounded down to the nearest int`. You have to use a round function like [floor()](http://www.php.net/manual/en/function.floor.php) to round it, PHP won't do it for you, try for example `$end = new DateTime('2013-02-20');` and the output will be decimal. – HamZa Apr 08 '13 at 20:52
  • 2
    You're absolutely right, whoops! Forgot I was talking about PHP for a second. – Quinn Strahl Apr 08 '13 at 20:59
  • @QuinnStrahl If want to find first occurrence of any day(lets say sunday) so how to find that? – Er.KT Sep 18 '14 at 10:22
  • 2
    @Er.KT The algorithm works for any day except for this last part: `If our starting day and the number of leftover days add up to 7 or more, then we reached a Sunday.` You should only have to change this expression: `$start->format('N') + $days % 7 >= 7` to something like `$start->format('N') + $days % 7 >= $ISO8601NumberForDesiredDayOfWeek`. As mentioned above, the ISO-8601 number for a day of the week is a number between 1 and 7, with Monday being 1 and Sunday being 7. – Quinn Strahl Sep 18 '14 at 19:52
  • @QuinnStrahl thanks for your answer,but i cant understand it, what i want that want to add task on every sunday from date 1 to 31 of any month,so how to get date of every sunday ? – Er.KT Sep 19 '14 at 06:39
  • @Er.KT That's a totally unrelated question. If you make a new question, I'd be happy to answer it there. – Quinn Strahl Sep 23 '14 at 18:49
  • @QuinnStrahl posted http://stackoverflow.com/questions/26008979/get-date-of-all-sunday-between-two-date-php-mysql – Er.KT Sep 24 '14 at 05:07
  • 4
    Hi,thanks for the solution.Can you make it dynamic like if I want to know how many Mondays are there then what to do. – Tanvir Feb 03 '15 at 15:58
  • Thanks for the solution. – Jerald Nov 01 '16 at 10:04
  • I used this method, but on the date 2018-06-18 to 2018-07-07 +1 day (1 day added to count both starting and ending day = 20 days) it calculates 17 days exclusive any sunday in between. It should be 18. Solution was to use `$days % 7 > 7` instead of `$days % 7 >= 7`. – Christian Apr 27 '18 at 09:00
10
<?php
$no = 0;
$start = new DateTime('2013-01-01');
$end   = new DateTime('2013-04-30');
$interval = DateInterval::createFromDateString('1 day');
$period = new DatePeriod($start, $interval, $end);
foreach ($period as $dt)
{
    if ($dt->format('N') == 7)
    {
        $no++;
    }
}
echo $no;

See it in action

John Conde
  • 217,595
  • 99
  • 455
  • 496
  • thank you. it works. so it was not working because string was not incremented properly? – Daman Mokha Apr 08 '13 at 18:31
  • Basically yes. Working with strings, especially dates, is best left to functionality designed to do that. DateTime is designed specifically to work with dates and as you can see makes it easy to do. – John Conde Apr 08 '13 at 18:33
2

Here is a solution if you want the Sundays in a specific date range.

function dateRange($begin, $end, $interval = null)
{
  $begin = new DateTime($begin);
  $end = new DateTime($end);

  $end = $end->modify('+1 day');
  $interval = new DateInterval($interval ? $interval : 'P1D');

  return iterator_to_array(new DatePeriod($begin, $interval, $end));
}

/* define date range */
$dates = dateRange('2018-03-01', '2018-03-31');

/* define weekdays */
$weekends = array_filter($dates, function ($date) {
  $day = $date->format("N");
  return $day === '6' || $day === '7';
});

/* weekdays output */
foreach ($weekends as $date) {
  echo $date->format("D Y-m-d") . "</br>";
}

/* define sundays */
$sundays = array_filter($dates, function ($date) {
  return $date->format("N") === '7';
});

/* sundays output */
foreach ($sundays as $date) {
echo $date->format("D Y-m-d") . "</br>";
}

/* define mondays */
$mondays = array_filter($dates, function ($date) {
 return $date->format("N") === '1';
});

/* mondays output */
foreach ($mondays as $date) {
echo $date->format("D Y-m-d") . "</br>";
}

Just change the number for any days you want in your output:

Monday = 1
Tuesday = 2
Wednesday = 3
Thursday = 4
Friday = 5
Saturday = 6
Sunday = 7
Lingo
  • 580
  • 2
  • 7
  • 26