Можно переключать календарь столетиями/десятилетиями/годами/месяцами.
Реализован диапазон дат от 01.01.0001 до 31.12.3999.
Код написан под вариант работы - webhook
Как всё работает, можно посмотреть в видео.
![](https://i.ytimg.com/vi/_x0SIbjFO3I/sddefault.jpg)
Если сильно быстро нажимать, то можно наткнуться на антиспам систему телеграма. По моим наблюдениям быстро отрабатывает 4 нажатия, пятое уже телеграм придерживает.
Если что-то не понятно или есть какие косяки, пишите, исправим.
//
//Формат:
//1 - Выбор даты
//2 - Выбор месяца
//3 - Выбор года
//4 - Выбор десятилетия
Функция Календарь(Формат, ТекущееЗначение, УдалитьКлавиатуру = Ложь) Экспорт
СтрокиКлавиатуры = Новый Массив;
Если Формат = 1 Тогда
ПрефиксФормата = "calendar_1_";
ДатаСтрокой = Формат(НачалоМесяца(ТекущееЗначение), "ДФ=yyyy_MM_dd; ДП=0001_01_01");
//Первая строка кнопок
КнопкиСтрокиКлавиатуры = Новый Массив;
//Кнопка "Влево"
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("<", ПрефиксФормата + "less_" + ДатаСтрокой));
//Кнопка "Текущий месяц"
Месяц = Формат(ТекущееЗначение, "ДФ=MMMM; ДП=Январь");
МесяцСтрокой = Лев(Месяц, 3);
Заголовок = МесяцСтрокой + " " + Формат(ТекущееЗначение, "ДФ=yyyy; ДП=0001");
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(Заголовок, ПрефиксФормата + "changeformat_" + ДатаСтрокой));
//Кнопка "Вправо"
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(">", ПрефиксФормата + "more_" + ДатаСтрокой));
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
//Вторая строка кнопок
КнопкиСтрокиКлавиатуры = Новый Массив;
//Выведем кнопки дней недели
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Пн", "calendar_empty"));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Вт", "calendar_empty"));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Ср", "calendar_empty"));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Чт", "calendar_empty"));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Пт", "calendar_empty"));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Сб", "calendar_empty"));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Вс", "calendar_empty"));
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
//Вывод дат календаря
ТекущийДеньМесяца = НачалоМесяца(ТекущееЗначение);
ПоследнийДеньМесяца = КонецМесяца(ТекущееЗначение);
КолВоПустыхКнопокВНачале = ДеньНедели(НачалоМесяца(ТекущееЗначение)) - 1;
КолВоПустыхКнопокВКонце = 0;
ВсегоКнопок = КолВоПустыхКнопокВНачале + День(ПоследнийДеньМесяца);
Пока ВсегоКнопок%7 <> 0 Цикл
ВсегоКнопок = ВсегоКнопок + 1;
КолВоПустыхКнопокВКонце = КолВоПустыхКнопокВКонце + 1;
КонецЦикла;
КнопкиСтрокиКлавиатуры = Новый Массив;
Для Кнопка = 1 По ВсегоКнопок Цикл //42 если нужно чтобы всегда выводилось 6 строк
//Если закончилась неделя, создадим новый массив кнопок
Если КнопкиСтрокиКлавиатуры.Количество() = 7 Тогда
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
КнопкиСтрокиКлавиатуры = Новый Массив;
КонецЕсли;
//Выведем пустые кнопки
Пока Кнопка <= КолВоПустыхКнопокВНачале Цикл
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(" ", "calendar_empty"));
Кнопка = Кнопка + 1;
Продолжить;
КонецЦикла;
//Выведем дни месяца
Если ТекущийДеньМесяца <= ПоследнийДеньМесяца Тогда
ПорядковыйНомерДняМесяца = Строка(День(ТекущийДеньМесяца));
ДеньМесяца = Формат(ТекущийДеньМесяца, "ДФ=yyyy_MM_dd; ДП=0001_01_01");
//Вывод иконки в кнопку
Если НачалоДня(ТекущийДеньМесяца) = НачалоДня(ТекущаяДата()) Тогда
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(СимволПоХТМЛКоду(9728), ПрефиксФормата + ДеньМесяца));
Иначе
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(ПорядковыйНомерДняМесяца, ПрефиксФормата + ДеньМесяца));
КонецЕсли;
ТекущийДеньМесяца = ТекущийДеньМесяца + 86400;
Продолжить;
КонецЕсли;
//Если вывод дней закончен, выведем недостающие пустые кнопки
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(" ", "calendar_empty"));
КонецЦикла;
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
ИначеЕсли Формат = 2 Тогда
ПрефиксФормата = "calendar_2_";
ДатаСтрокой = Формат(НачалоГода(ТекущееЗначение), "ДФ=yyyy_MM_dd; ДП=0001_01_01");
//Первая строка кнопок
КнопкиСтрокиКлавиатуры = Новый Массив;
//Кнопка "Влево"
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("<", ПрефиксФормата + "less_" + ДатаСтрокой));
//Кнопка "Текущий месяц"
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(Формат(ТекущееЗначение, "ДФ=yyyy; ДП=0001"), ПрефиксФормата + "changeformat_" + ДатаСтрокой));
//Кнопка "Вправо"
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(">", ПрефиксФормата + "more_" + ДатаСтрокой));
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
//Выведем кнопки месяцев
НачалоГода = НачалоГода(ТекущееЗначение);
//Вторая строка кнопок
КнопкиСтрокиКлавиатуры = Новый Массив;
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Янв", ПрефиксФормата + Формат(НачалоГода, "ДФ=yyyy_MM_dd; ДП=0001_01_01")));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Фев", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 1), "ДФ=yyyy_MM_dd")));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Мар", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 2), "ДФ=yyyy_MM_dd")));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Апр", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 3), "ДФ=yyyy_MM_dd")));
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
//Третья строка кнопок
КнопкиСтрокиКлавиатуры = Новый Массив;
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Май", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 4), "ДФ=yyyy_MM_dd")));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Июнь", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 5), "ДФ=yyyy_MM_dd")));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Июль", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 6), "ДФ=yyyy_MM_dd")));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Авг", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 7), "ДФ=yyyy_MM_dd")));
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
//Четвертая строка кнопок
КнопкиСтрокиКлавиатуры = Новый Массив;
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Сен", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 8), "ДФ=yyyy_MM_dd")));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Окт", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 9), "ДФ=yyyy_MM_dd")));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Ноя", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 10), "ДФ=yyyy_MM_dd")));
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("Дек", ПрефиксФормата + Формат(ДобавитьМесяц(НачалоГода, 11), "ДФ=yyyy_MM_dd")));
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
ИначеЕсли Формат = 3 Тогда
ПрефиксФормата = "calendar_3_";
ГодНачалаПериода = Год(ТекущееЗначение) - Год(ТекущееЗначение)%10;
ГодНачалаПериода = ?(ГодНачалаПериода <= 0, 1, ГодНачалаПериода);
ДатаСтрокой = Формат(Дата(ГодНачалаПериода, 1, 1), "ДФ=yyyy_MM_dd; ДП=0001_01_01");
//Первая строка кнопок
КнопкиСтрокиКлавиатуры = Новый Массив;
//Кнопка "Влево"
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("<", ПрефиксФормата + "less_" + ДатаСтрокой));
//Кнопка "Текущий месяц"
ПервыйГод = ((Год(ТекущееЗначение) - Год(ТекущееЗначение)%10)) - 1;
ПервыйГод = ?(ПервыйГод <= 0, 1, ПервыйГод);
ПоследнийГод = ПервыйГод + 11;
ПоследнийГод = ?(ПоследнийГод >= 4000, 3999, ПоследнийГод);
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(ПериодЛет(ТекущееЗначение, 10, 4), ПрефиксФормата + "changeformat_" + ДатаСтрокой));
//Кнопка "Вправо"
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(">", ПрефиксФормата + "more_" + ДатаСтрокой));
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
//Выведем кнопки годов
КнопкиСтрокиКлавиатуры = Новый Массив;
Для Год = ПервыйГод По ПоследнийГод Цикл
//Каждые 4 кнопки, создадим новый массив кнопок
Если КнопкиСтрокиКлавиатуры.Количество() = 4 Тогда
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
КнопкиСтрокиКлавиатуры = Новый Массив;
КонецЕсли;
//Выведем года
ГодСтрокой = Формат(Год, "ЧГ=0");
ДатаСтрокой = Формат(Дата(Год, 1, 1), "ДФ=yyyy_MM_dd; ДП=0001_01_01");
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(ГодСтрокой, "calendar_3_" + ДатаСтрокой));
КонецЦикла;
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
ИначеЕсли Формат = 4 Тогда
ПрефиксФормата = "calendar_4_";
ГодНачалаПериода = Год(ТекущееЗначение) - Год(ТекущееЗначение)%100;
ГодНачалаПериода = ?(ГодНачалаПериода <= 0, 1, ГодНачалаПериода);
ДатаСтрокой = Формат(Дата(ГодНачалаПериода, 1, 1), "ДФ=yyyy_MM_dd; ДП=0001_01_01");
//Первая строка кнопок
КнопкиСтрокиКлавиатуры = Новый Массив;
//Кнопка "Влево"
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры("<", ПрефиксФормата + "less_" + ДатаСтрокой));
//Кнопка "Текущий месяц"
Десятилетие = ((Год(ТекущееЗначение) - Год(ТекущееЗначение)%100)) - 10;
Десятилетие = ?(Десятилетие <= 0, 1, Десятилетие);
ПоследнееДесятилетие = Десятилетие + 110;
ПоследнееДесятилетие = ?(ПоследнееДесятилетие >= 4000, 3999, ПоследнееДесятилетие);
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(ПериодЛет(ТекущееЗначение, 100, 4), ПрефиксФормата + "changeformat_" + ДатаСтрокой));
//Кнопка "Вправо"
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(">", ПрефиксФормата + "more_" + ДатаСтрокой));
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
//Выведем кнопки десятилетий
КнопкиСтрокиКлавиатуры = Новый Массив;
Пока Десятилетие <= ПоследнееДесятилетие Цикл
//Каждые 4 кнопки, создадим новый массив кнопок
Если КнопкиСтрокиКлавиатуры.Количество() = 4 Тогда
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
КнопкиСтрокиКлавиатуры = Новый Массив;
КонецЕсли;
//Выведем десятилетия
ДесятилетиеСтрокой = ПериодЛет(Десятилетие, 10);
КнопкиСтрокиКлавиатуры.Добавить(КнопкаКлавиатуры(ДесятилетиеСтрокой, "calendar_4_" + Формат(Дата(Десятилетие, 1, 1), "ДФ=yyyy_MM_dd; ДП=0001_01_01")));
Десятилетие = Десятилетие + 10;
КонецЦикла;
СтрокиКлавиатуры.Добавить(КнопкиСтрокиКлавиатуры);
КонецЕсли;
ДанныеКлавиатуры = Новый Структура;
ДанныеКлавиатуры.Вставить("inline_keyboard", СтрокиКлавиатуры);
Если УдалитьКлавиатуру Тогда
ДанныеКлавиатуры.Вставить("remove_keyboard", Истина);
КонецЕсли;
Возврат ДанныеКлавиатуры;
КонецФункции
Функция ОбработатьВходящийЗапрос(Запрос) Экспорт
ТелоЗапроса = Запрос.ПолучитьТелоКакСтроку();
ДанныеЗапроса = ОбъектДжсон(ТелоЗапроса);
Если ДанныеЗапроса.Свойство("callback_query") Тогда
Возврат ОбработатьОбратныйВызов(ДанныеЗапроса.callback_query);
КонецЕсли;
Возврат ОтветОк();
КонецФункции
Функция ОбработатьОбратныйВызов(ДанныеОбратногоВызова)
//Обработка нажатия кнопки клавиатуры выбора даты
Если Лев(ДанныеОбратногоВызова.Data, 8) = "calendar" Тогда
Возврат ОбработатьНажатиеКнопкиКалендаря(ДанныеОбратногоВызова);
КонецЕсли;
Возврат ОтветОк();
КонецФункции
Функция ОбработатьНажатиеКнопкиКалендаря(ДанныеОбратногоВызова)
Если ДанныеОбратногоВызова.data = "calendar_empty" Тогда
Возврат ОтветОк();
КонецЕсли;
ИдентификаторЧата = ДанныеОбратногоВызова.from.id;
ИдентификаторСообщения = ДанныеОбратногоВызова.message.message_id;
ТекущийФормат = Число(Сред(ДанныеОбратногоВызова.data, 10, 1));
ПрефиксФормата = Лев(ДанныеОбратногоВызова.data, 11);
ДатаСтрокой = Прав(ДанныеОбратногоВызова.data, 10);
//Изменение формата календаря
Если СтрНайти(ДанныеОбратногоВызова.data, "changeformat") Тогда
НовыйФормат = ?(ТекущийФормат <> 4, ТекущийФормат + 1, 1);
ДанныеОтвета = ИзменитьКлавиатуруСообщения(ИдентификаторЧата, ИдентификаторСообщения, Календарь(НовыйФормат, ДатаИзСтроки(ДатаСтрокой)));
Возврат ОтветДжсон(ДанныеОтвета);
ИначеЕсли СтрНайти(ДанныеОбратногоВызова.data, "less") Тогда //Уменьшение периода
НоваяДата = ДатаИзСтроки(ДатаСтрокой);
Если НоваяДата = Дата(1, 1, 1) Тогда
НовыйПериод = НоваяДата;
ИначеЕсли ТекущийФормат = 1 Тогда
НовыйПериод = ДобавитьМесяц(НоваяДата, -1);
ИначеЕсли ТекущийФормат = 2 Тогда
НовыйПериод = ДобавитьМесяц(НоваяДата, -12);
ИначеЕсли ТекущийФормат = 3 Тогда
НовыйПериод = ДобавитьМесяц(НоваяДата, -120);
ИначеЕсли ТекущийФормат = 4 Тогда
НовыйПериод = ДобавитьМесяц(НоваяДата, -1200);
КонецЕсли;
ДанныеОтвета = ИзменитьКлавиатуруСообщения(ИдентификаторЧата, ИдентификаторСообщения, Календарь(ТекущийФормат, НовыйПериод));
Возврат ОтветДжсон(ДанныеОтвета);
ИначеЕсли СтрНайти(ДанныеОбратногоВызова.data, "more") Тогда //Увеличение периода
НоваяДата = ДатаИзСтроки(ДатаСтрокой);
Если ТекущийФормат = 1 Тогда
НовыйПериод = ДобавитьМесяц(НоваяДата, 1);
ИначеЕсли ТекущийФормат = 2 Тогда
НовыйПериод = ДобавитьМесяц(НоваяДата, 12);
ИначеЕсли ТекущийФормат = 3 Тогда
НовыйПериод = ДобавитьМесяц(НоваяДата, 120);
ИначеЕсли ТекущийФормат = 4 Тогда
НовыйПериод = ДобавитьМесяц(НоваяДата, 1200);
КонецЕсли;
Если НовыйПериод >= Дата(3999, 12, 1) Тогда
НовыйПериод = Дата(3999, 12, 1);
КонецЕсли;
ДанныеОтвета = ИзменитьКлавиатуруСообщения(ИдентификаторЧата, ИдентификаторСообщения, Календарь(ТекущийФормат, НовыйПериод));
Возврат ОтветДжсон(ДанныеОтвета);
Иначе//Выбор Десятилетия/Года/Месяца/Даты
Если ТекущийФормат = 1 Тогда
//Тут пишем код обработки выбранной даты
Иначе
ДанныеОтвета = ИзменитьКлавиатуруСообщения(ИдентификаторЧата, ИдентификаторСообщения, Календарь(ТекущийФормат -1, ДатаИзСтроки(ДатаСтрокой)));
Возврат ОтветДжсон(ДанныеОтвета);
КонецЕсли;
КонецЕсли;
Возврат ОтветОк();
КонецФункции
Функция ИзменитьКлавиатуруСообщения(ИдентификаторЧата, ИдентификаторСообщения, Клавиатура)
ДанныеСообщения = Новый Структура;
ДанныеСообщения.Вставить("method", "editMessageReplyMarkup");
ДанныеСообщения.Вставить("chat_id", ИдентификаторЧата);
ДанныеСообщения.Вставить("message_id", ИдентификаторСообщения);
ДанныеСообщения.Вставить("reply_markup", Клавиатура);
Возврат ДанныеСообщения;
КонецФункции
Функция КнопкаКлавиатуры(Текст = "", ДанныеОбратногоВызова = "", Ссылка = "")
КнопкаКлавиатуры = Новый Структура;
КнопкаКлавиатуры.Вставить("text", Текст);
Если ЗначениеЗаполнено(ДанныеОбратногоВызова) Тогда
КнопкаКлавиатуры.Вставить("callback_data", ДанныеОбратногоВызова);
КонецЕсли;
Если ЗначениеЗаполнено(Ссылка) Тогда
КнопкаКлавиатуры.Вставить("url", Ссылка);
КонецЕсли;
Возврат КнопкаКлавиатуры;
КонецФункции
Функция ДатаИзСтроки(ДатаСтрокой)
МассивЗначений = СтрРазделить(ДатаСтрокой, "_");
Год = МассивЗначений[0];
Месяц = МассивЗначений[1];
День = МассивЗначений[2];
Возврат Дата(Год, Месяц, День);
КонецФункции
Функция ПериодЛет(ВходящаяДата, Период, КоличествоСимволовОкончанияПериода = 2)
Если ТипЗнч(ВходящаяДата) = Тип("Число") Тогда
ВходящаяДата = ?(ВходящаяДата <= 0, 1, ВходящаяДата);
ДатаПериода = Дата(ВходящаяДата, 1, 1);
Иначе
ДатаПериода = ВходящаяДата;
КонецЕсли;
НачалоПериода = Год(ДатаПериода) - Год(ДатаПериода)%Период;
КонецПериода = НачалоПериода + Период - 1;
Возврат Формат(НачалоПериода, "ЧН=00; ЧГ=0") + "-" + Прав(Формат(КонецПериода, "ЧГ=0"), КоличествоСимволовОкончанияПериода);
КонецФункции
Функция СимволПоХТМЛКоду(Код) Экспорт
Буфер = Новый БуферДвоичныхДанных(4);
Буфер.ЗаписатьЦелое32(0, Код, ПорядокБайтов.BigEndian);
Возврат ПолучитьСтрокуИзБуфераДвоичныхДанных(Буфер, "UTF-32BE");
КонецФункции
Функция ОтветОк() Экспорт
Результат = Новый Структура;
Результат.Вставить("Result", "Ok");
Возврат ОтветДжсон(Результат);
КонецФункции
Функция СтрокаДжсон(ОбъектJSON) Экспорт
ПараметрыЗаписиJSON = Новый ПараметрыЗаписиJSON(, Символы.Таб);
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку(ПараметрыЗаписиJSON);
ЗаписатьJSON(ЗаписьJSON, ОбъектJSON);
Возврат ЗаписьJSON.Закрыть()
КонецФункции
Функция ОбъектДжсон(СтрокаJSON) Экспорт
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
ОбъектJSON = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();
Возврат ОбъектJSON;
КонецФункции
Функция ОтветДжсон(Объект, КодСостояния = 200) Экспорт
Ответ = Новый HTTPСервисОтвет(КодСостояния);
Ответ.Заголовки.Вставить("Content-Type", "application/json");
Ответ.УстановитьТелоИзСтроки(СтрокаДжсон(Объект));
Возврат Ответ;
КонецФункции