Книга: Основы программирования в Linux
Время и дата
Разделы на этой странице:
Время и дата
Программе часто полезно иметь возможность определить время и дату. Возможно, она хочет зарегистрировать длительность собственного выполнения или ей нужно изменять свое поведение в определенные моменты времени. Например, игра может отказываться запускаться в рабочие часы или программа резервного копирования по расписанию хочет дождаться ранних часов, прежде чем начать резервное копирование в автоматическом режиме.
Примечание
Во всех системах UNIX применяется одна и та же точка отсчета времени и дат: полночь по Гринвичу (GMT) на 1 января 1970 г. Это "начало эпохи UNIX", и ОС Linux — не исключение. Время в системе Linux измеряется в секундах, начиная с этого момента времени. Такой способ обработки аналогичен принятому в системе MS-DOS за исключением того, что эпоха MS-DOS началась в 1980 г. В других системах применяют точки отсчета иных эпох.
Время задается с помощью типа time_t
. Это целочисленный тип, достаточный для хранения дат и времени в секундах. В Linux-подобных системах это тип long integer
(длинное целое), определенный вместе с функциями, предназначенными для обработки значений времени, в заголовочном файле time.h.
Примечание
Не думайте, что для хранения времени достаточно 32 битов. В системах UNIX и Linux, использующих 32-разрядный тип time_t
, временное значение "будет превышено" в 2038 г. Мы надеемся, что к тому времени системы перейдут на тип time_t
, содержащий более 32 битов. Недавнее широкое внедрение 64-разрядных процессоров превращает это практически в неизбежность.
#include <time.h>
time_t time(time_t *tloc);
Вы можете найти низкоуровневое значение времени, вызвав функцию time
, которая вернет количество секунд с начала эпохи (упражнение 4.6). Она также запишет возвращаемое значение по адресу памяти, на который указывает параметр tloc
, если он — непустой указатель.
Упражнение 4. Функция time
Далее для демонстрации функции time приведена простая программа envtime.c.
#include <time.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
int i;
time_t the_time;
for (i = 1; i <= 10; i++) {
the_time = time((time_t *)0);
printf("The time is %ldn", the_time);
sleep(2);
}
exit(0);
}
Когда вы запустите программу, она будет выводить низкоуровневое значение времени каждые 2 секунды в течение 20 секунд.
$ ./anytime
The time is 1179643852
The time is 1179643854
The time is 1179643856
The time is 1179643858
The time is 1179643860
The time is 1179643862
The time is 1179643864
The time is 1179643866
The time is 1179643868
The time is 1179643870
Как это работает
Программа вызывает функцию time
с пустым указателем в качестве аргумента, которая возвращает время и дату как количество секунд. Программа засыпает на две секунды и повторяет вызов time в целом 10 раз.
Использование времени и даты в виде количества секунд, прошедших с начала 1970 г., может быть полезно для измерения длительности чего-либо. Вы сможете сосчитать простую разность значений, полученных из двух вызовов функции time
. Однако комитет, разрабатывавший стандарт языка ISO/ANSI С, в своих решениях не указал, что тип time_t
будет применяться для определения произвольных интервалов времени в секундах, поэтому была придумана функция difftime
, которая вычисляет разность в секундах между двумя значениями типа time_t
и возвращает ее как величину типа double
:
#include <time.h>
double difftime(time_t time1, time_t time2);
Функция difftime
вычисляет разницу между двумя временными значениями и возвращает величину, эквивалентную выражениювремя1–время2
, как число с плавающей точкой. В ОС Linux значение, возвращаемое функцией time
, — это количество секунд, которое может обрабатываться, но для максимальной переносимости следует применять функцию difftime
.
Для представления времени и даты в более осмысленном (с человеческой точки зрения) виде мы должны преобразовать значение времени в понятные время и дату. Для этого существуют стандартные функции.
Функция gmtime
подразделяет низкоуровневое значение времени на структуру, содержащую более привычные поля:
#include <time.h>
struct tm *gmtime(const time_t timeval)
В структуре tm
, как минимум, определены элементы, перечисленные в табл. 4.2.
Таблица 4.2
Элемент tm |
Описание |
---|---|
int tm_sec |
Секунды, 0–61 |
int tm_min |
Минуты, 0–59 |
int tm_hour |
Часы, 0–23 |
int tm_mday |
День в месяце, 1–31 |
int tm_mon |
Месяц в году, 0–11 (January (январь) соответствует 0) |
int tm_year |
Годы, начиная с 1900 г. |
int tm_wday |
День недели, 0–6 (Sunday (воскресенье) соответствует 0) |
int tm_yday |
День в году, 0–365 |
int tm_isdst |
Действующее летнее время |
Диапазон элемента tm_sec
допускает появление время от времени корректировочной секунды или удвоенной корректировочной секунды.
Выполните упражнение 4.7.
Упражнение 4.7. Функция gmtime
Далее приведена программа gmtime.с, выводящая текущие время и дату с помощью структуры tm
и функции gmtime
.
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
struct tm *tm_ptr;
time_t the_time;
(void)time(&the_time);
tm_ptr = gmtime(&the_time);
printf("Raw time is %ldn", the_time);
printf("gmtime gives:n");
printf("date: %02d/%02d/%02dn",
tm_ptr->tm_year, tm_ptr->tm_mon+1, tm_ptr->tm_mday);
printf("time: %02d:%02d:%02dn",
tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);
exit(0);
}
Выполнив эту программу, вы получите хорошее соответствие текущим времени и дате:
$ ./gmtime; date
Raw time is 1179644196
gmtime gives:
date: 107/05/20
time: 06:56:36
Sun May 20 07:56:37 BST 2007
Как это работает
Программа вызывает функцию time
для получения машинного представления значения времени и затем вызывает функцию gmtime для преобразования его в структуру с удобными для восприятия значениями времени и даты. Она выводит на экран полученные значения с помощью функции printf
. Строго говоря, выводить необработанное значение времени таким способом не следует, потому что наличие типа длинного целого не гарантировано во всех системах. Если сразу же после вызова функции gmtime выполнить команду date, можно сравнить оба вывода.
Но здесь у вас возникнет небольшая проблема. Если вы запустите эту программу в часовом поясе, отличном от Greenwich Mean Time (время по Гринвичу) или у вас действует летнее время, как у нас, вы заметите, что время (и, возможно, дата) неправильное. Все дело в том, что функция gmtime
возвращает время по Гринвичу (теперь называемое Universal Coordinated Time (всеобщее скоординированное время) или UTC). Системы Linux и UNIX поступают так для синхронизации всех программ и систем в мире. Файлы, созданные в один и тот же момент в разных часовых поясах, будут отображаться с одинаковым временем создания. Для того чтобы посмотреть местное время, следует применять функцию localtime
.
#include <time.h>
struct tm *localtime(const time_t *timeval);
Функция localtime
идентична функции gmtime
за исключением того, что она возвращает структуру, содержащую значения с поправками на местный часовой пояс и действующее летнее время. Если вы выполните программу gmtime
, но замените все вызовы функции gmtime
на вызовы localtime
, в отчете программы вы увидите правильные время и дату.
Для преобразования разделенной на элементы структуры tm в общее внутреннее значение времени можно применить функцию mktime
:
#include <time.h>
time_t mktime(struct tm *timeptr);
Функция mktime
вернет -1, если структура не может быть представлена как значение типа time_t
.
Для вывода программой date
"дружественных" (в противоположность машинному) времени и даты можно воспользоваться функциями asctime
и ctime
:
#include <time.h>
char *asctime(const struct tm *timeptr);
char *ctime(const time_t *timeval);
Функция asctime
возвращает строку, представляющую время и дату, заданные tm
-структурой timeptr
. У возвращаемой строки формат, подобный приведенному далее:
Sun Jun 9 12:34:56 2007n
У нее всегда фиксированный формат длиной 26 символов. Функция ctime
эквивалентна следующему вызову:
asctime(localtime(timeval))
Она принимает необработанное машинное значение времени и преобразует его в местное время.
А теперь выполните упражнение 4.8.
Упражнение 4.8. Функция ctime
В этом примере благодаря приведенному далее программному коду вы увидите функцию ctime
в действии.
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
time_t timeval;
(void)time(&timeval);
printf ("The date is: %s", ctime(&timeval));
exit(0);
}
Откомпилируйте и затем запустите на выполнение ctime.c, и вы увидите нечто похожее на приведенные далее строки:
$ ./ctime
The date is: Sat Jun 9 08:02:08 2007.
Как это работает
Программа ctime.c вызывает функцию time
для получения машинного значения времени и дает возможность функции ctime
выполнить всю тяжелую работу по преобразованию этого значения в удобочитаемую строку, которую потом и выводит на экран.
Для лучшего управления точным форматированием времени и даты ОС Linux и современные UNIX-подобные системы предоставляют функцию strftime
. Она довольно похожа на функцию sprintf
для дат и времени и действует аналогичным образом:
#include <time.h>
size_t strftime(char *s, size_t maxsize, const char *format, struct tm *timeptr);
Функция strftime
форматирует время и дату, представленные в структуре tm
, на которую указывает параметр, timeptr
, и помещает результат в строку s. Эта строка задается длиной maxsize
(как минимум) символов. Строка format
применяется для управления символами, записываемыми в строку. Как и в функции printf
, она содержит обычные символы, которые будут переданы в строку, и спецификаторы преобразований для форматирования элементов времени и даты. В табл. 4.3 перечислены используемые спецификаторы преобразований.
Таблица 4.3
Спецификатор преобразования | Описание |
---|---|
%a |
Сокращенное название дня недели |
%А |
Полное название дня недели |
%b |
Сокращенное название месяца |
%B |
Полное название месяца |
%c |
Дата и время |
%d |
День месяца, 01–31 |
%H |
Час, 00–23 |
%I |
Час по 12-часовой шкале, 01–12 |
%j |
День в году, 001–366 |
%m |
Номер месяца в году, 01–12 |
%M |
Минуты, 00–59 |
%p |
a.m. (до полудня) или p.m. (после полудня) |
%S |
Секунды, 00–59 |
%u |
Номер дня недели, 1–7 (1 соответствует понедельнику) |
%U |
Номер недели в году, 01–53 (воскресенье — первый день недели) |
%V |
Номер недели в году, 01–53 (понедельник — первый день недели) |
%w |
Номер дня недели, 0–6 (0 соответствует воскресенью) |
%x |
Дата в региональном формате |
%X |
Время в региональном формате |
%y |
Номер года, меньший 1900 |
%Y |
Год |
%Z |
Название часового пояса |
%% |
Символ % |
Таким образом, обычная дата, такая же, как полученная из программы date, соответствует следующей строке формата функции strftime
:
"%a %b %d %Н: %М: %S %Y"
Для облегчения чтения дат можно использовать функцию strptime
, принимающую строку с датой и временем и формирующую структуру tm
с теми же датой и временем:
#include <time.h>
char *strptime(const char *buf, const char *format, struct tm *timeptr);
Строка format
конструируется точно так же, как одноименная строка функции strftime
. Функций strptime
действует аналогично функции sscanf
: она сканирует строку в поиске опознаваемых полей и записывает их в переменные. В данном случае это элементы структуры tm
, которая заполняется в соответствии со строкой format
. Однако спецификаторы преобразований для strptime
немного мягче спецификаторов функции strftime
. Так, в функции strptime
разрешены как сокращенные, так и полные названия дней и месяцев. Любое из этих представлений будет соответствовать спецификатору %a функции strptime
. Кроме того, в то время как функция strftime
для представления чисел, меньших 10, всегда применяет ведущие нули, strptime
считает их необязательными.
Функция strptime
возвращает указатель на символ, следующий за последним, обработанным в процессе преобразования. Если она встречает символы, которые не могут быть преобразованы, в этой точке преобразование просто прекращается. Для того чтобы убедиться в том, что в структуру tm
записаны значимые данные, вызывающей программе следует проверять, достаточно ли символов строки принято и обработано.
Рассмотрим работу функций на примере (упражнение 4.9).
Упражнение 4.9. Функции strftime
и strptime
Обратите внимание на выбор спецификаторов преобразований, использованных в следующей программе:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
struct tm *tm_ptr, timestruct;
time_t the_time;
char buf[256];
char *result;
(void)time(&the_time);
tm_ptr = localtime(&the_time);
strftime(buf, 256, "%A %d %B, %I:%S %p", tm_ptr);
printf("strftime gives: %sn", buf);
strcpy(buf, "Thu 26 July 2007, 17:53 will do fine");
printf("calling strptime with: %sn", buf);
tm_ptr = ×truct;
result = strptime(buf, "%a %d %b %Y, %R", tm_ptr);
printf("strptime consumed up to: %sn", result);
printf("strptime gives:n");
printf ("date: %02d/%02d/%02dn",
tm_ptr->tm_year % 100, tm_ptr->tm_mon+1, tm_ptr->tm_mday);
printf("time: %02d:%02dn",
tm_ptr->tm_hour, tm->ptr->tm_min);
exit(0);
}
Когда вы откомпилируете и выполните программу strftime.c, то получите следующий результат:
$ ./strftime
strftime gives: Saturday 09 June, 08:16 AM
calling strptime with: Thu 26 July 2007, 17:53 will do fine
strptime concurred up to: will do fine
strptime gives:
date: 07/07/26
time: 17:53
Как это работает
Программа strftime получает текущее местное время с помощью вызовов функций time
и localtime
. Затем она преобразует его в удобочитаемую форму с помощью функции strftime
с подходящим аргументом форматирования. Для демонстрации применения функции strptime
программа задает строку, содержащую дату и время, затем вызывает strptime для извлечения необработанных значений времени и даты и выводит их на экран. Спецификатор преобразования %R
функции strptime
— это сокращенное обозначение комбинации %Н:%M
.
Важно отметить, что для успешного просмотра даты функции strptime необходима точная строка формата. Обычно она не может точно обработать даты, считываемые из строк, введенных пользователями, до тех пор, пока не будет строго выверен формат.
Возможно, при компиляции программы strftime.c вы получите предупреждение компилятора. Причина в том, что по умолчанию в библиотеке GNU не объявлена функция strptime
. Для устранения проблемы следует явно запросить средства стандарта X/Open, добавив следующую строку перед заголовочным файлом time.h:
#define _XOPEN_SOURCE
- Часы в Windows показывают неправильное время
- Использование CAST() с типами дата
- Глава 7. Дата и время
- Время показывается в 12-часовом формате, а мне привычнее 24-часовой. Как это изменить?
- Можно ли сделать так, чтобы вместе с часами отображалась и дата?
- Новый винчестер издает странный звук во время работы. Он не похож на тот, с которым работал старый диск. Это нормально и...
- Время от времени оптическая мышь начинает мигать. Она сломалась?
- Почему за время, пока компьютер выключен, сильно отстают системные часы, сбивается дата?
- Дата изменения
- Дата и время
- 3.1.5. Дата и время
- Рис. 90. Дата и время.