Вывод количества лет, месяцев, дней "полупрописью"

Функция, которая возвращает количество лет, месяцев, дней "полупрописью", т.е. в формате "4 года 5 месяцев 3 дня".

Бухгалтер попросила поправить разрядность количества месяцев срока полезного использования объектов основных средств (не изучал я законодательства, а бухгалтер уверяет, что у нее есть объект со сроком 109.2 месяца).

Полез я в типовую бухгалтерию от МиСофт, поправил, где только возможно, тестирую и получаю ошибку в непредвиденном месте: есть оказывается функция, которая возвращает вместо 13 месяцев строку «1 год 1 месяц». А у меня дробное число, вот и нарвался я на ошибку.

Скажу честно, мне не понравилась функция, которая вообще не использует возможности 1С по использованию т.н. «Предмета исчисления», вот и решил переписать.

Маленькое пояснение.

В одном из мест кода «день» представлен как «#$ень». Чтобы представить число дней в виде именно числа дальше по коду ищется этот самый «день» и удаляется все, что до него. Когда я искал просто букву «д», то получал результат в виде «12 дцать дней», «20 дцать дней», а усложнять код для поиска «день, дня, дней» не захотел.

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

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

Собственно функция:

Функция РасшифровкаСрокаПолезногоИспользования(СрокПолезногоИспользования) Экспорт

РасшифровкаСрокаПолезногоИспользования = "";
Если ЗначениеЗаполнено(СрокПолезногоИспользования) Тогда
ЧастьМесяца = СрокПолезногоИспользования - Цел(СрокПолезногоИспользования);
ЧислоДней = Цел(30 * ЧастьМесяца);
Суффикс = "";
Если НЕ ЧислоДней = 0 Тогда
Суффикс = ЧислоПрописью(ЧислоДней, "НД = Ложь", "#$ень, #$ня, #$ней, м,,,,,0");
ПозицияПослеПрописи = Найти(Суффикс, "#$");
Суффикс = Сред(Суффикс, ПозицияПослеПрописи);
Суффикс = " " + СокрЛП(ЧислоДней) + " " + Суффикс;
КонецЕсли;
ЧислоЛет = Цел(СрокПолезногоИспользования / 12);
ЧислоМесяцев = Цел((СрокПолезногоИспользования % 12));
ЧислоЛетМесяцев = ЧислоЛет + ЧислоМесяцев/100;
Если ЧислоЛет = 0 Тогда
СтрокаЧислоЛетМесяцев = ЧислоПрописью(ЧислоМесяцев, "НД = Ложь", "месяц, месяца, месяцев, м,,,,,0");
СтрокаПоиска = "месяц";
Иначе
СтрокаЧислоЛетМесяцев = ЧислоПрописью(ЧислоЛетМесяцев, ?(ЧислоМесяцев = 0, "НД = Ложь", "НД = Истина"), "год, года, лет, м" + ?(ЧислоМесяцев = 0, ",,,,,0" ,", месяц, месяца, месяцев, м, 2"));
СтрокаПоиска = "год";
КонецЕсли;
ПозицияПослеЛет = Найти(СтрокаЧислоЛетМесяцев, СтрокаПоиска);
Если ПозицияПослеЛет = 0 Тогда
ПозицияПослеЛет = Найти(СтрокаЧислоЛетМесяцев, "лет");
Если ПозицияПослеЛет = 0 Тогда
//Фигня какая-то, оставим алгоритм МиСофт
От сих старый код типовой конфигурации
**************************************
Если НЕ (ЧислоЛет = 0) Тогда
// Построим строку с числом лет
Если (СтрДлина(ЧислоЛет) > 1) И (Число(Сред(ЧислоЛет, СтрДлина(ЧислоЛет) - 1, 1)) = 1) Тогда
СтрокаГод = " лет";
ИначеЕсли Число(Прав(ЧислоЛет, 1)) = 1 Тогда
СтрокаГод = " год";
ИначеЕсли (Число(Прав(ЧислоЛет, 1)) > 1) И (Число(Прав(ЧислоЛет, 1)) < 5) Тогда
СтрокаГод = " года";
Иначе
СтрокаГод = " лет";
КонецЕсли;
РасшифровкаСрокаПолезногоИспользования = РасшифровкаСрокаПолезногоИспользования + Строка(ЧислоЛет) + СтрокаГод;
КонецЕсли;
Если НЕ (ЧислоМесяцев = 0) Тогда
// Построим строку с числом месяцев
Если (СтрДлина(Цел(ЧислоМесяцев)) > 1) И (Число(Сред(Цел(ЧислоМесяцев), СтрДлина(Цел(ЧислоМесяцев)) - 1, 1)) = 1) Тогда
СтрокаМесяц = " месяцев";
ИначеЕсли Число(Прав(Цел(ЧислоМесяцев), 1)) = 1 Тогда
СтрокаМесяц = " месяц";
ИначеЕсли (Число(Прав(Цел(ЧислоМесяцев), 1)) > 1) И (Число(Прав(Цел(ЧислоМесяцев), 1)) < 5) Тогда
СтрокаМесяц = " месяца";
Иначе
СтрокаМесяц = " месяцев";
КонецЕсли;
РасшифровкаСрокаПолезногоИспользования = РасшифровкаСрокаПолезногоИспользования + ?(НЕ ЗначениеЗаполнено(РасшифровкаСрокаПолезногоИспользования), "", " ") + Строка(ЧислоМесяцев) + СтрокаМесяц;
КонецЕсли;
РасшифровкаСрокаПолезногоИспользования = "(" + РасшифровкаСрокаПолезногоИспользования + ")";
 
