Как разложить время Unix в C
-
16-09-2019 - |
Вопрос
Кажется, что никому никогда не придется этого делать, но я работаю над модулем ядра для встраиваемой системы (OpenWRT), в котором кажется, что time.h
делает включить timespec
и time_t
типы и clock_gettime
и gmtime
функционирует, но делает нет включать localtime
, ctime
, time
, или, что особенно важно, tm
тип.
Когда я пытаюсь привести указатель возврата из gmtime в свою собственную структуру, я получаю ошибку segfault.
Поэтому я думаю, что был бы доволен решением проблемы любым из двух способов: было бы здорово выяснить, как получить доступ к этому отсутствующему типу, или, альтернативно, как реализовать свой собственный метод для декомпозиции временной метки Unix.
Решение
Это должно быть точно (заполняет урезанную имитацию struct tm
, мой year
использует нашу эпоху вместо эпохи 1900 года нашей эры):
struct xtm
{
unsigned int year, mon, day, hour, min, sec;
};
#define YEAR_TO_DAYS(y) ((y)*365 + (y)/4 - (y)/100 + (y)/400)
void untime(unsigned long unixtime, struct xtm *tm)
{
/* First take out the hour/minutes/seconds - this part is easy. */
tm->sec = unixtime % 60;
unixtime /= 60;
tm->min = unixtime % 60;
unixtime /= 60;
tm->hour = unixtime % 24;
unixtime /= 24;
/* unixtime is now days since 01/01/1970 UTC
* Rebaseline to the Common Era */
unixtime += 719499;
/* Roll forward looking for the year. This could be done more efficiently
* but this will do. We have to start at 1969 because the year we calculate here
* runs from March - so January and February 1970 will come out as 1969 here.
*/
for (tm->year = 1969; unixtime > YEAR_TO_DAYS(tm->year + 1) + 30; tm->year++)
;
/* OK we have our "year", so subtract off the days accounted for by full years. */
unixtime -= YEAR_TO_DAYS(tm->year);
/* unixtime is now number of days we are into the year (remembering that March 1
* is the first day of the "year" still). */
/* Roll forward looking for the month. 1 = March through to 12 = February. */
for (tm->mon = 1; tm->mon < 12 && unixtime > 367*(tm->mon+1)/12; tm->mon++)
;
/* Subtract off the days accounted for by full months */
unixtime -= 367*tm->mon/12;
/* unixtime is now number of days we are into the month */
/* Adjust the month/year so that 1 = January, and years start where we
* usually expect them to. */
tm->mon += 2;
if (tm->mon > 12)
{
tm->mon -= 12;
tm->year++;
}
tm->day = unixtime;
}
Мои извинения за все эти магические числа.367*месяц/12 — это изящный трюк для создания последовательности дней календаря 30/31.Расчет работает с годами, которые начинаются в марте, до исправления в конце, что упрощает задачу, потому что тогда високосный день выпадает на конец «года».
Другие советы
В пользовательском пространстве glibc проделает большую работу по обработке «локальной» части представления времени.В ядре это недоступно.Вероятно, вам не следует заморачиваться с этим внутри вашего модуля, если необходимо, сделайте это в пользовательском пространстве.
А time_t
— это количество секунд с 1 января 1970 года по всемирному координированному времени, поэтому разложить его на месяц, день и год не так уж сложно, если вам нужен результат в формате UTC.Eсть доступна куча исходников по Гуглу "источник gmtime".Большинство встроенных систем не учитывают обработку местного времени, поскольку это немного сложнее из-за зависимости от настроек часового пояса и окружающей среды.