Разность дат

Иногда требуется получить разность дат в виде типа: 5 дней 6 месяцев 4 года. Это можно сделать с помощью запроса, получив на выходе сразу 3 нужных числа, без последующих вычислений.

Вот отрывок кода:

 Запрос = Новый запрос("ВЫБРАТЬ
| ВЫБОР
|  КОГДА РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ДЕНЬ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, МЕСЯЦ), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ), ДЕНЬ) < 0
|   ТОГДА ДЕНЬ(КОНЕЦПЕРИОДА(&Д1, МЕСЯЦ)) + РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ДЕНЬ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, МЕСЯЦ), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ), ДЕНЬ)
|  ИНАЧЕ РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ДЕНЬ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, МЕСЯЦ), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ), ДЕНЬ)
| КОНЕЦ КАК Дни,
| ВЫБОР
|  КОГДА РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ДЕНЬ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, МЕСЯЦ), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ), ДЕНЬ) < 0
|   ТОГДА ВЫБОР
|     КОГДА РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, ГОД), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ГОД), МЕСЯЦ) < 0
|      ТОГДА 12 + РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, ГОД), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ГОД), МЕСЯЦ)
|     ИНАЧЕ РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, ГОД), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ГОД), МЕСЯЦ)
|    КОНЕЦ - 1
|  ИНАЧЕ ВЫБОР
|    КОГДА РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, ГОД), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ГОД), МЕСЯЦ) < 0
|     ТОГДА 12 + РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, ГОД), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ГОД), МЕСЯЦ)
|    ИНАЧЕ РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, ГОД), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ГОД), МЕСЯЦ)
|   КОНЕЦ
| КОНЕЦ КАК Месяцы,
| ВЫБОР
|  КОГДА РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), МЕСЯЦ) - РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&Д1, ГОД), НАЧАЛОПЕРИОДА(ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ГОД), МЕСЯЦ) < 0
|   ТОГДА РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ГОД) - 1
|  ИНАЧЕ РАЗНОСТЬДАТ(&Д1, ДОБАВИТЬКДАТЕ(&Д2, ДЕНЬ, 1), ГОД)
| КОНЕЦ КАК Годы");


Запрос.УстановитьПараметр("Д2",КонечнаяДата);
Запрос.УстановитьПараметр("Д1",НачальнаяДата);
ТабДМГ=Запрос.Выполнить().Выгрузить();
Стр = ТабДМГ[0];

Сообщить("Разность дат составляет: "+Стр.Дни+" дней "+Стр.Месяцы+" месяцев "+Стр.Годы+" лет ");

Конечно, можно ещё дописать правильность написания дней, месяцев и лет в зависимости от числа. Но это уже другая тема.

Тут мы вычисляем:

 * для дней — берем общее количество дней в интервале и отнимаем уже учтенное количество дней, а это разница между началами наших месяцев из дат. Это число будет учтено в строке с раcчетом месяца. Но при рачете дней проверяем число полученных дней на отрицателность. Если число дней отрицательно — значит мы перешли в следующий месяц, но фактически месяц между датами еще не прошел. Чтобы учесть это, мы прибавляем к отрицательному числу дней количество дней в месяце первой даты.

 * для месяцев — так же берем общее количество месяцев и отнимаем уже учтенное количество месяцев, а это разница между началами наших лег. Это число уже тоже будет учтено в строке с раcчетом года. Но при расчете месяца также проверяем число полученных месяцев на отрицательность. Если число месяцев отрицательно — значит мы перешли в новый год, но фактически год между датами ещё не прошел. Чтобы учесть это, мы прибавляем к отрицательному числе месяцев 12.

 * для года  — берем количество лет между датами. Но если количество месяцев было отрицательным, то мы отнимаем один год, т.к. он, все же, ещё не прошел.

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

Ну, вот как-то так. Если будут какие-то вопросы — пишите.

