Как разобрать ISO8601 в time_t? - PullRequest
2 голосов
/ 10 июня 2019

Как правильно проанализировать время ISO8601 в time_t?

Входные строки представляют собой конкретный вариант даты-времени ISO8601:

1991-02-03T04: 05: 06.000-07: 00
(мне не нужно беспокоиться о «Z» или подразумеваемых вариантах местного времени)

Я могу использовать strptime() для анализа до доли секунды, но на странице руководства упоминается setlocale(), поэтому я беспокоюсь, что мне нужно что-то с этим сделать. Я? Или это только для таких вещей, как названия месяца и дня?

Кажется, нет никакого способа пропустить (или обработать) доли секунды в strptime, и моя версия этого, похоже, не поддерживает '% z' в любом случае (и tm_gmtoff не является стандартным ), так что я застрял в парсинге секунд и смещения часового пояса ввода «вручную». Достаточно просто.

Поэтому я предполагаю, что могу просто изменить tm_min Я получил от strptime количество минут смещения TZ. Правильно?

Тогда мы подходим к mktime(). Похоже, что ожидаемый способ заставить его функционировать в UTC:

получить TZ, очистить TZ, tzset(), mktime(), сбросить TZ, tzset()
(есть timegm(), но нестандартно)

Я собираюсь обработать множество этих строк, и мне не нужны никакие другие временные обработки в этой программе, так что это похоже на бесполезные накладные расходы, Могу ли я просто очистить TZ и * 1043? * один раз в начале?

Ответы [ 2 ]

2 голосов
/ 10 июня 2019

Я могу использовать strptime() для анализа до доли секунды, но на странице руководства упоминается setlocale(), поэтому я беспокоюсь, что мне нужно что-то с этим сделать.Я?

Да, к сожалению;в зависимости от локали% S может потребовать доли секунды и искать десятичную запятую, а не десятичную точку.

Лично я бы сделал это полностью вручную, с помощью strtok и strtol,заполнив соответствующие поля struct tm.Вам не нужно заполнять tm_yday и tm_wday, чтобы mktime работал.

Я предполагаю, что могу просто изменить tm_min Я получил от strptime с количеством минут TZсмещение.Правильно?

Это сложнее, чем кажется.В зависимости от фактического значения смещения часового пояса вам нужно будет отрегулировать tm_hr, а также tm_min (на самом деле, общий случай - это целое количество часов, поэтому вы будете настраивать tm_hr, а не * 1025).*), и если корректировка сдвинула время за дневную границу, вам нужно будет нормализовать час и исправить день, месяц и год.

Вы также должны убедиться, что tm_isdst и tm_gmtoff равны нулю, а tm_zone равно NULL.

Могу ли я просто очистить TZ и tzset () один раз в начале?

В системе, где "ожидаемый путь"работает, да.Однако «ожидаемый путь» не гарантированно сработает, и на самом деле, если у вас нет timegm, я бы ожидал, что не будет работать.

Используйте timegm.Если у вас нет timegm, , получите его от gnulib.

1 голос
/ 10 июня 2019

Оказывается, что написать свой минималистский 'mktime без чепухи с часовым поясом' не так уж и сложно.

#define divis(y,n) (((y) % (n)) == 0)
#define isLY(y) (divis((y),4) && (!divis((y),100) || divis((y),400)))
#define nLY(y)  (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)

static int      mdays[]         = {31,28,31,30,31,30,31,31,30,31,30,31};

/*
 * mktime implicitly applies the local timezone, this doesn't.
 * This also only works with valid values, no checking or normalizing happens.
 */
static time_t epoch (
        struct tm *     tm
) {
        int     years           = tm->tm_year + 1900;
        int     months          = tm->tm_mon;
        int     days            = tm->tm_mday - 1;
        int     hours           = tm->tm_hour;
        int     minutes         = tm->tm_min;
        int     seconds         = tm->tm_sec;

        if ((months > 1) && isLY(years)) ++days;
        while (months-- > 0) days += mdays[months];
        days += (365 * (years - 1970)) + nLY(years);
        return (time_t)((86400 * days) + (3600 * hours) + (60 * minutes) + seconds);
}

Итак, я объединю это с предложением в ответе @zwol напросто проанализируйте строку «вручную», а не разбирайтесь с возможными идиосинкразиями strptime(), mktime() и struct tm.

...