(PHP) Интервал дней между датами

Berkut
Дата: 21.09.2006 13:18:47
Задача: На сервере ведется статистика за последние 30 дней (может задаваться динамически).

Требуется построить график. По шкале X номера дней от текущего дня - 30 дней до текущего дня.

Перерыл весь ман в поисках функции для получения интервала дней между двумя датами. К сожалению, не нашел.

Самописный вариант получился немного громоздкий.
Может существуют другие способы более изящного решения?

Вот мой вариант:

метод класса:
/**
 * Возвращает список дней между интервалами дат $low_date и $high_date.
 *
 * @param   integer         $low_date           нижния граница отрезка
 * @param   integer         $high_date          верхняя граница отрезка
 * @return  array
 */
    function daysInterval($low_date, $high_date)
    {
        $days = array();
        
        $low_day = date('d', $low_date);
        $high_day = date('d', $high_date);
        
        $low_month = date('m', $low_date);
        $high_month = date('m', $high_date);

        $low_year = date('Y', $low_date);
        $high_year = date('Y', $high_date);
        
        // границы относятся к разным годам
        if ($low_year<$high_year)
        {
            for ($y=$low_year; $y<=$high_year; $y++)
            {
                // год совпадает с годом нижней границы
                if ($y==$low_year)
                {
                    for($m=$low_month; $m<=12; $m++)
                    {
                        if ($m==$low_month)
                        {
                            $h_range = $this->daysInMonth(mktime(0,0,0, $m, 1, $y));
                            $l_range = $low_day;
                        }
                        else 
                        {
                            $h_range = $this->daysInMonth(mktime(0,0,0, $m, 1, $y));
                            $l_range = 1;
                        }
                    
                        $days = array_merge($days, range($l_range, $h_range));                    
                    }
                }
                // год совпадает с годом верхней границы
                elseif ($y==$high_year)
                {
                    for($m=1; $m<=$high_month; $m++)
                    {
                        if ($m==$high_month)
                        {
                            $h_range = $high_day;
                            $l_range = 1;
                        }
                        else 
                        {
                            $h_range = $this->daysInMonth(mktime(0,0,0, $m, 1, $y));
                            $l_range = 1;
                        }
                    
                        $days = array_merge($days, range($l_range, $h_range));                    
                    }
                }
                // год находится в промежутке между границами
                else
                {
                    for($m=1; $m<=12; $m++)
                    {
                        $h_range = $this->daysInMonth(mktime(0,0,0, $m, 1, $y));
                        $l_range = 1;
                    
                        $days = array_merge($days, range($l_range, $h_range));                    
                    }
                }
            }
        }
        // интервальные точки относятся к одному году, но в разных месяцах
        elseif ($low_month<$high_month)
        {
            for ($m=$low_month; $m<=$high_month; $m++)
            {
                if ($m==$low_month)
                {
                    $h_range = $this->daysInMonth(mktime(0,0,0, $m, 1, $high_year));
                    $l_range = $low_day;
                }
                elseif ($m==$high_month)
                {
                    $h_range = $high_day;
                    $l_range = 1;
                }
                else 
                {
                    $h_range = $this->daysInMonth(mktime(0,0,0, $m, 1, $high_year));
                    $l_range = 1;
                }
                $days = array_merge($days, range($l_range, $h_range));
            }
        }
        // границы принадлежат одному месяцу
        else 
        {
            $days = range($low_day, $high_day);
        }
        
        return $days;
    }

daysInMonth() - возвращает количество дней в месяце.

тестовый скрипт:
<?php
require_once $_SERVER['DOCUMENT_ROOT'].'/classes/cdate.class.php';

$d = new CDate;

$now = time();

echo "Границы в одном месяце:<br>\n";
echo '<pre>'.print_r($d->daysInterval(mktime(0,0,0, date('m', $now), date('d', $now)-20, date('Y', $now)), $now), true).'</pre>';

$now = mktime(0,0,0, 9, 10, 2006);

echo "Границы в разных месяцах:<br>\n";
echo '<pre>'.print_r($d->daysInterval(mktime(0,0,0, date('m', $now), date('d', $now)-20, date('Y', $now)), $now), true).'</pre>';

$now = mktime(0,0,0, 1, 10, 2006);

echo "Границы в разных годах:<br>\n";
echo '<pre>'.print_r($d->daysInterval(mktime(0,0,0, date('m', $now), date('d', $now)-20, date('Y', $now)), $now), true).'</pre>';

$now = mktime(0,0,0, 3, 10, 2004);

echo "Високосный год:<br>\n";
echo '<pre>'.print_r($d->daysInterval(mktime(0,0,0, date('m', $now), date('d', $now)-20, date('Y', $now)), $now), true).'</pre>';
?>

результат работы:

