Работаем с банковскими (рабочими) днями

    Полгода назад при разработке системы финансового учета поднялся вопрос о идентификации банковский дней. Произошло это в следующем контексте — при выставлении счета отводится некий промежуток времени (в банковских днях) по истечению которого надо трубить тревогу, если счет еще не оплачен.

    Вооружившись трудовым кодексом решил выяснить какие дни можно считать операционными для банка. Статья 112-я гласит:

    Нерабочие праздничные дни

    Нерабочими праздничными днями в Российской Федерации являются:
    1, 2, 3, 4 и 5 января — Новогодние каникулы;
    7 января — Рождество Христово;
    23 февраля — День защитника Отечества;
    8 марта — Международный женский день;
    1 мая — Праздник Весны и Труда;
    9 мая — День Победы;
    12 июня — День России;
    4 ноября — День народного единства.

    При совпадении выходного и нерабочего праздничного дней выходной день переносится на следующий после праздничного рабочий день.


    На основен этой информации был написан простой класс на PHP который позволяет:
    1. подсчитывать кол-во банковских (рабочих) дней за определенный промежуток времени
    2. высчитывать дату +N банковских дней
    3. ну и, естейственно, определять каким днем является определенное число.

    Примеры использования:
    echo BankDay::getEndDate('2008-10-14', 10, 'd.m.Y');
    //28.10.2008
    echo BankDay::getEndDate('now', 10);
    //1229008080
    echo BankDay::getNumDays('2008-02-25', '2008-03-15');
    //13
    echo BankDay::getNumDays('now', '10 days');
    //7
    echo BankDay::isWorkDay('2009-01-06');
    //false
    * This source code was highlighted with Source Code Highlighter.


    Скачать исходник


    Дабы избежать недопониманий — в данном контексте «банковский» = «операционный» = «рабочий» день
    Поделиться публикацией

    Комментарии 42

      +1
      эмм… А как учитывается тот факт, что у нас частенько переносятся празничные дни? типа «в субботу работаем за вторник»?
        +1
        Эм…
        1. А как это можно предсказать? Базовое, судя по описанию автора («При совпадении выходного и нерабочего праздничного дней выходной день переносится на следующий после праздничного рабочий день.») класс умеет, а что-то более сложное зависит от левых пяток управленцев. Или нет?
        2. А в банках тоже часто произвольно переносят рабочие дни?
          0
          1. еще как зависит :)
          2. если честно, не в курсе, думаю что они ориентируются на законы, а это п. 1

          Но не думаю что ± 1 день может быть смертельно.
            0
            На постановления правительства. Лично я не полагаюсь на правило совпадающих праздников и выходных, а прописываю каждый перенос индивидуально. В этом году, ЕМНИП, уже был один «неправильный» перенос.

            Не смертельно, конечно, но на бабло можно попасть серьёзно.
              0
              Угу, тоже встречался с решением ввиде таблицы Calendar, где для каждого дня прописывалось явно, рабочий он или не рабочий.

              Ибо все это индивидуально. Да и прадзники чуть ли не каждый год меняются.
                0
                У меня смешанное решение: большинство праздников и выходных подхватываются автоматически (переносов праздников не бывает). Всё прописывается в 3 таблицы: (1) регулярные праздники в формате: ( дата, ( год начала, год конца) ), (2) даты искусственно будних и (3) искусственно выходных дней. Очень удобно.
          0
          Это возможно только вручную корректируя даты на каждый год. Так же здесь не обрабатываются исключения из правил, которые регулярно издаются.
          Т. е. учитывается самое общее.
            0
            Ага, прошу прощения, не заметил строчку про совпадение выходных и праздников. В общем, спасибо, может пригодиться.
          +5
          Отстой ваш класс, бегает по всем дням, делает для каждого дня нетривиальный и тяжелый вызов strtotime, а потом еще и не менее тяжелый date('w').

          Вот вам функция, которая в константное время считает количество рабочих дней в интервале, не учитывая праздников. Остается прочекать для каждого праздника, 1) является ли он выходным и 2) входит ли в промежуток.

          function workday_count($start_day_as_timestamp,$total_days)
          {
              //$weekday 0 = Sun 1 = Mon ...   
              $weekday = date('w',$start_day_as_timestamp);   
          
              $days_without_1st_incomplete_week = $total_days — (7-$weekday);
              $workdays_in_1st_incomplete_week = 6 — $weekday;   
          
              $workdays_in_complete_weeks = floor($days_without_1st_incomplete_week / 7) * 5;   
          
              $days_in_last_week = $days_without_1st_incomplete_week % 7;
              $workdays_in_last_week = $days_in_last_week ? $days_in_last_week — 1: 0;   
          
              return $workdays_in_1st_incomplete_week + $workdays_in_complete_weeks + $workdays_in_last_week;
          }
          


          Дарю безвозмездно, ибо писал сие вчера, в ответ на не менее cтрашную функцию
            0
            Спасибо ) Всегда есть что оптимизировать :)
            0
            По идее надо делать скрипт, который бы парсил производственный календарь на текущий год, обновляемый на надежном сайте. Только вот что-то таких календарей в xml не выкладывают.
              0
              производственный календарь не равен банковским дням… причём совсем :)
              0
              знаете… это проблема с банковскими днями не такая простая как кажется на первый взгляд…
              самое главное — «банковский день» (возможно и) = «операционный», но 10000000% не равен «рабочий»…

              во-первых непонятно с какого момента этот отсчёт начинать считать… если счёт выдан вечером в среду — то среда считается как банковский день или нет… а то что в большинство банков операции производятся только до обеда — это как учитывать…

              очень часто сталкивался с тем что если счёт выписан на три банковских дня и выдан в четверг (т.е. вроде как действителен в четверг, пятницу… ииии… ну ладно предположим вещё в понедельник) — то в этот самый понедельник его уже оплатить нельзя — ну отказываются банки и всё тут :)… или говорят… а мы в субботу работали… ваш счёт просрочен и недействителен :)

              и никто… начиная от главных бухгалтеров предприятий (как минимум 4-х), ни от управляющих банков (тоже не одного) — не смог дать вразумительного ответа…
              это хорошо ещё когда разговор идёт там типа — «в течении 15 банковских дней»… но когда о 3-х (что зачастую и печатают в подавляющем большинстве счетов) — то там мрак полный
                +2
                да, бардак не автоматизируем…
                  0
                  ну это наша основная присказка :)
                  0
                  Это все понятно :)
                  В моем случае выглядит это как красный и зеленый цвет в списке исходящих счетов. Если что-то покраснеет или позеленеет раньше или позже на день, то ничего страшного в этом нет :)
                  0
                  ну скажем так. По закону каждый банк имеет право сам устанавливать продолжительность операционного дня. Скажем до 12:00 принимать платежки или до 15:00. А то что позже проводить следующим днем. Но в любом случае не позднее последнего пятого рейса 19:30 местного времени. Вроде так.
                    0
                    Бедные, а в Питере рейсов нет, и зачем они…
                    +1
                    В банковских системах это решается с помощью отдельной таблицы операционных дней. Они автоматически генерятся на основе рабочего календаря (без суббот и воскресений) и потом каждый год корректируем руками добавляя/удаляя рабочии дни.
                    Может быть и Вам достаточно так же поступить?
                      0
                      Это самое гибкое решение.
                      А для справки можно посмотреть тут: www.calend.ru/work/ Там и про изменения написано.
                        0
                        Думаю что это самый правильный вариант.

                        желание автоматизировать и заставить систему работать без человеческого вмешательства меня погубит :)
                        –3
                        Теперь понятно почему всё так плохо в банковской системе. Да потому, что софт написан на PHP.
                          0
                          а разве 31 декабря — банковский день?
                            0
                            да, банки работают. Некоторые просто закрываются раньше
                              0
                              я даже скажу больше… по закону (во всяком случае в Украине) — 31 декабря ещё и рабочий день к тому же :)))) как и 2-е января :)))
                                0
                                по законодательству — да,
                                да и по факту, в принципе, тоже
                                0
                                Нужно учесть, что есть еще и локальные праздники, которые объявляются нерабочим днем. В Чувашии, например, есть День Республики, 24 июня — нерабочий день. Думаю, подобные праздники есть и в других республиках.
                                  0
                                  Там в начале класса можно есть список праздников, туда можно дописать и 24е июня. Только один момент — точно ли этот праздник переносится при попадании на выходной день?
                                • НЛО прилетело и опубликовало эту надпись здесь
                                    0
                                      0
                                      А вам принципиально что это php, да?
                                      • НЛО прилетело и опубликовало эту надпись здесь
                                        • НЛО прилетело и опубликовало эту надпись здесь
                                          0
                                          Капец. Наколенная разработка людей, которые нифига не представляют себе финансы и бухучёт, которые только и умеют, что рисовать код и нахватались знаний про интерфейсы.
                                          В какое место ни ткни — ни понимания о актах, счёт-фактурах, о кодах бюджетирования, когда в одном платеже по двум счетам оплачивают и много-много ещё о чём.

                                          Это убогое поделище я уверен ещё смогут кому-то впарить!!! Но я с удовольствием посмотрю, как эта программа начнёт медленно катиться к реальным финансам компании и как будет наступать в головах разработчиков осознавание масштабности задачи.

                                          Браво! Я в восхищении!!! Только от большого незнания можно вляпаться в ту задачу, которую вы себе поставили!!!
                                            0
                                            Господа, все комментарии по самой системе будут рады видеть в фидбеке на промо-сайте. А я, увы, оставлю вас без ответов.
                                            Я всего лишь взял абстрактный класс и рассказал в каком контексте он мне понадобился :)
                                        0
                                        В конкретных регионах есть свои праздники, с которыми так же действуют правила переноса.

                                        В Татарстане, например, таких три: Курбан-байрам (каждый год по-разному, по лунному календарю как-то считается), День независимости Татарстана (30 августа) и День конституции Татарстана (6 ноября).

                                        И банки их соблюдают, даже филиалы общероссийских банков и иностранных.

                                        Вообще идеально было бы, если б существовал какой-то официальный репозиторий с открытым API (как у ЦБ РФ с курсами валют).
                                          +1
                                          попробую кинуть идею, а вы может заинтересуетесь и реализуете.

                                          ваш класс решает три задачи. при этом задача номер три является частным случаем первой задачи.

                                          3. узнать является ли ваш день банковским или нет, достаточно будет посчитать количество рабочих дней в промежутке от этого дня, до этого дня. Если 0 — то не банковский, если 1 — то банковский. Возможно не совсем корректно будет по производительности, но зато надежно. Ну либо в третьей задаче надо просто проверить на выходной, либо праздничный день :)

                                          то есть остается две задачи.

                                          1 задача —
                                          я бы разбил на три класса, первый класс будет мне возващать количество выходных в определенный промежуток времени, второй класс будет возвращать количество праздников в определенный промежуток времени. А вот третий класс уже как нетрудно догадаться, будет возвращать банковские дни, при этом высчитать их легко, вычесть из общего количества дней выходные и данковские. Ну и подкорректировать границы с учетом заморочек какое время суток сейчас. Получиться более гибко и понятно.

                                          2 задача — тут тоже ничего сложного. Берем левую границу, прибавляем N дней, высчитываем количество банковских дней, если оно меньше, чем то которое запросили, прибавляем еще разницу, и так пока у нас разница не превратится в 0. То есть в зависимости от того насколько длинный промежуток, получится три-четыре прохода наверное в среднем.

                                          думаю должно получиться красивее, попробуйте.
                                            0
                                            промахнулся кнопкой ответ — ответ оказался ниже :)
                                            0
                                            Не очень понял идею в целом, но уточнение про время думаю может быть полезным. Т. е. если запрашивают дату 17.08.2009 18:03 + 2 дня то надо считать 18.08.200 + 2 дня ибо этот день уже прошел.
                                            Хотя тут все зависит от типа входных данных — в некоторых случаях это может внести больше путаницы чем пользы, поэтому предлагаю всем желающим модифицировать под свои потребности :)
                                            Но за идею спасибо.
                                              0
                                              А где исходник?

                                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                            Самое читаемое