Определение текущей даты на основании данных онлайн сервисов

Программирование - Практика программирования

конфигурация 8.х

5
Пример получения текущей даты-времени на примере сервиса yandex.ru

Т.к. в свое время я не нашел внятных примеров, решил выложить свой пример для yandex, может кому поможет и сократит время.

Основным родом моей деятельности является синхронизация данных между УС на базе 1С и различными другим базами данных.

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

Нередко пользователи переводят дату на компьютере в будущее-прошлое. Причины для этого разные и не буду в них вдаваться. Важно, что при подобной смене метод 1С ТекущаяДата() обращается к параметрам системы и возвращает дату будущую-прошлую, что приводит в моем случае к неверной установке периода синхронизации и, как следствие, к к получению неактуальных данных.

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

Поэтому было принято решение переопределять текущую дату на основании данных онлайн сервиса.

Были эксперименты с сервисами time.nist.gov, ntp.psn.ru, time.windows.com, timeserver.ru, wwp.greenwichmeantime.com, но yandex.ru показался мне наиболее удачным в плане надежности, т.к. с прочими периодически возникали проблемы соединения.

Для подключения к сервису используется типовое HTTPСоединение и дальнейшее распарсивание строки ответа с приведением к типу Дата.

	//Тело POST-запроса, сюда yandex вернет ответ 
	ТелоPOSTЗапроса = "";
	
	//POST-запрос на сервер-приёмник
	СерверПриемник = "www.yandex.ru"; //имя сервера без протокола (http) и порта
	
	// сформируем пустой запрос, нам от yandex ничего не нужно, а при пустом запросе в ответ он как и пошлет текущее дату-время
	ЗаголовкиHTTP = Новый Соответствие;
	ЗаголовкиHTTP.Вставить("Content-Type", "text/html;charset=UTF-8");

	Таймаут = 120;
	SSL =  Новый ЗащищенноеСоединениеOpenSSL();
	НТТР = Новый HTTPСоединение(СерверПриемник, , , , , Таймаут, SSL);

	Попытка
		//Формируем HTTP запрос
		ЗапросHTTP = Новый HTTPЗапрос("", ЗаголовкиHTTP);
		ЗапросHTTP.УстановитьТелоИзСтроки(ТелоPOSTЗапроса); //Устанавливает строку, из которого будет прочитано тело POST-запроса.
		
		//Ответ от сервера получим в возвращаемом значении типа HTTPОтвет
		ОтветHTTP = НТТР.ОтправитьДляОбработки(ЗапросHTTP);
	Исключение
		ТекстСообщения = НСтр("ru = 'Неудачная попытка соединения с '")  + СерверПриемник + ": " + ОписаниеОшибки();
		Сообщить(ТекстСообщения);
		Возврат;
	КонецПопытки;
	
	ЗаголовкиОтвета = ОтветHTTP.Заголовки;
	Если ЗаголовкиОтвета.Количество() > 0 Тогда
		Для каждого КлючИЗначение Из ЗаголовкиОтвета Цикл
			// в ответе много всякого, но нас интересует только DATE
			Если КлючИЗначение.Ключ = "Date" Тогда
				Сообщить("Дата = " +РаспарситьСтрокуЗначения(КлючИЗначение.Значение, Истина));
				Сообщить("ДатаВремя = " +РаспарситьСтрокуЗначения(КлючИЗначение.Значение, Ложь));
				Прервать;
			КонецЕсли;
		КонецЦикла; //Для каждого КлючИЗначение Из ЗаголовкиОтвета
	КонецЕсли;
	
	ОтветHTTP = NULL;

Самое интересно происходит в процедуре РаспарситьСтрокуЗначения(), которая и занимается разбором ответа от сервиса и приведением его к типу Дата.