24 Comments

  1. rybusha

    (0)Идея хорошая, но ошибка в днях.

    Reply
  2. user623969_dusa

    високосные года отрабатывает?

    Reply
  3. jun-ko

    Високосные отрабатывает.

    С днями надо сделать так же как с месяцами. Сейчас сделаю.

    Reply
  4. jun-ko

    Дни исправлены ) Спасибо за замечание.

    Reply
  5. rybusha

    (4)Теперь+

    Reply
  6. jun-ko

    (5) Благодарю )

    Reply
  7. user599011_nusmancrb

    Отлично. Возьму на вооружение)

    Reply
  8. nytlenc

    Орда непросвещённых, восторгаясь творением выстроились ставить звезды статьям об элементарных вещах!

    Скоро статья в которой будет написано 2 + 2 = 4 будет собирать тысячи лайков….

    Reply
  9. jun-ko

    (8) Да. Не все достигли такого уровня просвещения, как вы. )

    Reply
  10. sssss_aaaaa_2011

    (0) А по какой из существующих методик считает сей запрос? Или вы не в курсе о существовании таких? Или может таки появилась одна универсальная методика? Где о ней почитать? В каких месяцах считаем? В январях? Февралях? Средних? Фиксированных?

    Почему разница между 29.01.2017 и 01.03.2017 по результатам запроса составляет 1 мес и 4 дня? С какого перепугу? Почему не 1 мес и 1 день под одной самой распространенной методике? Или 0 мес и 30 дней по другой распространенной методике?

    Reply
  11. psih12

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

    ДатаНачала — 01.01

    ДатаКонца — 05.01

    Количество дней продаж у менеджеров — 5-1= 4 дня

    А на самом деле — 5-1+1=5 дней.

    Поэтому поддерживаю автора комментария в том,что в запрос необходимо добавить ещё один параметр — учитывать начальную дату или нет — и, соответственно, дописать запрос.

    Reply
  12. DoctorRoza

    Не вникая в запрос, что если находить РАЗНОСТЬДАТ в виде секунд, а потом их превращать в годы/месяцы/дни? Или, все-таки, городить огород!?

    Reply
  13. sssss_aaaaa_2011

    (13) Сколько секунд в месяце? В каком? А считаем в каких месяцах?

    ps: одни пытаются создать вечный двигатель, другие простую формулу расчета разницы дат в нефиксированных единицах измерения. И те, и другие, видимо, до сих пор не знают, что уже доказана невозможность сделать желаемое по законам физики/математики. И тем, и другим кажется, что вот только чуть-чуть доработать и все будет ништяк…

    Reply
  14. rpgshnik

    Работает, славно!

    Reply
  15. maxis33

    Соглашусь про наличие кучи методик.

    Например у нас широко используется функция считающая количество месяцев для периода, причем результатом является, например, число 3,43434.. это происходит из за того, что «ставка» — годовая, и выставляется каждый месяц в размере 1/12.

    Reply
  16. DrAku1a

    Ох, люблю я эти извращения! )

    Reply
  17. Altair777

    (0) А почему именно запросом?

    Reply
  18. duhh

    Криво считает, например 19.11.16 и 31.10.17.

    Reply
  19. sdjoker

    а я накидал функцию

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

    Показать

    Reply
  20. Tolpinski

    На «КОНЕЦ — 1» в каком случае переходим?

    09.02.2011 — 04.02.2012 например

    Reply
  21. Tolpinski

    В ЗУП 2.5 есть функция в модуле ОбщегоНазначенияЗК, работает на мой взгляд корректнее, да и проще визуально

    // Процедура вычисляет количество лет, месяцев и дней между двумя датами

    //

    // Параметры

    // Дата1 – дата, первая дата (более поздняя, часто текущая, стаж определяется по состоянию на эту дату)

    // Дата2 – дата, вторая дата (ранняя дата, с нее начинается «течение» стажа)

    // Лет – Число, в этот параметр будет записано кол-во лет между двумя датами (Дата1-Дата2)

    // Месяцев – Число, в этот параметр будет записано кол-во месяцев между двумя датами (Дата1-Дата2)

    // Дней – Число, в этот параметр будет записано кол-во дней между двумя датами (Дата1-Дата2)

    //

    Процедура РазобратьРазностьДат(Дата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) Тогда

    Дней = Дней + ((ДобавитьМесяц(Дата1,-Лет*12-Месяцев)-Дней*86400) — Дата2)/86400;

    //(День(КонецМесяца(Дата2)) — День(НачалоМесяца(Дата2))) — (День(КонецМесяца(ДобавитьМесяц(Дата1,-1))) — День(НачалоМесяца(ДобавитьМесяц(Дата1,-1))));

    КонецЕсли;

    КонецЕсли;

    КонецПроцедуры // РазобратьРазностьДат

    Reply
  22. BigB

    Добавлю свой запрос:

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

    Показать

    Тренировался на датах:

    Д1=13.12.1976

    Д2=10.08.2017

    Д1=29.01.2017

    Д2=01.03.2017

    Д1=19.11.2016

    Д2=31.10.2017

    Д1=09.02.2011

    Д2=04.02.2012

    И сверял с онлайн калькулятором.

    Вроде всё работает правильно.

    Reply
  23. spacecraft

    (23)

    Вроде всё работает правильно.

    не совсем…

    Д1 = 01.03.2015

    Д2 = 29.02.2016

    Лет = 1

    Месяц = -1

    Дней = 28

    Reply
  24. 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
    КОНЕЦ КАК Х) КАК Годы

    Показать

    Период: 31.10.1960 — 30.09.2001

    Если смотреть эти калькуляторы

    Калькулятов дней

    Калькулятор дней

    то получается: 40 лет 10 месяцев 30 дней

    А если смотреть эти

    Калькулятор подсчёта трудового стажа

    Калькулятор стажа

    то получается: 40 лет 11 месяцев

    Вопрос к знатокам: Какой алгоритм правильный?

    Reply

Leave a Comment

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