Границы в одном месяце:<br>
<pre>Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
[5] => 6
[6] => 7
[7] => 8
[8] => 9
[9] => 10
[10] => 11
[11] => 12
[12] => 13
[13] => 14
[14] => 15
[15] => 16
[16] => 17
[17] => 18
[18] => 19
[19] => 20
[20] => 21
)
</pre>Границы в разных месяцах:<br>
<pre>Array
(
[0] => 21
[1] => 22
[2] => 23
[3] => 24
[4] => 25
[5] => 26
[6] => 27
[7] => 28
[8] => 29
[9] => 30
[10] => 31
[11] => 1
[12] => 2
[13] => 3
[14] => 4
[15] => 5
[16] => 6
[17] => 7
[18] => 8
[19] => 9
[20] => 10
)
</pre>Границы в разных годах:<br>
<pre>Array
(
[0] => 21
[1] => 22
[2] => 23
[3] => 24
[4] => 25
[5] => 26
[6] => 27
[7] => 28
[8] => 29
[9] => 30
[10] => 31
[11] => 1
[12] => 2
[13] => 3
[14] => 4
[15] => 5
[16] => 6
[17] => 7
[18] => 8
[19] => 9
[20] => 10
)
</pre>Високосный год:<br>
<pre>Array
(
[0] => 19
[1] => 20
[2] => 21
[3] => 22
[4] => 23
[5] => 24
[6] => 25
[7] => 26
[8] => 27
[9] => 28
[10] => 29
[11] => 1
[12] => 2
[13] => 3
[14] => 4
[15] => 5
[16] => 6
[17] => 7
[18] => 8
[19] => 9
[20] => 10
)
</pre>

P.S. Сори, за многабукф
DocAl
Дата: 21.09.2006 13:29:56
А данные не в базе?
Berkut
Дата: 21.09.2006 13:37:12
Вообще-то да.

Кажется, я догадываюсь, о чем вы :)
4m@t!c
Дата: 21.09.2006 13:37:25
Осторожно! Лисапедист!
function myinterval($a, $b)
{
   $d = 60*60*24;
   $a1 = $a;
   $arr = array();
   for ($a1; $a1<=$b; $a1=$a1+$d)
   {
      $arr[] = date('d', $a1);
   }
   return $arr;
}
----------------------------------------
Артисты не приехали, приехали цыгане
Berkut
Дата: 21.09.2006 13:38:34
Единственное, что ... в самой базе могут быть "точки разрыва", т.е. нет данных за день.
Berkut
Дата: 21.09.2006 13:40:11
4m@t!c
Осторожно! Лисапедист!

Вот уж точно "лисапедист". :)

Спасибо, 4m@t!c!
4m@t!c
Дата: 21.09.2006 13:46:25
Я бы не исключал варианта решения задачи средствами СУБД. Самому стало интересно, как "красиво" решить задачу на SQL.
----------------------------------------
Артисты не приехали, приехали цыгане
Berkut
Дата: 21.09.2006 13:52:06
4m@t!c
Я бы не исключал варианта решения задачи средствами СУБД. Самому стало интересно, как "красиво" решить задачу на SQL.


Угу. Уточню, что в бд данные за день собираются ежечасно.

Навскидку, сначала выделить timestamp для дней, месяцев, а затем соотвественно сгруппировать.

P.S. 4m@t!c, вот уж меня понесло не в ту степь.
Когда писал метод, чувствовал, что можно гораздо проще.
4m@t!c
Дата: 21.09.2006 13:59:24
Berkut
P.S. 4m@t!c, вот уж меня понесло не в ту степь.

Бывает. Надеюсь, что далеко не унесло и это единственный лисапедный метод класса ;)))))

Найдете решение средствами СУБД, запостите на форум, пожалуйста.
----------------------------------------
Артисты не приехали, приехали цыгане
®B!N
Дата: 21.09.2006 14:09:37
код надо отшлифовать, потому что тут же его и придумал :-) но идея думаю ясна...

$low_dateInfo = getdate($low_date);
$high_dateInfo = getdate($high_date);
$current_date = mktime(0,0,0,$low_dateInfo['mon'],$low_dateInfo['mday'],$low_dateInfo['year']);
$current_dateInfo = getdate($current_date);
$count=0;

do
{
$current_date = mktime(0,0,0,$current_dateInfo['mon'],$current_dateInfo['mday']+1,$current_dateInfo['year']);
$current_dateInfo = getdate($current_date);
$arrdays[$count]=$current_dateInfo['mday'];
$count+=1;
}while(mktime(0,0,0,$current_dateInfo['mon'],$current_dateInfo['mday'],$current_dateInfo['year'])<mktime(0,0,0,$high_dateInfo['mon'],$high_dateInfo['mday'],$high_dateInfo['year']))