Расчет возраста по дате рождения и текущей дате




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2024-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

49 Comments

  1. FirePyres

    как то все сложно, у меня вот так получилось:

    ВЫБРАТЬ
    &ТД КАК ТД,
    &ДР КАК ДР
    ПОМЕСТИТЬ данные
    ;
    
    ////////////////////////////////////////////////////////////­////////////////////
    ВЫБРАТЬ
    РАЗНОСТЬДАТ(Данные.ДР, Данные.ТД, ГОД) + ВЫБОР
    КОГДА МЕСЯЦ(Данные.ДР) < МЕСЯЦ(Данные.ТД)
    ИЛИ МЕСЯЦ(Данные.ДР) = МЕСЯЦ(Данные.ТД)
    И ДЕНЬ(Данные.ДР) <= ДЕНЬ(Данные.ТД)
    ТОГДА 0
    ИНАЧЕ -1
    КОНЕЦ КАК Лет,
    МЕСЯЦ(Данные.ТД) — МЕСЯЦ(Данные.ДР) + ВЫБОР
    КОГДА МЕСЯЦ(Данные.ТД) — МЕСЯЦ(Данные.ДР) < 0
    ТОГДА 12
    ИНАЧЕ 0
    КОНЕЦ КАК Месяц,
    ВЫБОР
    КОГДА ДЕНЬ(Данные.ТД) — ДЕНЬ(Данные.ДР) >= 0
    ТОГДА ДЕНЬ(Данные.ТД) — ДЕНЬ(Данные.ДР)
    ИНАЧЕ ДЕНЬ(Данные.ТД) — ДЕНЬ(Данные.ДР) + РАЗНОСТЬДАТ(Данные.ТД, ДОБАВИТЬКДАТЕ(Данные.ТД, МЕСЯЦ, 1), ДЕНЬ)
    КОНЕЦ КАК День,
    Данные.ДР
    ИЗ
    данные КАК Данные
    

    Показать

    Reply
  2. D.Gal

    (1) как месяцев может быть 12?

    Reply
  3. FirePyres

    пример покажите,

    я лично не вижу куска где такое возможно

    МЕСЯЦ(Данные.ТД) — МЕСЯЦ(Данные.ДР)
    + ВЫБОР
    КОГДА МЕСЯЦ(Данные.ТД) — МЕСЯЦ(Данные.ДР) < 0      ТОГДА 12
    
    ИНАЧЕ 0
    КОНЕЦ
    
    Reply
  4. D.Gal

    Да, невозможно, это я ошибся, только проблема с високосными годами осталась. Дни неправильно считает…

    Reply
  5. МимохожийОднако

    Полагаю в ЗУП уже есть подобные процедуры и функции

    Reply
  6. Vlan

    А почему именно запросом получать дни?

    Ведь гораздо проще соорудить такую, например, конструкцию:

     Лет = 0;
    Месяцев = 0;
    Дней = 0;
    ДатаР = Объект.ДатаРождения;
    Пока ДатаР <= ТекущаяДата() Цикл
    ДатаР = ДобавитьМесяц(ДатаР,1);
    Месяцев = Месяцев + 1;
    КонецЦикла;
    Месяцев = Месяцев-1;
    Лет = Цел(Месяцев/12);
    Месяцев = Месяцев-Лет*12;
    Дней = Цел((ТекущаяДата() — ДобавитьМесяц(ДатаР,-1))/(60*60*24));
    Объект.КоличествоДней = «Лет: «+Строка(Лет)+» Месяцев: «+Строка(Месяцев)+» Дней: «+Строка(Дней);
    

    Показать

    Reply
  7. v3rter

    Вопрос с месяцами скорее методический — как складывать между собой два неполных месяца интервала и с чем сравнивать их суммарное количество дней.

    Reply
  8. v3rter

    Сравнивать их с 29, 30, 31, с количеством дней в месяце первой даты, с количеством дней в месяце второй даты, или с минимумом количества дней в месяцах обеих дат.

    Reply
  9. sp18s

    (1) херня. вбейте

    Имя параметра Значение параметра

    ДР 05.05.2015 0:00:00

    ТД 03.05.2017 0:00:00

    и получите 1 лет и 29 дней.

    Reply
  10. Vlad_2008

    Странный запрос в (1) и процедура в (6) странная, и считает не правильно, для 27.04.2017 — 29.02.1988 выдала: «Лет: 29 Месяцев: 1 Дней: 30».

    У меня запрос из трех строк выдает следующее:

    27.04.2017 — 29.02.1988 = 29 лет 1 мес 27 дней

    03.05.2017 — 29.02.1988 = 29 лет 2 мес 3 дня

    Reply
  11. Vlan

    (10) Для високосного года достаточно добавить одно условие и будет правильно.

     Лет = 0;
    Месяцев = 0;
    Дней = 0;
    ДатаР = Объект.ДатаРождения;
    Високосный = 0;
    Если Не День(ДобавитьМесяц(ДатаР,12)) = День(ДатаР) Тогда
    Високосный = 1;
    ДатаР = ДатаР — 60*60*24;
    КонецЕсли;
    Пока ДатаР <= Объект.ЛюбаяДата Цикл
    ДатаР = ДобавитьМесяц(ДатаР,1);
    Месяцев = Месяцев + 1;
    КонецЦикла;
    Месяцев = Месяцев-1;
    Лет = Цел(Месяцев/12);
    Месяцев = Месяцев-Лет*12;
    Дней = Цел((Объект.ЛюбаяДата — ДобавитьМесяц(ДатаР,-1))/(60*60*24))-Високосный;
    Объект.КоличествоДней = «Лет: «+Строка(Лет)+» Месяцев: «+Строка(Месяцев)+» Дней: «+Строка(Дней);
    

    Показать

    Между прочим, для 03.05.2017 — 29.02.1988 должно быть 29 лет 2 мес 4 дня, а не 3 дня, как у вас.

    Reply
  12. v3rter

    В экселе с разностью дат аналогичные проблемы http://excel2.ru/articles/funkciya-razndat-vychislenie-raznosti-dvuh-dat-v-dnyah-mesyacah-godah-v-ms-excel-razndat по ссылке варианты разностей с полными месяцами и с неполными, альтернативные формулы и немного обсуждения

    Reply
  13. D.Gal

    (6) Я скажу, что такова была постановка задачи, решить которую надо запросом, в коде же модулей было бы значительно проще.

    Reply
  14. D.Gal

    (5) Можете скинуть реализацию из ЗУП?

    Reply
  15. Vlad_2008

    (11) Ну я считал так:

    29.02.1988 — 28.02.2017 = 29 лет

    01.03.2017 — 30.04.2017 = 2 мес

    01.05.2017 — 03.05.2017 = 3 дня

    поправьте меня, где я потерял день?

    В моем понимании, 29.02.1988 не должно считаться за день в этом расчете — это собсна день рождения, в этот день у человека «полный год», а вот со след. дня и пошел расчет … «между прочим» )).

    P.S.

    А «високосный» Вы по прежнему считаете не правильно. А саму конструкцию проверки високосного я бы сделал так: «Год(ДатаР) % 4». Ваш способ, конечно, оригинален, я бы не додумался. Скорее всего я бы сделал так: День(КонецМесяца(ДатаР)) = 29 … вау ))))))))

    И еще, у Вас если д/р 29.02.1988, то Високосный = 1, а если 28.02.1988, то Високосный = 0 … странно все ето ))) ужОс !!

    Reply
  16. Vlan

    (15) Високосный нужен только для 29.02, остальные дни считаются одинаково.

    Логика расчета проста: каждое число следующего месяца, совпадающее с днем рождения дает полный месяц. Каждый следующий день, если месяц неполный, добавляется к дням. Именно так у вас «потерялся» один день. Полный месяц приходится на 29-е число. Затем идут 30,1,2,3. Итого 4 дня.

    Reply
  17. Vlad_2008

    (16) Да, Вы правы. Я зачем-то считал календарные месяцы, вместо реальных.

    Переделал запрос, теперь все четко:

    Reply
  18. borodatii

    (15) из википедии:

    … год является високосным в двух случаях: либо он кратен 4, но при этом не кратен 100, либо кратен 400.
    Reply
  19. МимохожийОднако

    (14) В ЗУП не нашёл. Может быть это?

    http://kb.mista.ru/article.php?id=664

    Reply
  20. Vlan

    Нашел и у себя ошибку. На 28-м числе дает неверные значения. Вот так поправил, теперь вроде с 29.02 все верно:

     Лет = 0; Месяцев = 0; Дней = 0; Високосный = Ложь;
    Сутки = 60*60*24;
    ДатаР = Объект.ДатаРождения;
    Если Не День(ДобавитьМесяц(ДатаР,12)) = День(ДатаР) Тогда
    Високосный = Истина;
    КонецЕсли;
    Пока ДатаР <= Объект.ЛюбаяДата Цикл
    ДатаР = ДобавитьМесяц(ДатаР,1);
    Если Високосный И Месяц(ДатаР)=3
    И Не Год(Объект.ДатаРождения)= Год(ДатаР) Тогда
    ДатаР = ДатаР+Сутки;
    КонецЕсли;
    Месяцев = Месяцев + 1;
    КонецЦикла;
    Месяцев = Месяцев-1;
    Лет = Цел(Месяцев/12);
    Месяцев = Месяцев-Лет*12;
    Дней = Цел((Объект.ЛюбаяДата — ДобавитьМесяц(ДатаР,-1))/Сутки);
    Объект.КоличествоДней = «Лет: «+Строка(Лет)+» Месяцев: «+Строка(Месяцев)+» Дней: «+Строка(Дней);
    

    Показать

    Reply
  21. Vlan

    А можно еще упростить, ведь сбивается общий расчет только для 29.02 при переходе с високосного года на обычный:

     Лет = 0; Месяцев = 0; Дней = 0;
    Сутки = 60*60*24;
    ДатаР = Объект.ДатаРождения;
    Пока ДатаР <= Объект.ЛюбаяДата Цикл
    ДатаР = ДобавитьМесяц(ДатаР,1);
    Если Месяц(ДатаР) = 3 И Не День(ДатаР) = День(Объект.ДатаРождения) Тогда
    ДатаР = ДатаР+Сутки;
    КонецЕсли;
    Месяцев = Месяцев+1;
    КонецЦикла;
    Месяцев = Месяцев-1;
    Лет = Цел(Месяцев/12);
    Месяцев = Месяцев-Лет*12;
    Дней = Цел((Объект.ЛюбаяДата-ДобавитьМесяц(ДатаР,-1))/Сутки);
    Объект.КоличествоДней = «Лет: «+Строка(Лет)+» Месяцев: «+Строка(Месяцев)+» Дней: «+Строка(Дней);

    Показать

    Reply
  22. avart2006

    Вот мой реально работающий код в отчете:

    Лет=0; Мес=0; Дн=0;
    РазобратьРазностьДат(ДатаКон, ДатаНач, Лет, Мес, Дн);
    ЛетС = ФормаМножественногоЧисла(«год»,»года»,»лет»,Лет);
    МесС = ФормаМножественногоЧисла(«месяц»,»месяца»,»месяцев»,Мес);
    ДнС = ФормаМножественногоЧисла(«день»,»дня»,»дней»,Дн);
    СтрокаИтог = СокрЛП(?(Лет>0,Строка(Лет)+» «+ЛетС,»»)+» «+?(Мес>0,Строка(Мес)+» «+МесС,»»)+» «+Строка(дн)+» «+ДнС);
    
    Функции:*****************************************************************************************************
    Функция ФормаМножественногоЧисла(Слово1, Слово2, Слово3, Знач ЦелоеЧисло)
    
    // Изменим знак целого числа, иначе отрицательные числа будут неправильно преобразовываться
    Если ЦелоеЧисло < 0 Тогда
    ЦелоеЧисло = -1 * ЦелоеЧисло;
    КонецЕсли;
    
    Если ЦелоеЧисло <> Цел(ЦелоеЧисло) Тогда
    // для нецелых чисел — всегда вторая форма
    Возврат Слово2;
    КонецЕсли;
    
    // остаток
    Остаток = ЦелоеЧисло%10;
    Если (ЦелоеЧисло >10) И (ЦелоеЧисло<20) Тогда
    // для второго десятка — всегда третья форма
    Возврат Слово3;
    ИначеЕсли Остаток=1 Тогда
    Возврат Слово1;
    ИначеЕсли (Остаток>1) И (Остаток<5) Тогда
    Возврат Слово2;
    Иначе
    Возврат Слово3;
    КонецЕсли;
    
    КонецФункции
    
    Процедура РазобратьРазностьДат(Дата1, Дата2, Лет = 0, Месяцев = 0, Дней = 0)
    Лет  = 0;
    Месяцев = 0;
    Дней = 0;
    Если Дата1 > Дата2 Тогда
    
    ВременнаяДата = Дата1;
    Если День(ВременнаяДата) < День(Дата2) Тогда
    Дней = (ВременнаяДата — ДобавитьМесяц(ВременнаяДата,-1))/86400;
    ВременнаяДата = ДобавитьМесяц(ВременнаяДата,-1);
    КонецЕсли;
    Если Месяц(ВременнаяДата) < Месяц(Дата2) Тогда
    ВременнаяДата = ДобавитьМесяц(ВременнаяДата,-12);
    Месяцев = 12;
    КонецЕсли;
    Лет  = Макс(    Год(ВременнаяДата)  — Год(Дата2), 0);
    Месяцев = Макс(Месяцев + Месяц(ВременнаяДата) — Месяц(Дата2), 0);
    Дней = Макс(Дней  + День(ВременнаяДата) — День(Дата2), 0);
    
    // скорректируем отображаемое значение, если «»вмешалось»» разное количество дней в месяцах
    Если Дата2 <> (ДобавитьМесяц(Дата1,-Лет * 12-Месяцев)-Дней * 86400) Тогда
    Дней = Дней + (День(КонецМесяца(Дата2)) — День(НачалоМесяца(Дата2))) — (День(КонецМесяца(ДобавитьМесяц(Дата1,-1))) — День(НачалоМесяца(ДобавитьМесяц(Дата1,-1))));
    КонецЕсли;
    
    КонецЕсли;
    КонецПроцедуры

    Показать

    Reply
  23. Vlad_2008

    (20) Вчера гонял Ваш предыдущий вариант на ошибки и видел что на 28 числе они были, сейчас проверим новый вариант )).

    Думаю, что и в Вашем варианте и в варианте из (22) будут ошибки, а думаю так, потому что Вы используете деление на «сутки». Чисто математически, очевидно, что при расчете за 1000 лет «что-то пойдет не так».

    Reply
  24. Vlad_2008

    (18)

    … год является високосным в двух случаях: либо он кратен 4, но при этом не кратен 100, либо кратен 400.

    … НЕ кратен 100, либо кратен 400 …

    Мой мозг не смог впитать сие …

    Reply
  25. borodatii

    (24)

    Год високосный в одном из двух случаев:

    1) кратен 4, но при этом не кратен 100, например, 16, 96, 1996.

    2) кратен 400, например 800, 2000.

    Иначе — обычный, например, 100, 200, 300.

    Reply
  26. Vlan

    (23)Сомневаюсь, что за 1000 лет что-то поменяется. Если только календарь вдруг закончится, как у Майя. 🙂

    В моем алгоритме загвоздка была только в добавлении месяца с 29.01 до 29.02. 1C автоматом исправляла недопустимую дату на 28.02, если год не високосный. Достаточно было это обойти, чтобы программа заблистала во всей красе.

    Reply
  27. v3rter

    (25)

    2) кратен 400, например 800, 2000.

    Если уж совсем быть точными, григорианский календарь запустили 4 октября 1582 года, то есть кратные 400 года надо считать високосными если год>=1600. Но в нашем случае это, думаю, несущественно.

    Reply
  28. Vlad_2008

    (25) , (27)

    Да, я видимо, застрял в юлианском календаре. Теперь вместо «Год % 4» буду делать

    «ЭтоВисокосный = (Год % 400 = 0) ИЛИ (Год % 100 <> 0 И Год % 4 = 0)».

    Кстати, в 1С 400,800,1200 — високосные, т.е. уже не важно, что запустили в 1582 году )).

    Reply
  29. Vlad_2008

    (26) Проверил Ваш новый вариант, количество ошибок прежнее, просто они чуть сместились по дням, вот пара замеров:

    Дата рождения: 31.12.1672 0:00:00,  период: 01.01.2017 0:00:00 — 31.12.2017 0:00:00
    Ошибок = 336  ( 92,1% )
    
    01.01.2017 = 344 — 00 — 01  /  344 — 00 — 01    344 — 00 — 02  !!!!!!!!!!!!
    —————————————————
    
    Дата рождения: 29.02.1988 0:00:00,  период: 01.01.2017 0:00:00 — 31.12.2017 0:00:00
    Ошибок = 30  ( 8,2% )
    
    29.01.2017 = 28 — 11 — 00  /  28 — 11 — 00    28 — 11 — 01  !!!!!!!!!!!!
    —————————————————
    
    [Дата текущая] = [расчет в запросе] / [расчет в коде]   [Ваш расчет]

    Показать

    На всякий случай, замечу, что я в своих расчетах не использую определение «високосности».

    А Ваш цикл расчета месяцев всегда будет «грешить», т.к. конец месяца будет сбиваться не только в феврале и не обязательно в високосный год:

    — 29.11.2016 + з мес = 28.02.2017 ( 29 -> 28 )

    — 31.03.2017 + 3 мес = 30.06.2017 ( 31 -> 30 )

    имеется в виду ДобавитьМесяц(Дата, 3).

    Задайте ДР = 31.03.1975 и ДТ = 29.01.2017:

    — старый = Лет: 41 Месяцев: 10 Дней: 1

    — новый = Лет: 41 Месяцев: 9 Дней: 30

    — д.быть = 29.01.2017 = 41 — 09 — 29.

    Reply
  30. Vlad_2008

    Тут вот еще замер производительности сделал, вариант (26) с циклом, явно проигрывает:

    Период текущих дат: 01.01 — 31.12.2017
    
    1) дата рождения: 29.02.1988
    
    запрос     :  2  (  1,598 )
    код        :  0  (  0,013 )
    Инфостарт  :  1  (  1,235 )
    
    2) дата рождения: 31.01.1700
    
    запрос     :  2  (  1,606 )
    код        :  0  (  0,013 )
    Инфостарт  : 14  ( 13,164 )
    

    Показать

    Reply
  31. Vlan

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

    Я переделал свою обработку. Проверяйте.

    &НаКлиенте
    Процедура Рассчитать(Команда)
    Объект.КоличествоДней.Очистить();
    ДатаНач = Объект.ДатаНач;
    ЛюбаяДата = Объект.ЛюбаяДата;
    ДатаРождения = Объект.ДатаРождения;
    ЧислоДатаР = День(ДатаРождения);
    Сутки = 60*60*24;
    Пока ДатаНач <= ЛюбаяДата Цикл
    Лет = 0; Месяцев = 0; Дней = 0;
    ДатаР = ДатаРождения;
    Пока ДатаР <= ДатаНач Цикл
    ДатаР = ПолучитьЧислоМесяца(ДатаР,ЧислоДатаР,1);
    Месяцев = Месяцев+1;
    КонецЦикла;
    Месяцев = Месяцев-1;
    Лет = Цел(Месяцев/12);
    Месяцев = Месяцев-Лет*12;
    ДатаР = ПолучитьЧислоМесяца(ДатаР,ЧислоДатаР,-1);
    Дней = Цел((ДатаНач-ДатаР)/Сутки);
    Дни = Объект.КоличествоДней.Добавить();
    Дни.Дата = ДатаНач;
    Дни.Количество = «Лет: «+Строка(Лет)+» Месяцев: «+Строка(Месяцев)+» Дней: «+Строка(Дней);
    ДатаНач = ДатаНач + Сутки;
    КонецЦикла;
    КонецПроцедуры
    
    &НаКлиенте
    Функция ПолучитьЧислоМесяца(ДатаР,Знач ЧислоДатаР,Смещение)
    День = ЧислоДатаР;
    Год = Год(ДатаР);
    Месяц = Месяц(ДатаР)+Смещение;
    Если Месяц > 12 Тогда
    Месяц = 1;
    Год = Год + 1;
    ИначеЕсли Месяц = 0 Тогда
    Месяц = 12;
    Год = Год — 1;
    КонецЕсли;
    Пока День > 0 Цикл
    Попытка
    ДатаР = Дата(Год,Месяц,День);
    Прервать;
    Исключение
    День = День — 1;
    КонецПопытки;
    КонецЦикла;
    Возврат ДатаР;
    КонецФункции
    

    Показать

    Остается открытым вопрос: от дня рождения на 31.05.2016 до 28.02.2017 будет полных 9 месяцев или 8 месяцев и 28 дней?

    Reply
  32. v3rter

    недокументированная Разндат() экселя считает, что 8

    Reply
  33. Vlan

    (32)Тогда получается, что полных 9 месяцев никогда не будет, что несколько странно. Придется еще костылей добавлять.

    Reply
  34. v3rter

    С 31.05.2016 по 01.03.2017эксель считает 9 полных месяцев, или я что-то не понял?

    Reply
  35. Vlan

    (34)У меня получается 0 дней, если числа дат совпадают. Следующая дата это уже n-месяцев + 1 день.

    Reply
  36. Vlad_2008

    (31) Я, думаю, что это 9 полных месяцев, т.к. «месячный» день рождения приходится именно на 28.02.2017.

    Reply
  37. bashhhh

    Вроде можно проще:

    ПолныхЛет = Год(ТекДата) — Год(ДатаРождения)
    — ?(Месяц(ТекДата) < Месяц(ДатаРождения)
    Или Месяц(ТекДата) = Месяц(ДатаРождения) И День(ТекДата) < День(ДатаРождения),
    1, 0);
    Reply
  38. D.Gal

    (37) Вы когда этот комментарий писали, думали хоть, что в контексте моего запроса и задачи он вообще ничего значит. Зачем мне количество полных лет кодом, когда я написал, что задачу необходимо решать запросом было? Или Вы может не мне написали?

    Reply
  39. [4EPHbIYY_KOT]

    (38) Дали задачу в ЗУП 3.1 посчитать стаж работника на предприятии относительно даты приёма. Чтоб не рисовать новый код и отчёт, взял отчёт «Штатные сотрудники». Изменяя вариант, добавил в предприятии 3 пользовательских поля-выражения:

    Лет, выражение:

    РазностьДат([Дата приема], ТекущаяДата(), «ГОД») + Выбор

    Когда Месяц([Дата приема]) < Месяц(ТекущаяДата()) Или Месяц([Дата приема]) = Месяц(ТекущаяДата()) И День([Дата приема]) <= День(ТекущаяДата())

    Тогда 0

    Иначе -1

    Конец

    Месяцев, выражение:

    (РазностьДат(ДобавитьКДате([Дата приема], «Месяц», 1), ТекущаяДата(), «МЕСЯЦ») + Выбор

    Когда День([Дата приема]) <= День(ТекущаяДата())

    Тогда 1

    Иначе 0

    Конец) % 12

    Дней, выражение:

    Выбор

    Когда День([Дата приема]) <= День(ТекущаяДата())

    Тогда День(ТекущаяДата()) — День([Дата приема])

    Иначе День(ТекущаяДата()) + РазностьДат([Дата приема], КонецПериода([Дата приема], «Месяц»), «День»)

    Конец + 1.

    В детальных записях их вывел и сгруппировал в группу «Стаж на предприятии». Кадровики довольны.

    Но это писал на языке СКД, в обычных запросах надо дописать несколько строк, чтобы получить остаток от деления на 12 при вычислении полных месяцев, а текущую дату втыкать параметром в запрос. Думаю, в решении Вашей задачи вполне может помочь.

    Reply
  40. Zurfik

    Вот моя реализация вашей задачи, прошу закрепить, она считает полное количество лет, но можно легко переделать чтобы еще количество месяцев и дней выводила. Встал вопрос решить в запросе т.к. не нашел пришлось самому писать:

    Выбор Когда ДеньГода(ДОБАВИТЬКДАТЕ(&ДатаРождения, День, РАЗНОСТЬДАТ(&ДатаРождения, &ТекущаяДата, День)))+(ДЕНЬГОДА(КонецПериода(&ДатаРождения,Год))-ДЕНЬГОДА(&ДатаРождения))>ДЕНЬГОДА(КонецПериода(&ТекущаяДата,Год))
    Тогда РАЗНОСТЬДАТ(&ДатаРождения, &ТекущаяДата, Год)+1
    Иначе РАЗНОСТЬДАТ(&ДатаРождения, &ТекущаяДата, Год) Конец КАК ПолныхЛет
    
    Reply
  41. Zurfik

    (40)

    Правильнее будет заменить «>» на «>=», тогда если день рождения совпадет с текущей датой, то количество полных лет будет максимальное уже на эту дату.

    Reply
  42. Vlad_2008

    (16) Может показалось …

    Каков возраст на 01.03.2019 в полных годах, если дата рождения будет 29.02.2008, покажет Ваш запрос ?

    У меня Ваш запрос показал след. результаты:

    ДатаРождения ТекущаяДата Условие ПолныхЛет

    29.02.2008 0:00:00 01.03.2019 0:00:00 > 12

    29.02.2008 0:00:00 01.03.2019 0:00:00 >= 12

    Reply
  43. Vlan

    (42) Не найду эту обработку. Чисто теоретически (по моим алгоритмам) должно быть 11 лет и 1 день.

    Reply
  44. Vlad_2008

    (43) Ой, не к тому сообщению написал … это я к сообщению № 40

    Мои расчеты показывают такой же результат )), а вот у ребят как-то странно!

    Reply
  45. Vlad_2008

    (40) Может показалось …

    Каков возраст на 01.03.2019 в полных годах, если дата рождения будет 29.02.2008, покажет Ваш запрос ?

    У меня Ваш запрос показал след. результаты:

    ДатаРождения ____ ТекущаяДата _____ Условие ____ ПолныхЛет

    29.02.2008 0:00:00 ___ 01.03.2019 0:00:00 ___ > ___ 12

    29.02.2008 0:00:00 ___ 01.03.2019 0:00:00 ___ >= ___ 12

    Reply
  46. Zurfik

    (45) Попробуйте вот так. Должно все правильно считать.

    Выбор Когда ДеньГода(ДОБАВИТЬКДАТЕ(&ДатаРождения, День, РАЗНОСТЬДАТ(&ДатаРождения, &ТекущаяДата, День)))+(ДЕНЬГОДА(КонецПериода(&ДатаРождения,Год))-ДЕНЬГОДА(&ДатаРождения))>=ДЕНЬГОДА(КонецПериода(&ТекущаяДата,Год))
    Тогда РАЗНОСТЬДАТ(&ДатаРождения, &ТекущаяДата, Год)
    Иначе РАЗНОСТЬДАТ(&ДатаРождения, &ТекущаяДата, Год)-1 Конец КАК ПолныхЛет

    ДатаРождения: 29.02.2019

    ТекущаяДата: 28.02.2019

    Тоже считает правильно, как полных — 11лет.

    Reply
  47. BigB

    Я вот тут уже публиковал свой запрос. Повторю его здесь:

    ВЫБРАТЬ
    Годы.Лет КАК Лет,
    РАЗНОСТЬДАТ(Годы.ДХ, &Д2, МЕСЯЦ) — Годы.Х КАК Месяцев,
    РАЗНОСТЬДАТ(ДОБАВИТЬКДАТЕ(Годы.ДХ, МЕСЯЦ, РАЗНОСТЬДАТ(Годы.ДХ, &Д2, МЕСЯЦ) — Годы.Х), &Д2, ДЕНЬ) КАК Дней
    ИЗ
    (ВЫБРАТЬ
    РАЗНОСТЬДАТ(&Д1, &Д2, ГОД) — ВЫБОР
    КОГДА ДОБАВИТЬКДАТЕ(&Д1, ГОД, РАЗНОСТЬДАТ(&Д1, &Д2, ГОД)) > &Д2
    ТОГДА 1
    ИНАЧЕ 0
    КОНЕЦ КАК Лет,
    ДОБАВИТЬКДАТЕ(&Д1, ГОД, РАЗНОСТЬДАТ(&Д1, &Д2, ГОД) — ВЫБОР
    КОГДА ДОБАВИТЬКДАТЕ(&Д1, ГОД, РАЗНОСТЬДАТ(&Д1, &Д2, ГОД)) > &Д2
    ТОГДА 1
    ИНАЧЕ 0
    КОНЕЦ) КАК ДХ,
    ВЫБОР
    КОГДА ДЕНЬ(&Д1) > ДЕНЬ(&Д2)
    ТОГДА 1
    ИНАЧЕ 0
    КОНЕЦ КАК Х) КАК Годы

    Показать

    Reply
  48. Zurfik

    (47) На датах 29.02.2008 — 28.02.2019

    Он показывает 11лет -1 месяц 31 день.

    Это дата кстати на многих онлайн калькуляторах расчета возраста даёт очень интересные результаты)))

    Reply
  49. Vlad_2008

    Что-то, за два года, мы так и не увидели «правильный» запрос по расчету возраста в Год-Месяц-День … будем ждать дальше ))

    Да, онлайн калькуляторы тоже смотрел, ржал, почти все показывают какую-то ерунду.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *