Когда вы работаете с блогом или сайтом, на котором публикуются какие-либо события, определение ближайшего события — задача, которая может у вас возникнуть.
Предположим, что у нас есть список публикаций(далее постов), среди которых необходимо определить ту, которая уже опубликована или только планируется к публикации. Пусть формат списка постов будет следующий:
1 2 3 4 5 6 7 8 | Array ( [X] => Array ( [ID] => X [post_date] => YYYY-mm-dd HH:ii:ss ) ) |
На самом деле, данный формат соответствует формату постов в WordPress и это не случайно, так как задача по поиску ближайшего события возникла в рамках работы с сайтом на WordPress.
Чтобы не сильно углубляться в WordPress, просто сгенерируем список постов, которого хватит для того, чтобы протестировать функцию поиска ближайшего события.
Генерация постов
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /** * Функция генерации постов * * @return array */ function generate_posts() { // определение переменной, которая будет содержать посты $posts = []; // цикл, с количеством итераций от 5 до 10 for ( $i = 0; $i < rand( 5, 10 ); $i ++ ) { // определение количества часов, на которое время поста будет отличаться от текущего $delta = rand( - 24, 24 ); // если кол-во часов меньше 0 if ( $delta < 0 ) { // формируется прошедшее время в MySQL формате $date = date( 'Y-m-d H:i:s', strtotime( $delta . 'houer' ) ); // если кол-во часов больше 0 } elseif ( $delta > 0 ) { // формируется будущее время в MySQL формате $date = date( 'Y-m-d H:i:s', strtotime( '+' . $delta . 'houer' ) ); // если кол-во часов равно 0 } else { // формируется настоящее время в MySQL формате $date = date( 'Y-m-d H:i:s' ); } // добавление поста к списку, где порядковый номер соответствует ID, для удобства $posts[ $i ] = [ 'ID' => $i, 'post_date' => $date, ]; } return $posts; } |
Данная функция формирует список, содержащий от 5 до 10 постов, каждый из которых имеет случайно полученное время в интервале ±24 часа.
Принцип поиска ближайшего события сводится к стандартной задаче поиска минимального значения. Ближайшее событие определяется тем, что между текущем временем и временем события минимальный промежуток. То есть, если из текущего времени вычесть время начала события, получится разница между указанными временами. Чем это значение будет меньше, тем событие ближе к текущему времени.
При этом в данном примере не имеет значения — прошло событие или еще не наступило. Если событие наступило час назад, а следующее будет только через три часа, то ближайшим событием в этом случае, будет то, которое прошло час назад. Думаю убрать данное условие, при необходимости, у вас не составит труда.
Определение разницы времени между событиями
1 | $residual = absint( $time - $current_time ); |
Где $time
— это время поста, $current_time
— текущее время, absint()
— функция возврата абсолютного значения числа.
Функция возврата абсолютного значения числа
1 2 3 4 5 | function absint( $int ) { $int = abs( intval( $int ) ); return $int; } |
Переданные в функцию данные сперва приводятся к типу integer
, а затем определяется абсолютное значение данного числа. Иными словами функция возвращает число без учета знака.
Определение ближайшего события
Для поиска минимальной разницы необходимо перебрать все посты, определяя разницу во времени. При этом разницу необходимо сравнивать с минимальным значением разницы, чтобы в случае нахождения разницы меньше текущей заменить значение минимальной разницы и при этом запомнить ID
поста, время которого наиболее близко к текущему.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | $min = 3600 * 24 * 30; // перебор постов foreach ( $posts as $post ) { // работать с постом, как с массивом $post = (array) $post; // время текущего поста в формате unix $time = strtotime( $post['post_date'] ); // разность дат без учета знака $residual = absint( $time - $current_time ); // если разность меньше значения $min if ( $min > $residual ) { // устанавливаем время до ближайшего события $min = $residual; // запоминаем ID того поста, который ближе всех к текущей дате $post_id = $post['ID']; } } |
Для того, чтобы было с чем сравнивать время первого поста, за пределами цикла переменной $min
присваивается достаточно большое значение, в нашем случае 30 дней, после чего в цикле идет сравнение значения переменной $min
с разницей во времени и если разница сравниваемого поста с текущим временем меньше $min
, то эта разница устанавливается в качестве значения$min
, а ID
поста помещается в переменную $post_id
, что означает, что пост с данным ID
— это и есть ближайшее событие.
Далее можно вызывать функции и выводить результат. Для вывода данных я использую простую функцию вывода по формату.
Функция вывода по формату
1 2 3 4 5 6 | function pr( $data ) { echo '<pre>'; print_r( $data ); echo '</pre>'; } |
Результат выполнения скрипта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | Array ( [0] => Array ( [ID] => 0 [post_date] => 2018-02-20, 00:01:50 ) [1] => Array ( [ID] => 1 [post_date] => 2018-02-20, 01:01:50 ) [2] => Array ( [ID] => 2 [post_date] => 2018-02-21, 08:01:50 ) [3] => Array ( [ID] => 3 [post_date] => 2018-02-21, 11:01:50 ) [4] => Array ( [ID] => 4 [post_date] => 2018-02-20, 05:01:50 ) ) ID ближайшего события: 4, с начала события прошло 18ч. |
Листинг скрипта:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | error_reporting( E_ALL ); ini_set( "display_errors", 1 ); header( 'Content-Type: text/html; charset=utf-8' ); date_default_timezone_set( 'Europe/Moscow' ); /** * Функция генерации постов * * @return array */ function generate_posts() { // определение переменной, которая будет содержать посты $posts = []; // цикл, с количеством итераций от 5 до 10 for ( $i = 0; $i < rand( 5, 10 ); $i ++ ) { // определение количества часов, на которое время поста будет отличаться от текущего $delta = rand( - 24, 24 ); // если кол-во часов меньше 0 if ( $delta < 0 ) { // формируется прошедшее время в MySQL формате $date = date( 'Y-m-d H:i:s', strtotime( $delta . 'houer' ) ); // если кол-во часов больше 0 } elseif ( $delta > 0 ) { // формируется будущее время в MySQL формате $date = date( 'Y-m-d H:i:s', strtotime( '+' . $delta . 'houer' ) ); // если кол-во часов равно 0 } else { // формируется настоящее время в MySQL формате $date = date( 'Y-m-d H:i:s' ); } // добавление поста к списку, где порядковый номер соответствует ID, для удобства $posts[ $i ] = [ 'ID' => $i, 'post_date' => $date, ]; } return $posts; } /** * Функция возврата абсолютного значения числа * * @param $int * * @return float|int */ function absint( $int ) { $int = abs( intval( $int ) ); return $int; } /** * Функция определения ближайшего события * * @param $posts * * @return int */ function get_nearest_post( $posts ) { // id ближайшего события $post_id = - 1; // если список постов не пуст if ( ! empty( $posts ) ) { // текущее время $current_time = time(); // берем в качестве минимального срока - месяц $min = 3600 * 24 * 30; // перебор постов foreach ( $posts as $post ) { // работать с постом, как с массивом $post = (array) $post; // время текущего поста в формате unix $time = strtotime( $post['post_date'] ); // разность дат без учета знака $residual = absint( $time - $current_time ); // если разность меньше значения $min if ( $min > $residual ) { // устанавливаем время до ближайшего события $min = $residual; // запоминаем ID того поста, который ближе всех к текущей дате $post_id = $post['ID']; } } // если ближайшее событие так и не найдено if ( $post_id < 0 ) { // берем первое из списка $post_id = $posts[0]['ID']; } } return $post_id; } /** * Функция вывода данных по формату * * @param $data */ function pr( $data ) { echo '<pre>'; print_r( $data ); echo '</pre>'; } // генерация постов $posts = generate_posts(); // поиск ближайшего события $nearest_post = get_nearest_post( $posts ); // нахождение разницы во времени между текущим временем и ближайшим событием $residual = ( strtotime( $posts[ $nearest_post ]['post_date'] ) - time() ); // если разница больше 0 if ( $residual > 0 ) { // формирование текста, указывающего, через сколько часов произойдет событие $text = 'событие будет через ' . intval( gmdate( 'H', $residual ) ) . 'ч'; // если разница меньше 0 } elseif ( $residual < 0 ) { // формирование текста, указывающего, сколько часов назад произошло событие $text = 'с начала события прошло ' . intval( gmdate( 'H', $residual ) ) . 'ч'; // если разница равна 0 } else { $text = 'события началось'; } // вывод списка постов pr( $posts ); // вывод результата pr( 'ID ближайшего события: ' . $nearest_post . ', ' . $text . '.' ); |
Определение ближайшего события — практическое применение
Каждый сам для себя может придумать практическое применение данного скрипта, но в данном случае решение применялось для того, чтобы пометить пост ближайшего события специальным классом, чтобы потом в слайдере событий код на JavaScript отобразил слайд с этим событием.