Во многих языках (Java, JS, C/C++…) если возникает необходимость получить текущую дату, то мы будем иметь что-то типа такой структуры:
struct rtc_time {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday; /* не используется */
int tm_yday; /* не используется */
int tm_isdst; /* не используется */
};
При этом, Январю и воскресенью соответствует число 0. Я задался вопросом: «почему так?». Вопрос этот возник потому, что я знаком со многими RTC контроллерами, в том числе и со встроенными в микроконтроллеры. Смотрите что получается:
man RTC - русский man по /dev/rtc Отсюда узнаем, что:
Все ПК i386 и системы с ACPI содержат RTC, которые совместимы с микросхемой Motorola MC146818 из первоначальной модели PC/AT. Сегодня такие RTC обычно встраивают в чипсет материнской платы (в южный мост), и они используют заменяемую резервную батарею (типа «таблетки»).
Открываем даташит на MC146818, и видим таблицу 3 на странице 12, из которой узнаем, что диапазон месяцев находится в переделе от 1 до 12. То есть MC146818 знает, что Январю соответствует число 1.
Включаем компьютер. На каком-то этапе (на каком?) через BIOS считываются данные из RTC контроллера, складываются в вышеописанную структуру rtc_time
, вычисляется epoch
, устанавливается системный таймер по вычисленному epoch
. И мы имеем текущее время в системе.
Пишем программу на Си(любом языке). Используем time.h
. Как я понимаю, при использовании любой функции из библиотеки для получения текущей даты/времени, будет использован системный вызов time, который вернет epoch
. Посмотрим на функции для работы со временем. Есть функции, которые преобразуют время в строку для вывода на экран, а есть функция gmtime, которая позволяет получить структуру tm, в которой будет все, что касается времени и даты, но только месяц и день недели будут идти с 0 . Теперь, если мы хотим работать с этим форматом, то мы должны всегда помнить об этом нюансе. Да, работать с массивом удобно: нулевой элемент не пустой, и вроде как экономия памяти. Но очень многие программисты наступают на грабли, когда работают с датой в разных ЯП из-за того, что Январь - нулевой месяц.
Причем, эти грабли кладутся так: RTC -> (JAN(01)->epoch) -> Kernel -> SysCall -> epoch -> App -> JAN(00). И, не дай бог, придется где-то работать с этим Январем (месяцем) в виде числа и отдельно в виде строки (вывод). Кстати, если посмотреть как работает /dev/rtc ,то можно увидеть, что после чтения из RTC контроллера происходит декремент месяца. А при записи в контроллер происходит инкремент. Основной вопрос: зачем сделаны такие мучения над временем? Для совместимости с чем? Почему бы через /dev/rtc не выдавать дату без изменений, считанную с RTC? Ведь я, как электронщик, ожидаю получить от RTC модуля именно то, что есть в даташите.
В итоге, в JS и JAVA часто приходится вертеть этими числами прибавляя и убавляя единицу. В Си/Си++ аналогично. Я всю голову сломал, пытаясь понять зачем все это так сделано. Разъясните.