Преобразование данных о времени
Стандартом POSIX-2001 предусмотрено несколько способов представления данных о времени. Выше были описаны структура tm и тип time_t. Кроме того, время может записываться в виде цепочки символов. (Есть еще структуры timeval и timespec, но они в данном контексте играют подчиненную роль, лишь уточняя значения типа time_t.)
Для выполнения преобразований между разными представлениями данных о времени служат описываемые далее функции (см. также рис. 12.1).
Рис. 12.1. Функции для выполнения преобразований между разными представлениями данных о времени.
Функции gmtime() и localtime() (см. листинг 12.8) преобразуют значения типа time_t в структуру типа tm. Соотношение между временем в секундах от начала отсчета и значениями полей структуры типа tm дается в приведенной выше формуле. Кроме того, функция localtime() учитывает данные о часовом поясе и сезонных поправках.
#include <time.h> struct tm *gmtime (const time_t *tloc); struct tm *localtime (const time_t *tloc)
Листинг 12.8. Описание функций gmtime() и localtime(). (html, txt)
Для учета данных о часовом поясе и сезонных поправках используются внешние переменные tzname, timezone, daylight, значения которых функция tzset() устанавливает по переменной окружения TZ (см. листинг 12.9).
#include <time.h> extern char *tzname[2]; extern long timezone; extern int daylight; void tzset (void);
Листинг 12.9. Описание функции tzset() и ассоциированных внешних переменных. (html, txt)
Элементам массива tzname присваиваются имена местного часового пояса в стандартном (tzname [0]) и "летнем" (tzname [1]) вариантах. Значение переменной timezone устанавливается равным разности в секундах между всемирным и местным поясным временем. Переменной daylight присваивается отличное от нуля значение, если для местного часового пояса предусмотрен переход на летнее время.
Отметим, что в общем случае значение переменой окружения TZ устроено довольно сложным образом:
Станд_поясСмещение[Лет_пояс[Смещение] [,Нач_лет[/Время],Кон_лет[/Время]]]
Здесь Станд_пояс и Лет_пояс - имена, присваиваемые элементам массива tzname [], Смещение - разность между всемирным и местным поясным временем (в виде чч[:мм[:сс]]), Нач_лет и Кон_лет, соответственно, даты начала и окончания действия летнего времени (обычно их задают в виде Mмм.н.д - месяц, неделя, день), время - время перехода (по умолчанию - два часа ночи). Можно видеть, что данных для вычисления местного времени оказывается вполне достаточно.
Функцию mktime() (см. листинг 12.10) можно считать обратной по отношению к localtime(). Она преобразует местное время, заданное в виде структуры типа tm, в значение типа time_t, т. е. в секунды от начала отсчета по всемирному времени.
#include <time.h> time_t mktime (struct tm *tmptr);
Листинг 12.10. Описание функции mktime(). (html, txt)
При входе в функцию mktime() значения полей tm_wday и tm_yday структуры tm, на которую указывает аргумент tmptr, игнорируются; при выходе они устанавливаются должным образом. Значения других полей также приводятся к стандартным для них диапазонам (при входе это условие может не выполняться).
Другим весьма мощным средством преобразования местного времени из структурного в текстовое представление является функция strftime() (см. листинг 12.11). Как и служебная программа date, она преобразует дату и время в соответствии с заданным форматом, только исходными данными служит не текущий момент времени, а структура типа tm, на которую указывает аргумент tmptr, и результат направляется не на стандартный вывод, а в буфер, заданный указателем s и длиной maxsize.
#include <time.h> size_t strftime (char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict tmptr);
Листинг 12.11. Описание функции strftime(). (html, txt)
По сравнению с date, функция strftime() содержит несколько дополнительных спецификаторов форматных преобразований. Перечислим наиболее употребительные из них.
%F | Представление даты в соответствии со стандартом ISO 8601:2000: эквивалент последовательности спецификаторов %Y-%m-%d. |
%R | Часы и минуты (%H:%M) в 24-часовом представлении. |
%z | Смещение относительно всемирного времени, представленное по стандарту ISO 8601:2000: +ччмм или -ччмм (положительные значения соответствуют часовым поясам к востоку от Гринвича). |
Функция strftime() возвращает число байт, помещенное в буфер (без учета завершающего нулевого байта). Если буфер оказался мал, возвращается ноль.
На роль обратной по отношению к strftime() могут претендовать сразу две функции: strptime() и getdate() (см. листинг 12.12).
#include <time.h> char *strptime (const char *restrict s, const char *restrict format, struct tm *restrict tmptr); struct tm *getdate (const char *s);
Листинг 12.12. Описание функций strptime() и getdate(). (html, txt)
Функция strptime() напоминает sscanf(): она сканирует цепочку символов, на которую указывает аргумент s, в соответствии с заданным форматом, включающим описанные выше спецификаторы преобразований, а также, быть может, пробельные и обычные символы, и помещает извлеченные значения в структуру типа tm по указателю tmptr. В качестве результата возвращается указатель на первый несканированный символ или NULL в случае неудачи.
Функция getdate(), по сути аналогичная strptime(), использует для разбора входной цепочки s форматы, содержащиеся в файле, чье полное маршрутное имя задано переменной окружения DATEMSK (для интерпретации выбирается первый подходящий формат). Если дата и время специфицированы не полностью (например, задан только день недели), как исходные берутся данные о первом подходящем моменте времени, начиная с текущего. Если в формате присутствует спецификатор %Z, выходная структура инициализируется текущим временем в сканируемом часовом поясе. В противном случае применяется местное время.
С помощью внешней переменной (или макроса) getdate_err функция getdate() возвращает коды ошибок.
Приведем пример использования описанных функций преобразования данных о времени (см. листинг 12.13).
Листинг 12.13. Пример программы, использующей функции преобразования данных о времени. (html, txt)
Возможные результаты выполнения этой программы показаны в листинге 12.14.
Листинг 12.14. Возможные результаты работы программы, использующей функции преобразования данных о времени. (html, txt)
Последовательность инструкций языка C
char dtbuf []; time_t st; (void) strftime (dtbuf, sizeof (dtbuf), "%c", localtime (&st));
является стандартной для получения текущего времени в текстовом виде. (Разумеется, вместо "%c" допустим другой спецификатор преобразования.) Нужно помнить только, что функции gmtime() и localtime() возвращают указатели на статические структуры, содержимое которых, возможно, перезаписывается при каждом вызове, поэтому, если оно понадобится в дальнейшем, его следует скопировать в собственные объекты.
(void) strftime (dtbuf, sizeof (dtbuf), "%c", localtime (&st)); printf ("Текущее местное время: %s\n", dtbuf);
/* Узнаем, каким днем недели будет 01 января 2038 года */ stm.tm_year = 2038 - 1900; stm.tm_mon = 1 - 1; stm.tm_mday = 1; stm.tm_hour = 0; stm.tm_min = 0; stm.tm_sec = 0; stm.tm_isdst = -1; if ((st = mktime (&stm)) == (time_t) (-1)) { perror ("MKTIME"); } else { (void) strftime (dtbuf, sizeof (dtbuf), "%A", &stm); printf ("День недели 01 января 2038 года: %s\n", dtbuf); printf ("Число секунд от начала отсчета в начале 2038 года (шест.): %x\n", (unsigned int) st); }
/* Узнаем, когда наступит переполнение значений типа time_t, */ /* представленных как 32-разрядное целое со знаком */ st = (time_t) 0x7fffffff; (void) strftime (dtbuf, sizeof (dtbuf), "%c", gmtime (&st)); printf ("Всемирное время конца 32-разрядного отсчета: %s\n", dtbuf);
/* Преобразуем эту дату в формат ISO 8601:2000 */ if (strptime (dtbuf, "%c", &stm) == NULL) { perror ("STRPTIME"); } else { (void) strftime (dtbuf, sizeof (dtbuf), "%F", &stm); printf ("Дата конца 32-разрядного отсчета в формате ISO 8601:2000: %s\n", dtbuf); }
return (0); }
Листинг 12.13. Пример программы, использующей функции преобразования данных о времени.
Возможные результаты выполнения этой программы показаны в листинге 12.14.
Текущее всемирное время: Sat Jan 3 13:54:02 2004 Текущее местное время: Sat Jan 3 16:54:02 2004 День недели 01 января 2038 года: Friday Число секунд от начала отсчета в начале 2038 года (шест.): 7fe7ed50 Всемирное время конца 32-разрядного отсчета: Tue Jan 19 03:14:07 2038 Дата конца 32-разрядного отсчета в формате ISO 8601:2000: 2038-01-19
Листинг 12.14. Возможные результаты работы программы, использующей функции преобразования данных о времени.
Последовательность инструкций языка C
char dtbuf []; time_t st; (void) strftime (dtbuf, sizeof (dtbuf), "%c", localtime (&st));
является стандартной для получения текущего времени в текстовом виде. (Разумеется, вместо "%c" допустим другой спецификатор преобразования.) Нужно помнить только, что функции gmtime() и localtime() возвращают указатели на статические структуры, содержимое которых, возможно, перезаписывается при каждом вызове, поэтому, если оно понадобится в дальнейшем, его следует скопировать в собственные объекты.