За последние 24 часа нас посетили 18572 программиста и 1589 роботов. Сейчас ищут 1243 программиста ...

Алгоритм проверки попадания текущего времени в промежутки, указанные в расписании

Тема в разделе "Решения, алгоритмы", создана пользователем Allexg, 9 янв 2022.

  1. Allexg

    Allexg Новичок

    С нами с:
    9 янв 2022
    Сообщения:
    4
    Симпатии:
    0
    Доброго всем времени. Поискал по форуму похожие топики, но не нашел, поэтому делаю новый. Наверное задача тривиальная и совсем для новичков, но все же попытаю счастья, может кто-то уже делал и знает элегантное решение, потому-что все, что приходит в голову мне - очень громоздкое и кривое :))

    Стоит задача сделать проверку работы заведений по указанному для каждого дня расписанию. Расписание хранится в виде ЧЧ:ММ - ЧЧ:ММ для каждого дня недели отдельно (например пн => 10:00 - 22:45, вт => 13:00 - 02:30, ср => 10:00 - 23:00)

    Загвоздка для меня состоит в том, чтобы учесть случаи, когда заведение начинает работать в одну дату, а закрывается в другую (то-есть уже наступил новый день, и текущая дата будет уже для расписания следующего "цикла" работы заведения, но заведение еще работает). На примере: в расписании для вторника заведение закрывается в 3 часа ночи, а в среду начинает работать в 12 дня. Если я в час ночи среды проверяю расписание - заведение начинает работу только в 12, но оно еще не закрылось по расписанию вторника.

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

    PS понимаю, что это больше похоже на школьную задачку на логику, но хочтеся написать грамотную проверку, а не кривой костыль))
     
  2. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.579
    Симпатии:
    1.759
    Переводите в UNIX TIMESTAMP перед проверкой, и можно сравнивать, как обычные числа.
     
  3. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.103
    Симпатии:
    1.243
    Адрес:
    там-сям
    Лично мне непонятно что значит "сделать проверку работы заведений". Или "в час ночи среды проверяю расписание".
    Уточни пожалуйста, это проверка работает ли заведение в конкретный момент времени datetime? Или может быть это вычисление промежутка(ов) времени когда заведение работает в указанную дату? Что именно?
     
  4. Allexg

    Allexg Новичок

    С нами с:
    9 янв 2022
    Сообщения:
    4
    Симпатии:
    0
    Ок если в контексте, то есть список заведений, для которых указано расписание на каждый день недели. Проверить нужно в конкретный (текущий) момент времени какие заведения работают.
     
  5. Drunkenmunky

    Drunkenmunky Активный пользователь

    С нами с:
    12 авг 2020
    Сообщения:
    1.484
    Симпатии:
    281
    Вам же посоветовали переводить даты а UNIX формат.
    Дату начала сравнивайте с окончанием.
    Если начало больше, то к окончанию прибавьте сутки(в секундах).
    И уже с этим промежутком сравнивайте время работы.
    Тоже в Юниксе.
     
  6. Allexg

    Allexg Новичок

    С нами с:
    9 янв 2022
    Сообщения:
    4
    Симпатии:
    0
    В общем проверка все равно получается громоздкой, как ни крути.
    Нашел такое решение, что в общем не сильно отличается от моего текущего решения:

    Код (Text):
    1. $start_work = "08:00";
    2.       $end_work = "02:00";
    3.       $currentTime = "01:00";
    4.  
    5.       //текущее время с датой, даже если оно у вас 12:00 все равно имеет дату
    6.       $currentDateTime = strtotime(date('Y-m-d')  ." ". $currentTime);
    7.       //Дата и время во сколько мы закрылись вчера
    8.       $previousDayEnd;
    9.        //Дата и время во сколько мы открылись сегодня
    10.       $startDateTime;
    11.        //Дата и время во сколько мы закрылись сегодня
    12.       $endDateTime;
    13.  
    14. $startDateTime = strtotime(date('Y-m-d')  ." ". $start_work);
    15.  
    16. if (strtotime($start_work) <= strtotime($end_work)){
    17.       $endDateTime = strtotime(date('Y-m-d')  ." ". $end_work);
    18.       $previousDayEnd = strtotime(date('Y-m-d')  ." ". $end_work . "-1 days");
    19. }
    20. else{
    21.      $endDateTime = strtotime(date('Y-m-d')  ." ". $end_work . "+1 days");
    22.      $previousDayEnd = strtotime(date('Y-m-d')  ." ". $end_work );
    23. }
    24.  
    25. //проверить полученные результаты
    26.  echo "Мы закрылись в : " .  date("Y-m-d H:i:s", $previousDayEnd). "\n";
    27. echo "Открытие : " . date("Y-m-d H:i:s", $startDateTime) . "\n";
    28. echo "Закрытие  : " .  date("Y-m-d H:i:s", $endDateTime). "\n";
    29.  
    30. if ($currentDateTime >= $startDateTime && $currentDateTime <= $endDateTime) {
    31. echo "Сейчас рабочее время : " . date("Y-m-d H:i:s", $currentDateTime) ."\n";
    32. }
    33. else if($currentDateTime < $startDateTime && $currentDateTime < $previousDayEnd ){
    34.     echo "Сейчас рабочее время : " . date("Y-m-d H:i:s", $currentDateTime) ."\n";
    35. }
    36. else {
    37.   echo "Мы закрыты в это время : " . date("Y-m-d H:i:s", $currentDateTime) ."\n";
    38. }
     
  7. Drunkenmunky

    Drunkenmunky Активный пользователь

    С нами с:
    12 авг 2020
    Сообщения:
    1.484
    Симпатии:
    281
    PHP:
    1. <?php
    2. $start_work = "08:00";
    3. $end_work = "02:00";
    4. $currentTime = "13-01-2022 01:00"; // корректная текущая дата
    5.  
    6. $status = 'закрыто';
    7.  
    8. $ustart   = date_format(date_create($start_work),  'U');
    9. $ustop    = date_format(date_create($end_work),    'U');
    10. $ucurrent = date_format(date_create($currentTime), 'U');
    11.  
    12. if($ustop < $ustart)
    13. {
    14.   $ustop += 60*60*24;
    15. }
    16.  
    17. if($ucurrent > $ustart && $ucurrent < $ustop)
    18. {
    19.   $status = 'открыто';
    20. }
    21.  
    22. echo $status;
    23. ?>
    $start_work и $end_work так же задавать с датой.
    Иначе после полуночи статус будет выдавать как "закрыто"
     
    #7 Drunkenmunky, 12 янв 2022
    Последнее редактирование: 12 янв 2022
  8. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.579
    Симпатии:
    1.759
    В коде лучше использовать только ISO-формат (ГГГГ-ММ-ДД)
     
  9. Drunkenmunky

    Drunkenmunky Активный пользователь

    С нами с:
    12 авг 2020
    Сообщения:
    1.484
    Симпатии:
    281
    Я с вами абсолютно согласен.
    Но, конкретно в этом коде, не обязательно.
     
  10. Allexg

    Allexg Новичок

    С нами с:
    9 янв 2022
    Сообщения:
    4
    Симпатии:
    0
    Спасибо. Только один нюанс есть - в этих примерах (моем и Вашем) подразумевается, что расписание на все дни одинаковое, что в реальности не так. Поэтому без проверки окончания работы в предыдущий день не обойтись.
     
  11. Drunkenmunky

    Drunkenmunky Активный пользователь

    С нами с:
    12 авг 2020
    Сообщения:
    1.484
    Симпатии:
    281
    artoodetoo нравится это.
  12. artoodetoo

    artoodetoo Суперстар
    Команда форума Модератор

    С нами с:
    11 июн 2010
    Сообщения:
    11.103
    Симпатии:
    1.243
    Адрес:
    там-сям
    Ребята, мне кажется до сих пор ваше обсуждение имело не много смысла. Можно резко повыстить ценность темы, если написать SQL запрос, решающий поставленную задачу. И для быстрой проверки завести таблицу с правдоподобными данными. Всё это можно сделать на db-fiddle.com

    Начну с такого: https://www.db-fiddle.com/f/wguqsccbc7QALHSHDMV3wA/0
    а вы форкайте и дополняйте, если надо.

    Идея в том, чтобы хранить данные в удобном для эффективной работы виде. А преобразовать при вводе и выводе можно как угодно.

    Можно не высчитывать дата + время_с, (время_по < время_с ? дата + 1 день + время_по : дата + время_по) при каждой проверке, а сделать это один раз при сохранении данных.

    Я приспособил данные под операцию BETWEEN, она, как известно, подразумевает что промежуток с-по включает в себя границы. Поэтому там время конца без одной секунды. Компренде?

    Код (SQL):
    1. SELECT *
    2. FROM timesheet
    3. WHERE '2021-01-16 00:30:00' BETWEEN dt_from AND dt_to
    --- Добавлено ---
    Из личного опыта работы с расписанием: есть расписание по умолчанию или шаблон, а есть заранее запланированные нерабочие дни и есть возможность вручную поправить некоторые дни. Отсюда следует, что админка должна иметь, как минимум два раздела — шаблон расписания и реальный календарь. И операцию "накатить шаблон с даты по дату".
    Но это уже оффтоппик, к алгоритму не относится ;)
    --- Добавлено ---
    Задача "найти заведения, работающие в определённый день и показать их время работы в этот день" была бы посложнее, но тоже пишется в один SQL запрос.
     
  13. Дюран

    Дюран Активный пользователь

    С нами с:
    9 мар 2018
    Сообщения:
    284
    Симпатии:
    21
    Мне думается вряд ли у него нагенерированы в БД все эти метки времени, на все дни.
    Скорее просто запись о неделе.
    Я бы все это перевел в систему счисления, в минуты с начала недели, и по этим промежуткам уже шустрил ;)

    Только тут нужно от неопределенности избавиться, ваш формат позволяет запись типа
    вт => 01:00 - 02:30
    , а второе значение можно понимать и как день текущий и как день следующий
     
  14. Drunkenmunky

    Drunkenmunky Активный пользователь

    С нами с:
    12 авг 2020
    Сообщения:
    1.484
    Симпатии:
    281
    Более логичным вижу не запись в виде промежутка(точнее не только лишь в нем), а запись с промежутком и с заранее вычисленным временем работы, в секундах.
    Это существенно упростит код для промежутка с окончанием работы в следующих сутках - для операций будет достаточно только время начала работы и продолжительность рабочего дня.
    А собственно "промежуток" понадобится только для отображения.
     
  15. Chushkin

    Chushkin Активный пользователь

    С нами с:
    17 дек 2010
    Сообщения:
    1.062
    Симпатии:
    91
    Адрес:
    Мещёра, Центр, Болото N3
    А такой вариант не устроит?

    PHP:
    1. $start_work = '08:00';
    2. $end_work = '02:00';
    3.  
    4. $c = new DateTime();
    5. $b = new DateTime($start_work);
    6. $e = new DateTime($end_work . ($start_work > $end_work ? ' +1 day' : ''));
    7. $isOpen = ($c >= $b and $c <= $e);
     
  16. Drunkenmunky

    Drunkenmunky Активный пользователь

    С нами с:
    12 авг 2020
    Сообщения:
    1.484
    Симпатии:
    281
    Результат тот же будет.
    Но менее громоздко, да.