**************************************
До сих старый код типовой конфигурации
 

КонецЕсли;
КонецЕсли;
Если РасшифровкаСрокаПолезногоИспользования = "" Тогда
РасшифровкаСрокаПолезногоИспользования = СокрЛП(?(ЧислоЛет = 0, ЧислоМесяцев, ЧислоЛет)) + " " + Сред(СтрокаЧислоЛетМесяцев, ПозицияПослеЛет);
//Уберем обязательное двузначное число месяцев, если оно получилось:
РасшифровкаСрокаПолезногоИспользования = СтрЗаменить(РасшифровкаСрокаПолезногоИспользования, " 0", " ") + СтрЗаменить(Суффикс, "#$", "д");
 
 
КонецЕсли;
КонецЕсли;
Возврат РасшифровкаСрокаПолезногоИспользования;
КонецФункции// РасшифровкаСрокаПолезногоИспользования()

Здесь же оставил часть кода МиСофта на непредвиденные мною ситуации.

8 Comments

  1. juntatalor

    Функция, безусловно, хорошая.

    Но вы слышали, что вместо того, чтобы использовать вот такие конструкции:

    (Число(Прав(Цел(ЧислоМесяцев), 1)) < 5)

    Можно использовать операцию остатка целочисленного деления?

    ЧислоМесяцев % 10 < 5?

    Reply
  2. vld_trade

    (1) Это код МиСофт, который я оставил для сравнения или на случай моего тупизма.

    Reply
  3. Intervent

    На входе: 1.2

    На выходе: 1 месяц 6 #$ней

    Ожидал: 1 месяц 6 дней

    Reply
  4. vld_trade

    (3) Поправил и саму функцию и вставил пояснение про эти самые «#$ни». В 11:20 (GMT+3) публикация обновлена (проверил).

    Товарищи программисты! Представленная в публикации функция, если ее вставить в код 1С не заработает!

    Удалите из кода строчки, разукрашенные фиолетовым.

    Reply
  5. Intervent

    Если решил использовать ЧислоПрописью(), то можно проще:

    Функция РасшифровкаСрокаПолезногоИспользования2(СрокПолезногоИспользования) Экспорт
    НазванияПериодов = «день,дня,дней,месяц,месяца,месяцев,год,года,лет»;
    Периоды = СтрЗаменить(НазванияПериодов, «,», Символы.ПС);
    СрокВДнях = Цел(СрокПолезногоИспользования * 30);
    ЧислоДней    =  СрокВДнях % 30;
    ЧислоМесяцев = (СрокВДнях — ЧислоДней) / 30 % 12;
    ЧислоЛет     = (СрокВДнях — ЧислоМесяцев * 30 — ЧислоДней) / 360;
    Возврат СокрЛ(?(СрокВДнях < 30, «0 месяцев», «»)
    + Полупропись(ЧислоЛет,     Периоды, 6)
    + Полупропись(ЧислоМесяцев, Периоды, 3)
    + Полупропись(ЧислоДней,    Периоды));
    КонецФункции
    
    Функция Полупропись(Число, Периоды, Сдвиг = 0) Экспорт
    Если Число > 0 Тогда
    Пропись = ЧислоПрописью(Число,, «1,2,3,м,,,,,0″);
    Возврат » » + Число + » » + СтрПолучитьСтроку(Периоды, Сдвиг + Прав(Пропись, 1));
    КонецЕсли;
    КонецФункции

    Показать

    Тест на правильность:

    &НаКлиенте
    Процедура Команда1(Команда)
    Тест();
    КонецПроцедуры
    
    &НаСервереБезКонтекста
    Процедура Тест()
    Для Месяцы = 0 По 300 Цикл
    Для Дни = 0 По 30 Цикл
    Срок = Месяцы + Дни / 30 + 0.001;
    Вариант1 = РасшифровкаСрокаПолезногоИспользования (Срок);
    Вариант2 = РасшифровкаСрокаПолезногоИспользования2(Срок);
    Если Вариант1 <> Вариант2 Тогда
    Сообщить(«Вариант1: » + Вариант1);
    Сообщить(«Вариант2: » + Вариант2);
    Возврат;
    КонецЕсли;
    КонецЦикла;
    КонецЦикла;
    Сообщить(«Функции совпадают»);
    КонецПроцедуры
    

    Показать

    По замерам, Вариант1/Вариант2 = 9/11 сек. (на 301*31 итераций), зато Вариант2 читабельней.

    Если требуется оптимизированный вариант, то лучше делать без тяжёлой ЧислоПрописью().

    Reply
  6. vld_trade

    (5) Намного читабельнее, спасибо за наводку про «Периоды», которые «все в одном». В «Периоды», если использовать Структуру вместо текста, можно получить повышение производительности?

    Reply
  7. Intervent

    Пожалуйста. 🙂

    Давай замерим (для упрощения выбираю одинаковый номер — 1):

    Процедура Тест()
    Для Счёт = 1 По 99999 Цикл
    Вариант2 = ЗамерМногострочнаяСтрока(); //1,315
    Вариант3 = ЗамерМассив();              //1,363
    Вариант4 = ЗамерСтруктура();           //2,050
    КонецЦикла;
    КонецПроцедуры
    
    &НаСервереБезКонтекста
    Функция ЗамерМногострочнаяСтрока()
    НазванияПериодов = «день,дня,дней,месяц,месяца,месяцев,год,года,лет»;
    Периоды = СтрЗаменить(НазванияПериодов, «,», Символы.ПС);
    ЗамерМногострочнаяСтрока = СтрПолучитьСтроку(Периоды,1);
    КонецФункции
    
    &НаСервереБезКонтекста
    Функция ЗамерМассив()
    Массив = Новый Массив(9);
    Массив[0] = «день»;  Массив[3] = «месяц»;   Массив[6] = «год»;
    Массив[1] = «дня»;   Массив[4] = «месяца»;  Массив[7] = «года»;
    Массив[2] = «дней»;  Массив[5] = «месяцев»; Массив[8] = «лет»;
    ЗамерМассив = Массив[1];
    КонецФункции
    
    &НаСервереБезКонтекста
    Функция ЗамерСтруктура()
    Период = «»;
    Структура = Новый Структура;
    Структура.Вставить(«i1», «день»);
    Структура.Вставить(«i2», «дня»);
    Структура.Вставить(«i3», «дней»);
    Структура.Вставить(«i4», «месяц»);
    Структура.Вставить(«i5», «месяца»);
    Структура.Вставить(«i6», «месяцев»);
    Структура.Вставить(«i7», «год»);
    Структура.Вставить(«i8», «года»);
    Структура.Вставить(«i9», «лет»);
    Структура.Свойство(«i» + 1, Период);
    ЗамерСтруктура = Период;
    КонецФункции
    

    Показать

    Итог: прироста производительности нет.

    Reply
  8. brunet

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

    Reply

Leave a Comment

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