Как разложить unix-время на C

Кажется, что никто никогда не должен делать этого, но я работаю над модулем ядра для встраиваемой системы (OpenWRT), в котором кажется, что time.h действительно включает типы timespec и time_t, и функции clock_gettime и gmtime, но не включает localtime, ctime, time или, что особенно важно, тип tm.

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

Так что я думаю, что я был бы доволен решением проблемы одним из двух способов — было бы здорово выяснить, как получить доступ к этому отсутствующему типу, или, альтернативно, как свернуть мой собственный метод для декомпозиции временной метки unix.


person mikepurvis    schedule 13.08.2009    source источник
comment
Как gmtime объявляется в time.h?   -  person caf    schedule 14.08.2009


Ответы (3)


Это должно быть точным (заполняет урезанную имитацию 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*month/12 — ловкий трюк для создания последовательности 30/31 дня календаря. Расчет работает с годами, которые начинаются в марте, до исправления в конце, что упрощает задачу, потому что тогда високосный день приходится на конец «года».

person caf    schedule 14.08.2009
comment
Вместо того, чтобы извиняться за магические числа, почему бы хотя бы не прокомментировать некоторые из менее очевидных вещей в коде? Эта функция выиграла бы от некоторых комментариев. - person Matthew Nizol; 14.08.2009
comment
Я добавил несколько комментариев, которые, надеюсь, будут полезны. - person caf; 14.08.2009

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

person hlovdal    schedule 13.08.2009
comment
В итоге я создал задание cron, которое периодически записывало дату в интерфейс proc. Кажется окольным путем, но в конечном итоге это было лучшее решение, чем эта временная ерунда. Так что да, хороший призыв просто вытащить его из пользовательского пространства. - person mikepurvis; 20.08.2009

time_t - это количество секунд с 1 января 1970 года по всемирному координированному времени, поэтому разложить его на месяц, день и год не так уж сложно, если вы хотите получить результат в формате UTC. Существует куча доступных источников, найденных в Google "источник gmtime". Большинство встроенных систем не учитывают локальное время, так как это немного сложнее из-за зависимости от настройки часового пояса и среды.

person D.Shawley    schedule 14.08.2009