One more
<?php
$week_day = 1; // The desired week day (sunday: 1, monday:2 .... saturday: 0)
$year = '2016'; // Year, 4 digits
$rs = array();
$month = 1;
while ($month <= 12) {
$day = 1;
$date = new DateTime($year.'-'.$month.'-'.$day);
$day_last = $date->format('t');
$i = 1;
while ($day <= $day_last) {
$date->add(new DateInterval('P'.$i.'D'));
//$date->add(date_interval_create_from_date_string($i.' days'));
$day_week = $date->format('w');
if ($day_week == $week_day) {
$rs[$month][] = $day;
$i = 7;
}
$day += $i;
}
$month++;
}
echo '<pre>';
print_r($rs);
echo '</pre>';
Maintains a single instance of DateTime()
for each month instead of creating an instance for each day of each month.
Do not go every day of every month.
When identifying the first "Sunday", the counter jumps from 7 to 7, bringing a considerable gain of perfomance than checking day by day. That is, instead of doing 365/366 iterations, it only makes something around 52.
The routine is reusable. Just change the code of the day of the week and the year to get results for other days or years.
1: Domingo
2: Segunda
3: Terça
4: Quarta
5: Quinta
6: Sexta
0: Sábado
Return is a multidimensional array. For the year 2016, this is the result:
Array
(
[1] => Array
(
[0] => 3
[1] => 10
[2] => 17
[3] => 24
[4] => 31
)
[2] => Array
(
[0] => 7
[1] => 14
[2] => 21
[3] => 28
)
[3] => Array
(
[0] => 6
[1] => 13
[2] => 20
[3] => 27
)
[4] => Array
(
[0] => 3
[1] => 10
[2] => 17
[3] => 24
)
[5] => Array
(
[0] => 1
[1] => 8
[2] => 15
[3] => 22
[4] => 29
)
[6] => Array
(
[0] => 5
[1] => 12
[2] => 19
[3] => 26
)
[7] => Array
(
[0] => 3
[1] => 10
[2] => 17
[3] => 24
[4] => 31
)
[8] => Array
(
[0] => 7
[1] => 14
[2] => 21
[3] => 28
)
[9] => Array
(
[0] => 4
[1] => 11
[2] => 18
[3] => 25
)
[10] => Array
(
[0] => 2
[1] => 9
[2] => 16
[3] => 23
[4] => 30
)
[11] => Array
(
[0] => 6
[1] => 13
[2] => 20
[3] => 27
)
[12] => Array
(
[0] => 4
[1] => 11
[2] => 18
[3] => 25
)
)
Just iterate through the result to extract what you need.
I preferred to skip this part because iterating array()
is very basic.
Another relevant point is that the routine will still work even after the year 2038.
Of course in 2038 will be rare or null anyone using 32 bit systems. The concern is with the current age. Many still use 32-bit systems today and may want to do operations with dates after 2038 under environments in those environments, which may return erroneous results.
One more detail, regardless of the solution you choose, save the result to a static file or even to a database because if you need to run again you do not have to run it all over again. If executed once, the second time onwards is redundant since it will always yield the same result. This is a question of performance and optimization. Do your best.
Optimized version
This new optimized version runs 0.21 milliseconds. The previous runs in 0.245 milliseconds. It also consumes less memory, saving about 458 bytes from the original
It follows the same logic as the original with the difference that only one instance of Datetime()
is now created.
What kills performance a little is having to invoke $date->format('m')
and $date->format('d')
to get the month and day. Which are optional because the question does not ask for this. But I preferred to show a more detailed result telling you which days were found on the day of the week, in the case on Sunday.
The repetition limit is the total number of days in the year. The total is 366 for leap year and 365 for non-leap year.
When finding the first "Sunday", the increments are every 7 days, thus preventing the loop from looping 365 or 366 times. In this scheme, we have only 54 iterations.
$week_day = 0; // The desired week day (sunday: 1, monday:2 .... saturday: 0)
$year = '2016'; // Year, 4 digits
$rs = array();
$days = (((($year % 4) == 0) && ((($year % 100) != 0) || (($year %400) == 0))))? 366: 365;
$date = new DateTime(($year - 1).'-12-31');
$day = 1;
$i = 1;
while ($day <= $days) {
$date->add(new DateInterval('P'.$i.'D'));
//$date->add(date_interval_create_from_date_string($i.' days'));
$day_week = (int)$date->format('w');
if ($day_week == $week_day) {
$rs[(int)$date->format('m')][] = (int)$date->format('d');
$i = 7;
}
$day += $i;
}
echo '<pre>';
print_r($rs);
echo '</pre>';
I also made a change in both versions in the following snippet
$date->add(new DateInterval('P'.$i.'D'));
//$date->add(date_interval_create_from_date_string($i.' days'));
The original version was built with PHP running under Android. In this PHP does not have class DateInterval()
even being version 7. So I used date_interval_create_from_date_string()
.
Now I did using a Windows Pc and I was able to optimize more by switching back to new DateInterval()
because date_interval_create_from_date_string()
is an alias of it. Alias (shortcut) functions are always slower.