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




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2026-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 не будет опубликован. Обязательные поля помечены *