Вызов time(0)возвращает time_t, представляющее временной интервал от зависящего от реализации начала отсчета (обычно 0:00:00 1 января 1970 года) до текущего момента.
Ошибка 2038 года
Так как time_tможет представлять интервалы времени длиной в 68 лет, а многие реализации для представления текущего времени в качестве начала отсчета используют 1970 год, в большинстве популярных реализаций C++ невозможно представлять даты и времена после 2038 года. Это означает, что если программисты не предпримут мер предосторожности, то в 2038 году большая часть программного обеспечения перестанет работать.
Наиболее удобное представление текущих даты и времени можно получить, преобразовав их с помощью функций localtimeили gmtimeв структуру tm. Структура tmсодержит целочисленные поля, показанные в примере 5.2.
Пример 5.2. Содержимое структуры tm
struct tm {
int tm_sec; // секунды в минуте от 0 до 61 (60 и 61 для секунд координации)
int tm_min; // минуты в часе от 0 до 59
int tm_hour; // часы в сутках от 0 до 23
int tm_mday; // день месяца от 0 до 31
int tm_mon; // месяц года от 0 до 11
int tm_year; // год после 1900
int tm_wday; // дней после воскресенья
int tm_yday; // дней после 1-го января
int tm_isdst; // часы летнего времени
};
При использовании функции gmtimeне забудьте проверить ее возвращаемое значение. Если компьютер, на котором выполняется код, не имеет определенной локальной временной зоны (часового пояса), функция gmtimeне сможет вычислить время UTC и вернет 0. Если передать 0 в функцию asctime, то результатом будет неопределенное поведение.
Функции localtime, gmtimeи asctimeвозвращают указатели на статически размещенные в памяти объекты. Это более эффективно для библиотеки, не означает, что последующие вызовы будут изменять значение этих объектов. Код в примере 5.3 показывает, как это может привести к неожиданным эффектам.
Пример 5.3. Подводные камни использования asctime
void f() {
char* x = asctime(localtime(time(0)));
wait_for_15_seconds(); // выполняет длительную задачу обработки
asctime(localtime(time(0)));
cout << x << endl; // печатает текущее время, а не то что 15 секунд назад.
}
5.2. Форматирование даты/времени в виде строки
Проблема
Требуется преобразовать дату и/или время в строковый формат
Решение
Используйте шаблон класса time_putиз заголовочного файла , как показано в примере 5.4.
Пример 5.4. Форматирование строки даты/времени
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
ostream& formatDateTime(ostream& out, const tm& t, const char* fmt) {
const time_put& dateWriter = use_facet >(out.getloc());
int n = strlen(fmt);
if (dateWriter.put(out, out, ' ', &t, fmt, fmt + n).failed()) {
throw runtime_error("невозможно отформатировать дату и время");
}
return out;
}
string dateTimeToString(const tm& t, const char* format) {
stringstream s;
formatDateTime(s, t.format);
return s.str();
}
tm now() {
time_t now = time(0);
return *localtime(&now);
}
int main() {
try {
string s = dateTimeToString(now(), "%A %B, %d %Y %I:%M%p");
cout << s << endl;
s = dateTimeToString(now(), "%Y-%m-%d %H:%M:%S);
cout << s << endl;
} catch(...) {
cerr << "невозможно отформатировать дату и время" << endl;
return EXIT FAILURE.
}
return EXIT_SUCCESS;
}
Вывод программы из примера 5.4 будет содержать нечто подобное следующему, в зависимости от локальных настроек.
Sunday July, 24 2005 05:48PM 2005-07-24 17:48:11
Обсуждение
Метод putиз time_putиспользует спецификатор форматирования строки, аналогичный строке формата функции С printf. Символы строки формата выводятся в выходной буфер по мере их появления при условии, что им не предшествует символ %. Символ, перед которым стоит %, — это спецификатор формата, который имеет специальное значение, приведенное в табл. 5.1. Спецификаторы формата также поддерживают модификаторы, такие как целое число, указывающее длину поля, как в %4B.
Tабл. 5.1. Спецификаторы формата даты/времени
| Спецификатор |
Описание |
a |
Сокращенное название дня недели (например, Mon (пн)) |
A |
Полное название дня недели (например, Monday (понедельник)) |
b |
Сокращенное название месяца (например, Dec (дек)) |
B |
Полное название месяца (например, May (май)) |
c |
Полные дата и время |
d |
День месяца (01-31) |
H |
Час (00-23) |
I |
Час (01-12) |
j |
День года (001-366) |
m |
Месяц (01-12) |
M |
Минуты (00-59) |
p |
Признак AM/PM |
S |
Секунды, включая до двух секунд координации |
U |
Номер недели (00-53), причем неделя 1 начинается в первое воскресенье |
w |
День недели (0-6), где 0 — это воскресенье |
W |
Номер недели (00-53), причем неделя 1 начинается в первый понедельник |
x |
Дата в формате MM/DD/YY |
X |
Время в формате HH/MM/SS и 24-часовыми часами |
y |
Год текущего столетия (00-99) |
Y |
Год |
Z |
Сокращение временной зоны (часового пояса), или пустая строка, если зона неизвестна |
Библиотека Boost date_time, обсуждаемая в дальнейших рецептах, не содержит возможностей форматирования, предлагаемых time_put. Для удобства пример 5.5 содержит несколько процедур, преобразующих классы даты/времени Boost в формат структуры tm, так что вы можете использовать процедуры time_put.
Читать дальше