С недавних пор в свои проекты я начал добавлять общий модуль для работы с датами. Не то чтобы без него было никак не обойтись, но с ним стало удобнее. Тот кто танцевал с бубном вокруг операций с датами на 8-й платформе меня, надеюсь, понимает.
Раскладывание дат по частям, а потом сборка после обработки напильником меня, признаться, не сильно утомляла, до тех пор пока не понадобилось в больших количествах складывать даты и время, находить разности и проверять настали ли заветные моменты. Стал писать запросы, а потом, когда сложность запросов превысила мои скромные способности взял и перенес две безусловно полезные функции языка запросов в прикладной функционал обычного языка. И, признаться, сразу полегчало.
ДОБАВИТЬКДАТЕ и РАЗНОСТЬДАТ по какой-то неведомой для меня причине забыли включить в обычный язык, то ли по недосмотру, то ли для того, чтобы побыстрее заставить переучиться на новый язык запросов. И хотя с момента рождения 8-ки минуло уже уйма лет (в минутах и секундах тоже можно узнать:) адекватная работа с датами так и осталась уделом запросов.
Я не хочу критиковать разработчиков платформы, и не хочу косить стартмани за добрые дела поэтому просто выкладываю содержимое своего стандартного модуля, который я традиционно называю РаботаСДатами. Клиент-серверные настройки модуля за вами, надеюсь только, что все помнят про то, что запросы на стороне клиента не доступны. У меня как правило он строго на сервере. Заветные 10 000 знаков в статье мне тоже не очень то нужны поэтому к делу (или к телу модуля):
Функция РазностьДат(НачальнаяДата,КонечнаяДата,Размерность) Экспорт
Запрос = Новый ("Запрос");
Если ВРег(Размерность) = "СЕКУНДА" Тогда
Запрос.Текст =
"ВЫБРАТЬ
| РАЗНОСТЬДАТ(&НачальнаяДата, &КонечнаяДата, МИНУТА)*60 +
| (СЕКУНДА(&КонечнаяДата) - СЕКУНДА(&НачальнаяДата)) КАК Разность";
Иначе
Запрос.Текст =
"ВЫБРАТЬ
| РАЗНОСТЬДАТ(&НачальнаяДата, &КонечнаяДата, " + Размерность + ") КАК Разность";
КонецЕсли;
Запрос.УстановитьПараметр("НачальнаяДата",НачальнаяДата);
Запрос.УстановитьПараметр("КонечнаяДата",КонечнаяДата);
Возврат Запрос.Выполнить().Выгрузить()[0].Разность;
КонецФункции
Функция ДобавитьКДате(НачальнаяДата,Количество,Размерность) Экспорт
Запрос = Новый ("Запрос");
Запрос.Текст =
"ВЫБРАТЬ
| ДОБАВИТЬКДАТЕ(&НачальнаяДата, " + Размерность + ",&Количество) КАК НоваяДата";
Запрос.УстановитьПараметр("НачальнаяДата",НачальнаяДата);
Запрос.УстановитьПараметр("Количество",Количество);
Возврат Запрос.Выполнить().Выгрузить()[0].НоваяДата;
КонецФункции
Функция ДатаВЕдиницыВремени(НужнаяДата, Размерность) Экспорт
Запрос = Новый ("Запрос");
Если НЕ ВРег(Размерность) = "СЕКУНДА" Тогда
Запрос.Текст =
"ВЫБРАТЬ
| РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1,1,1,0,0,0), &КонечнаяДата, " + Размерность + ") КАК ДатаЧислом";
Иначе
Запрос.Текст =
"ВЫБРАТЬ
| РАЗНОСТЬДАТ(ДАТАВРЕМЯ(1,1,1,0,0,0), НАЧАЛОПЕРИОДА(&КонечнаяДата,МЕСЯЦ), МИНУТА)*60 +
| РАЗНОСТЬДАТ(НАЧАЛОПЕРИОДА(&КонечнаяДата,МЕСЯЦ), &КонечнаяДата, " + Размерность + ") КАК ДатаЧислом";
КонецЕсли;
Запрос.УстановитьПараметр("КонечнаяДата",НужнаяДата);
Возврат Запрос.Выполнить().Выгрузить()[0].ДатаЧислом;
КонецФункции
Функция ЕдиницыВремениВДату(Количество,Размерность) Экспорт
Запрос = Новый ("Запрос");
Если ВРег(Размерность) = "СЕКУНДА" Тогда
Запрос.Текст =
"ВЫБРАТЬ
| ДОБАВИТЬКДАТЕ(ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0), МИНУТА,&КоличествоМ), " + Размерность + ", &КоличествоС) КАК НоваяДата";
КоличествоМ = Цел(Количество/60);
Запрос.УстановитьПараметр("КоличествоМ",КоличествоМ);
Запрос.УстановитьПараметр("КоличествоС",Количество - КоличествоМ*60);
Иначе
Запрос.Текст =
"ВЫБРАТЬ
| ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(1, 1, 1, 0, 0, 0), " + Размерность + ",&Количество) КАК НоваяДата";
Запрос.УстановитьПараметр("Количество",Количество);
КонецЕсли;
Попытка
Возврат Запрос.Выполнить().Выгрузить()[0].НоваяДата;
Исключение
Возврат Дата(1,1,1,0,0,0);
КонецПопытки;
КонецФункции
Функция СуммаДатЧислом(Дата1, Дата2, Размерность) Экспорт
Возврат ДатаВЕдиницыВремени(Дата1,Размерность) + ДатаВЕдиницыВремени(Дата2, Размерность);
КонецФункции
Функция СуммаДатДатой(Дата1,Дата2) Экспорт
СуммаЧислом = СуммаДатЧислом(Дата1,Дата2,"СЕКУНДА");
Возврат ЕдиницыВремениВДату(СуммаЧислом,"СЕКУНДА");
КонецФункции
Теперь немного пояснений и все. Потерпите еще пару минут.
По назначению функций я думаю все понятно, но если нет прошу писать в комменты или личку. Размерность времени стандартная для запросов: СЕКУНДА, МИНУТА, ЧАС, ДЕНЬ, МЕСЯЦ и ГОД.
По немного странному виду функций. Дело в том, что при обработке дат в размерности СЕКУНДА выяснилось, что ядро исполнителя запросов переполняется когда секунд становится немножко больше чем дофига. Пришлось менять основные отрезки на минутную размерность, добавляя потом секундные хвостики. А то иначе 1С пишет много страшных букв, призывая даже SQL в оправдание своей беспомощности перед столь грандиозным количеством разрядов.
А еще есть один интересный эффект, связанный со сложением дат. Внезапно (а как же еще) выяснилось, что 1С не знает, что бывают даты больше 4000 года от Р.Х. Ни в секундах, ни в минутах, ни даже в годах пояснить ему (или ей... я не знаю какого у Вас 1С рода) феномен бесконечности времени и пространства не удалось, поэтому если уж Вы начнете складывать что-то выходящее суммой за рамки то получите 01.01.01 00:00:00. Не обижайтесь и не расстраивайтесь, а лучше представьте, что когда нибудь спустя почти 2000 лет ученые будущего найдут винтажную систему учета 1С и вдруг выяснят, что 4000 годом заканчивается ее календарь и картина мира. В мире восцарится шок и паника - "Древние мудрецы предсказали конец света в 4000 году! Спасайтесь кто может!"
Вот такие любопытные параллели с 2012 годом, когда закончился календарь майя, заложили создатели 1С в свое детище. Может быть всего лишь 1986 лет спуся мир снова сойдет с ума от зловещих предсказаний господина Нуралиева?
Для желающих обязательно что-нибудь скачать и одарить меня $m набросал обработку-демонстратор возможностей. Извините, только управляемая форма.
Ну шутки шутками, а на этом прощаюсь и желаю всем хороших и интересных разработок.