Опубликован: 15.06.2004 | Доступ: свободный | Студентов: 2290 / 579 | Оценка: 4.35 / 3.96 | Длительность: 27:47:00
ISBN: 978-5-9556-0011-6
Лекция 12:

Время и работа с ним

Преобразование данных о времени

Стандартом 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().

Для учета данных о часовом поясе и сезонных поправках используются внешние переменные 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() и ассоциированных внешних переменных.

Элементам массива 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().

При входе в функцию 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().

По сравнению с date, функция strftime() содержит несколько дополнительных спецификаторов форматных преобразований. Перечислим наиболее употребительные из них.

Таблица 12.1. Спецификаторы преобразований
Спецификатор Описание
%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().

Функция strptime() напоминает sscanf(): она сканирует цепочку символов, на которую указывает аргумент s, в соответствии с заданным форматом, включающим описанные выше спецификаторы преобразований, а также, быть может, пробельные и обычные символы, и помещает извлеченные значения в структуру типа tm по указателю tmptr. В качестве результата возвращается указатель на первый несканированный символ или NULL в случае неудачи.

Функция getdate(), по сути аналогичная strptime(), использует для разбора входной цепочки s форматы, содержащиеся в файле, чье полное маршрутное имя задано переменной окружения DATEMSK (для интерпретации выбирается первый подходящий формат). Если дата и время специфицированы не полностью (например, задан только день недели), как исходные берутся данные о первом подходящем моменте времени, начиная с текущего. Если в формате присутствует спецификатор %Z, выходная структура инициализируется текущим временем в сканируемом часовом поясе. В противном случае применяется местное время.

С помощью внешней переменной (или макроса) getdate_err функция getdate() возвращает коды ошибок.

Приведем пример использования описанных функций преобразования данных о времени (см. листинг 12.13).

#include <stdio.h>
#include <time.h>
#include <limits.h>

int main (void) {
 char dtbuf [LINE_MAX];        /* Буфер для данных о времени */
 time_t st;
 struct tm stm;

 (void) time (&st);

 (void) strftime (dtbuf, sizeof (dtbuf), "%c", gmtime (&st));
 printf ("Текущее всемирное время: %s\n", dtbuf);

 (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() возвращают указатели на статические структуры, содержимое которых, возможно, перезаписывается при каждом вызове, поэтому, если оно понадобится в дальнейшем, его следует скопировать в собственные объекты.

Антон Коновалов
Антон Коновалов

В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13
Планируется ли актуализация материалов данного очень полезного курса?

Андрей Шулин
Андрей Шулин
Россия, Усть-илимск