Недокументированная и нестандартная служба времени AVR LibC
- Войдите на сайт для отправки комментариев
Потребовалось мне тут узнавать из Интернета текущее время и дату. Ну, получить длинное число из NTP нетрудно, но потом же надо в дату и время пересчитать! Запустил IDE 1.6.5 и с тоской обнаружил, что там нет службы времени («time.h»). Полез на сайт Atmel в документацию библиотеки AVR LibC и с ещё большей тоской обнаружил, что её нет и там. Те, кто когда-либо работал с NTP, поймут мою тоску – время там узнать несложно, а вот дату без библиотечных функций – гороху накушаешься. Требуется очень аккуратная программа строк на сто – в общем, полдня возни.
Но, чёрт меня дёрнул запустить IDE 1.6.12 (типа, для очистки совести) – а оно там есть!
Таким образом, в поздних версиях IDE (вернее, библиотеки AVR LibC) имеется недокументированная служба времени. С какой версии IDE она появилась, я выяснять не стал, но точно могу сказать, что в 1.6.5 ещё не было, а в 1.6.12 уже есть.
Попробовал применить, а вот фигвам! Считая, что в типе time_t как и положено лежит UNIX-время, т.е. секунды с 01.01.1970, я ввёл поправку на NTP время (секунды с 01.01.1900) и … получил, что сегодня 2047 год.
Полез в исходники AVR LibC – документации-то нету :( Нарыл следующую информацию – в типе time_t они хранят не UNIX-время как все нормальные люди, а секунды с 01.01.2000. Отсюда и моя ошибка в 30 лет. Правда, они определили константы для пересчёта их времени в UNIX и в NTP – и на том спасибо.
На всякий случай проверил, что хранится в поле tm_year структуры struct tm. Здесь у них всё стандартно – год с 1900, как у всех.
Вот скетч – пример использования службы времени. Работа с NTP сюда не включена, а просто вставил константу 3693471153, которую я получил сегодня от NTP сервера. Она соответствует времени 15.01.2017 15:12:33. Смотрите в скетче, как из этого числа можно получить структуру struct tm (а в ней уже есть все – время, дата, день недели и т.п.).
// IDE 1.6.12 #include <time.h> #define GOT_FROM_NTP 3693471153UL // 15.01.2017 15:12:33 #define MOSCOW_TIME (3 * ONE_HOUR) void setup(void) { Serial.begin(115200); unsigned long tt = GOT_FROM_NTP - NTP_OFFSET; set_zone(MOSCOW_TIME); const struct tm * timeinfo = localtime(&tt); Serial.print("struct tm year: "); Serial.println(timeinfo->tm_year); char szTime[48]; strftime(szTime, sizeof(szTime), "%A, %B %d, %Y. %T", timeinfo); Serial.println(szTime); } void loop(void) {} /////////////////////////////////////// //// RESULT (IDE 1.6.12) ////////////// // // struct tm year: 117 // Sunday, January 15, 2017. 15:12:33 //
В порядке справки, приведу также и структуру struct tm, для тех, кто её не знает.
struct tm { int8_t tm_sec; // секунды [ 0 - 59 ] int8_t tm_min; // минуты [ 0 - 59 ] int8_t tm_hour; // часы [ 0 - 23 ] int8_t tm_mday; // день месяца [ 1 - 31 ] int8_t tm_wday; // день недели [ 0-воскресенье - 6-суббота ] int8_t tm_mon; // месяц [ 0-январь - 11-декабрь ] int16_t tm_year; // год с 1900 int16_t tm_yday; // день года [ 0 - 365 ] int16_t tm_isdst; // флаг перехода на сезонное время };
Виноват, в строке 9 более грамотно использовать тип time_t, а не unsigned long, хотя в данной реализации это одно и то же. У меня этот тип случайно остался от NTP-шной программы - забыл поменять.
А в остальном нормальная библиотека? Что там есть, чего нет? Локализации-то поди точно нет.
А в остальном нормальная библиотека? Что там есть, чего нет? Локализации-то поди точно нет.
Вот полный заголовочный файл time.h из avr libc 1.8.1
Если что, то на Pjrc.com есть рабочая библиотека "time" с прмерами..
Ну, Dimax, здесь всё-таки полная С-шная стандартная библиотека к которой многие уж десятилетия как привыкли.
Вот, недавно была тема где автор собирался массив времени восхода на весь год иметь, а тут - ввёл свои координаты и получай хоть восход, хоть закат ...
Оно как-то когда это устоявшийся стандарт как-то приятниее.
кто как понимает этот кусок комментариев в time.h ? (*ТС, можешь не выскакивать с воплями о платности своих услуг)
так понимаю, что нужно каждую секунду запускать system_tick(); и reti();
ок.
запускается system_tick(); - и, чего?
и, не запускается reti(); - нет о нём ничего в коде.
*по идее, должно быть как-то так
*по идее, должно быть как-то так
Насчет reti() не знаю, а вот system_tick(), как я понял, наращивает volatile time_t __system_time. Для чего? Возможно для портированного кода, который захочет получить текущее время через стандартную time(), которая и возвращает эту глобальную переменную.
а вот system_tick(), как я понял, наращивает volatile time_t __system_time.
а, как ты это понял? - где находится код, который что-то наращивает?
и, где смотреть __system_time ?
avr-libc-2.0.0/libc/time/
time.c
system_tick.S:
ок. спасибо.
Я своей функцией пользуюсь для пересчёта из time_t, она входит в состав логгера для telnet сервера (GetTimeAsSystemTime).
Я своей функцией пользуюсь для пересчёта из time_t, она входит в состав логгера для telnet сервера (GetTimeAsSystemTime).
Евгений, а как проверить насколько правильно сия библиотека рассчитывает локальное время? Могу к примеру сказать для любого города точное локальное время. Сравним?
Очень точно рассчитывает. Только UTC offset задайте правильно ;)
Есть специальный онлайн сервис, где можно конвертировать время: Epoch & Unix Timestamp Conversion Tools
На самом деле нет никаких "нестандартных и недокументированных служб". Есть давно стандартизированный набор unix'овых функций для преобразования данных о времени в разные формы (в т.ч. строковый). Поскольку математика работы со временем одна, то нестандартной она быть не может. В моём случае я описал по-русски почти каждое действие. Существует несколько программных реализаций подобных алгоритмов.
Также нужно иметь в виду, что начало отсчёта называется эпохой. Дату 1970 называют эпохой unix. Вы можете выбрать любую удобную для вас дату, имея в виду, что интервал времени в 32-битном числе получается не такой уж и большой. Для эпохи unix в 2030-х годах наступит переполнение для устройств, которые используют 32-битные счётчики. Если же эту дату отодвинуть чуть на попозже, то и время аппокалипсиса тоже отодвинется. Поэтому некоторые товарищи берут за начала отсчёта другие даты. Об этом можно почитать в википедии.
Очень точно рассчитывает. Только UTC offset задайте правильно ;)
А код не сможете привести, ну я ни разу не программист, сам точно не напишу
В качестве примечания: эти некоторые товарищи точкой отсчета в avr-libc версии <time.h> взяли 00:00:00 01/01/2000, базируясь тем самым на "Y2K epoch", что может доставить определенные проблемы при взаимодействии со внешними системами. Нужно своевременно корректировать timestamp на UNIX_OFFSET. Видимо это и есть нестандартность данной реализации, как и указанно в первом посте.
нашлась reti() в interrupt.h
А код не сможете привести, ну я ни разу не программист, сам точно не напишу
Вот он, в первом посте:
5
#define MOSCOW_TIME (3 * ONE_HOUR)
06
10
set_zone(MOSCOW_TIME);
11
const
struct
tm * timeinfo = localtime(&tt);
Учтите, что действие глобальной переменной __utc_offset, устанавливаемой при помощи set_zone() распространяется только на функции localtime_r(), localtime(), mktime() и частично на strftime(). В отношении последней есть примечание: "All conversions are made using the 'C Locale', ignoring the E or O modifiers. Due to the lack of a time zone 'name', the 'Z' conversion is also ignored."
нашлась reti() в interrupt.h
....гы!
-Рубашка нашлась, Петька! Она под майкой была! ©
Учтите, что действие глобальной переменной __utc_offset, устанавливаемой при помощи set_zone() распространяется только на функции localtime_r(), localtime(), mktime() и частично на strftime(). В отношении последней есть примечание: "All conversions are made using the 'C Locale', ignoring the E or O modifiers. Due to the lack of a time zone 'name', the 'Z' conversion is also ignored."
А координаты где вводить?
Координаты мерьканских секретных объектов?
http://www.nongnu.org/avr-libc/user-manual/group__avr__time.html - set_position().
Только на таймзону они никак не влияют, если что.
А координаты где вводить?
Координаты не влияют на локальное время. Они нужны для определения времени восхода, заката и т.п.
ТС, можешь не выскакивать с воплями о платности своих услуг
Могу и не выскакивать. Молодец, что запомнил!
насколько правильно сия библиотека рассчитывает локальное время?
Ни на сколько вообще. Вы сами задаёте UTC и сами же задаёте часовой пояс - её дело тупо сложить.
Могу и не выскакивать. Молодец, что запомнил!
таки, не выдержал - выскочил.
кто в курсе зачем в разных примерах по разному объявляется структура tm ?
Координаты мерьканских секретных объектов?
http://www.nongnu.org/avr-libc/user-manual/group__avr__time.html - set_position().
Только на таймзону они никак не влияют, если что.
Таки и не должны, они влияют на рассчет слежения трекера за спутниками к примеру, но кеплеровские данные всё равно придётся обновлять, хотя бы раз в месяц
Пример определения времени восхода солнца для двух разных городов.
Результат работы
Замечание: здесь не учитывается высота данного места над уровнем моря, потому время может на несколько минут отличаться от фактического.
Во, блин! Совсем уж собрался писать про ещё одну нестандартность, но всё не так просто.
Была у меня в древнем, как фортран, проекте под Visual Studio (VS) функция для вычисления дня недели
и ведь, ЧСХ, знаю, что говнокод, всегда говорил и студентам, и детям, и внукам, что так делать не стоит, но вот такая она у меня - эта функция. :-( Служила она мне верой и правдой во многих программах.
Решил перетащить в атмеловскую студию (AS) (всё, что сказано ниже, верно и для Arduino IDE). Что-то пошло не так. Ну, эта функция много лет верой и правдой, потому, её подозреваем в самую последнюю очередь. Час траха, чтобы выяснить, что таки она, сука, неправильно считает. Как же так?!? Ещё немного траха и тут выясняется, что структуры tm в VS и в AS - разные!
Т.е. у майкрософта день недели стоит после месяца и года, а у Атмела - перед - твающ...!
Ну, чё, решил разобраться, кто не прав: Atmel или Microsoft.
Открываем стандарт языка Си (ISO/IEC 9899:2018, § 7.27.1 Library, page. 285) и читаем
Вывод: и Майкрософт и Атмел, оба правы. Неправ ЕвгенийП, в том, что использует неназначенную инициализацию структуры!
В порядке придирки отмечу, что у Атмела неверно указан диапазон секунд - 0-59, а должно быть 0-60.
Но самый шок был. когда я полез в казалось бы абсолютно кошерный источник "правильного Си" - https://github.com/torvalds/linux/blob/master/include/linux/time.h и обнаружил, что там-то эта структура стандарту не соответствует - отсутствует поле tm_isdst. Вот так, ни хрена себе!
Надо Торвальду подсказать )))
В порядке придирки отмечу, что у Атмела неверно указан диапазон секунд - 0-59, а должно быть 0-60.
Это очень прикольно. Интересно , почему секунд может быть 60, а минут не может быть ?
Ну, дык, бывает "високосная секунда", а "високосной минуты" не бывает - обидели!
Вот оно че...