Функция ВернутьЧисловойСтрокой(пМесяц)
	
	Если Найти(НРег(пМесяц), "jan") > 0 Тогда
		Возврат "01";
	ИначеЕсли Найти(НРег(пМесяц), "feb") > 0 Тогда
		Возврат "02";
	ИначеЕсли Найти(НРег(пМесяц), "mar") > 0 Тогда
		Возврат "03";
	ИначеЕсли Найти(НРег(пМесяц), "apr") > 0 Тогда
		Возврат "04";
	ИначеЕсли Найти(НРег(пМесяц), "may") > 0 Тогда
		Возврат "05";
	ИначеЕсли Найти(НРег(пМесяц), "jun") > 0 Тогда
		Возврат "06";
	ИначеЕсли Найти(НРег(пМесяц), "jul") > 0 Тогда
		Возврат "07";
	ИначеЕсли Найти(НРег(пМесяц), "aug") > 0 Тогда
		Возврат "08";
	ИначеЕсли Найти(НРег(пМесяц), "sep") > 0 Тогда
		Возврат "09";
	ИначеЕсли Найти(НРег(пМесяц), "oct") > 0 Тогда
		Возврат "10";
	ИначеЕсли Найти(НРег(пМесяц), "nov") > 0 Тогда
		Возврат "11";
	ИначеЕсли Найти(НРег(пМесяц), "dec") > 0 Тогда
		Возврат "12";
	КонецЕсли;
	
КонецФункции

Функция РаспарситьСтрокуЗначения(пЗначение, ТолькоДата = Истина)
	// строка значения заголовка типа DATE  = "Tue, 27 Dec 2016 12:12:49 GMT"
	
	СтрокаДата = "";
	разделитель = ",";
	разделительПробел = " ";
    // местное отклонение по часовому поясу от Гринвича, в примере для простоты объявлено прямо в коде, но рекомендуется выносить в параметры или в реквизиты
	ОтклонениеПоЧасововмуПоясу = 3; 
	
	// отсекаем день недели - остается "27 Dec 2016 12:12:49 GMT"
	позицияРазделитель = Найти(пЗначение, разделитель);
	СтрокаДатаВремя = СокрЛП(Сред(пЗначение, позицияРазделитель+1));
	
	// получаем значение номера дня  - "27"
	позицияРазделительПробел = Найти(СтрокаДатаВремя, разделительПробел);
	СтрокаДень = СокрЛП(Сред(СтрокаДатаВремя, 1, позицияРазделительПробел-1));
	
	// отсекаем номер дня - остается "Dec 2016 12:12:49 GMT"
	СтрокаДатаВремя = СокрЛП(Сред(СтрокаДатаВремя, позицияРазделительПробел+1));
	
	// получаем номер месяца - "Dec", после преобразорвания - "12"
	позицияРазделительПробел = Найти(СтрокаДатаВремя, разделительПробел);
	СтрокаМесяц = СокрЛП(Сред(СтрокаДатаВремя, 1, позицияРазделительПробел-1));
	СтрокаМесяц = ВернутьЧисловойСтрокой(СтрокаМесяц); 
	
	// отсекаем номер месяца - остается "2016 12:12:49 GMT"
	СтрокаДатаВремя = СокрЛП(Сред(СтрокаДатаВремя, позицияРазделительПробел+1));
	
	// получаем номер года - "2016"
	позицияРазделительПробел = Найти(СтрокаДатаВремя, разделительПробел);
	СтрокаГод = СокрЛП(Сред(СтрокаДатаВремя, 1, позицияРазделительПробел-1));
	
	// вычислили дату, если запрашивали только ее, возвращаем
	Если ТолькоДата Тогда
		Возврат Дата(СтрокаГод+СтрокаМесяц+СтрокаДень);
	КонецЕсли;
	
	// вычисляем время
	// отсекам номер года - остается "12:12:49 GMT"
	СтрокаДатаВремя = СокрЛП(Сред(СтрокаДатаВремя, позицияРазделительПробел+1));
	
	// получаем время - "12:12:49", после преобразования - "121249"
	позицияРазделительПробел = Найти(СтрокаДатаВремя, разделительПробел);
	лСтрокаВремя = СокрЛП(Сред(СтрокаДатаВремя, 1, позицияРазделительПробел-1));
	лСтрокаВремя = СтрЗаменить(лСтрокаВремя, ":", "");
	
	// расчитываем время с учетом часового пояса
	лЧасы = Число(Сред(лСтрокаВремя, 1, 2));
	лОстальное = Сред(лСтрокаВремя, 3);
	лЧасы = лЧасы + ОтклонениеПоЧасововмуПоясу;
	СтрокаВремя = Строка(лЧасы) + лОстальное;
	СтрокаВремя = ?(СтрДлина(СтрокаВремя) = 6, СтрокаВремя, "0" + СтрокаВремя);
	
	// возвращаем полный формат даты с временем
	Возврат Дата(СтрокаГод+СтрокаМесяц+СтрокаДень+СтрокаВремя);
	
КонецФункции

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

5

См. также

Комментарии
Избранное Подписка Сортировка: Древо
1. Boneman 180 24.09.18 12:27 Сейчас в теме
Идея в принципе хорошая. Возьму на заметку, может где пригодится )
2. AlexPC 24.09.18 13:12 Сейчас в теме
Для клиент-серверного варианта может помочь ТекущаяДатаСеанса()
3. VictorRGB2 6 24.09.18 14:46 Сейчас в теме
(2) проверено, если изменена дата на сервере, вернет опять же измененную дату, а не текущую
собственно проблема и всплыла именно в режиме фоновых заданий, а клиенты менять дату на серверах категорически отказывались
вот и пришлось искать обходной путь
4. AlexPC 24.09.18 15:34 Сейчас в теме
(3)
клиенты менять дату на серверах категорически отказывались


Непонятно. Клиентам было разрешено менять дату на сервере 1с?
5. VictorRGB2 6 24.09.18 16:16 Сейчас в теме
(4) а кто им запретит то? мы не администрируем клиентские сервера, мы лишь поставщики решения по синхронизации данных и не более.
8. MikhailDr 25.09.18 07:41 Сейчас в теме
(5) тут проблема глобальнее, если у клиента есть доступ к серверу 1С, зачем вообще пользователю доступ к серверу
9. AlexPC 25.09.18 19:31 Сейчас в теме
(5)
а кто им запретит то? мы не администрируем клиентские сервера, мы лишь поставщики решения по синхронизации данных и не более.

А синхронизация проходит в рамках локальной сети или через интернет?
10. VictorRGB2 6 26.09.18 08:49 Сейчас в теме
(9) синхронизация через xml, формирование и чтение файлов в локальной сети, отправка и получение файлов через интернет
6. uno-c 27 25.09.18 01:03 Сейчас в теме
&НаКлиенте
Процедура СообщитьТекущееВремя(Команда)
	ХТТП = Новый HTTPСоединение("time.is",,,,,,Новый ЗащищенноеСоединениеOpenSSL);
	Ответ = ХТТП.Получить(Новый HTTPЗапрос("/Unix"));
	СтрокаОтвета = Ответ.ПолучитьТелоКакСтроку();
	Старт = СтрНайти(СтрокаОтвета, "<div id=""twd"">") + 14;
	ЮниксВремя = Число(Сред(СтрокаОтвета, Старт, 10));
	Местное = МестноеВремя(Дата(1970,1,1) + ЮниксВремя);
	Сообщить (Местное);
КонецПроцедуры
Показать
7. uno-c 27 25.09.18 02:13 Сейчас в теме
Можно и по Яндексу, для разнообразия используем объектную модель:
&НаКлиенте
Процедура СообщитьТекущееВремяПоЯндексу(Команда)
	ХТТП = Новый HTTPСоединение("yandex.ru",,,,,,Новый ЗащищенноеСоединениеOpenSSL);
	СтрокаОтвета = ХТТП.Получить(Новый HTTPЗапрос("/time/")).ПолучитьТелоКакСтроку();
	ЧтениеHTML = Новый ЧтениеHTML;
	ЧтениеHTML.УстановитьСтроку(СтрокаОтвета);
	ПостроительDOM = Новый ПостроительDOM;
	ДокументHTML = ПостроительDOM.Прочитать(ЧтениеHTML);
	СтрокаЖсон = ДокументHTML.Тело.Атрибуты.ПолучитьИменованныйЭлемент("data-bem").Значение;
	ЧтениеЖсон = Новый ЧтениеJSON;
	ЧтениеЖсон.УстановитьСтроку(СтрокаЖсон);
	ЖсонХДТО = ФабрикаXDTO.ПрочитатьJSON(ЧтениеЖсон);
	Сообщить (МестноеВремя(Дата(1970,1,1) + ЖсонХДТО.app.time/1000));
КонецПроцедуры
Показать
A_Max; mysm; ifal; +3 Ответить
Оставьте свое сообщение