gifts2017

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

Опубликовал Vlad (vld_trade) в раздел Программирование - Практика программирования

Функция, которая возвращает количество лет, месяцев, дней "полупрописью", т.е. в формате "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", " ") + СтрЗаменить(Суффикс, "#$", "д");
 
 
КонецЕсли;
КонецЕсли;
Возврат РасшифровкаСрокаПолезногоИспользования;
КонецФункции// РасшифровкаСрокаПолезногоИспользования()

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

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Сергей Борисов (juntatalor) 24.04.13 21:38
Функция, безусловно, хорошая.

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

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

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

ЧислоМесяцев % 10 < 5?
13jaguar; elenko1; +2 Ответить 1
2. Vlad (vld_trade) 25.04.13 00:23
(1) Это код МиСофт, который я оставил для сравнения или на случай моего тупизма.
3. Сергей Гуняков (Intervent) 25.04.13 10:44
На входе: 1.2
На выходе: 1 месяц 6 #$ней
Ожидал: 1 месяц 6 дней
4. Vlad (vld_trade) 25.04.13 11:22
(3) Поправил и саму функцию и вставил пояснение про эти самые "#$ни". В 11:20 (GMT+3) публикация обновлена (проверил).
Товарищи программисты! Представленная в публикации функция, если ее вставить в код 1С не заработает!
Удалите из кода строчки, разукрашенные фиолетовым.
5. Сергей Гуняков (Intervent) 25.04.13 13:48
Если решил использовать ЧислоПрописью(), то можно проще:
Функция РасшифровкаСрокаПолезногоИспользования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 читабельней.
Если требуется оптимизированный вариант, то лучше делать без тяжёлой ЧислоПрописью().
13jaguar; vld_trade; +2 Ответить 1
6. Vlad (vld_trade) 25.04.13 14:55
(5) Намного читабельнее, спасибо за наводку про "Периоды", которые "все в одном". В "Периоды", если использовать Структуру вместо текста, можно получить повышение производительности?
7. Сергей Гуняков (Intervent) 25.04.13 16:09
Пожалуйста. :)
Давай замерим (для упрощения выбираю одинаковый номер - 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, Период);
	ЗамерСтруктура = Период;
КонецФункции 
...Показать Скрыть

Итог: прироста производительности нет.
8. Vladimir Иванов (brunet) 28.08.13 18:08
В сроках полезного использования могут быть только целые месяца и никаких дней там не должно быть.
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа