strftime(3) форматирование даты и времени

ОБЗОР

#include <time.h>


size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);

ОПИСАНИЕ

Функция strftime() преобразует время из структуры tm в соответствии с определённым форматом format и помещает результат в символьный массив s размера max.

Формат представляет собой строку с null в конце, в которой могут содержаться специальные символьные последовательности, называемые определителями преобразования, каждая из которых состоит из символа '%' и ещё одного символа, называемого символом определителя преобразования. Все остальные символьные последовательности являются обычными символьными последовательностями.

Символы из обычных символьных последовательностей (включая байт null) копируются как есть из format в s. Символы из определителей преобразования заменяются следующем образом:

%a
Аббревиатура названия дня недели в зависимости от текущей локали.
%A
Полное название дня недели в зависимости от текущей локали.
%b
Аббревиатура названия месяца в зависимости от текущей локали.
%B
Полное название месяца в зависимости от текущей локали.
%c
Предпочтительный формат даты и времени для текущей локали.
%C
Век (год/100) как целое двухразрядное число. (SU)
%d
День месяца в десятичной форме (от 01 до 31).
%D
Эквивалентно %m/%d/%y (только для американцев, в других странах обычная форма — %d/%m/%y. Это означает, что международный контекст этого формата сомнителен и лучше этот формат не использовать). (SU)
%e
То же, что и %d: десятичное число, обозначающее номер дня месяца, но вместо начального нуля ставится пробел. (SU)
%E
Модификатор: использовать другой формат, смотрите далее. (SU)
%F
Эквивалентно %Y-%m-%d (формат даты согласно ISO 8601). (C99)
%G
Год на основе недель с веком в виде десятичного числа, согласно ISO 8601 (смотрите ЗАМЕЧАНИЯ). Год как четырехзначное число, соответствующее номер недели ISO (смотрите %V). Это тот же формат и значение, что и %Y, но если номер недели ISO входит также в прошедший или последующий год, то в этом случае отображается прошедший год. (TZ)
%g
То же, что и %G, но без века, то есть двухразрядное число года (00-99). (TZ)
%h
Эквивалентно %b. (SU)
%H
Час как десятичное число в 24-часовых часах (от 00 до 23).
%I
Час как десятичное число в 12-часовых часах (от 01 до 12).
%j
День года в виде десятичного числа (от 001 до 366).
%k
Час (24-часовые часы) в виде десятичного числа (от 0 до 23); цифры предваряются пробелом (смотрите также %H). (TZ)
%l
Час (12-часовые часы) в виде десятичного числа (от 1 до 12); цифры предваряются пробелом (смотрите также %l). (TZ)
%m
Месяц в виде десятичного числа (от 01 до 12).
%M
Минута в виде десятичного числа (от 00 до 59).
%n
Символ новой строки. (SU)
%O
Модификатор: использовать другой формат, смотрите далее. (SU)
%p
Или «AM» или «PM», в соответствии со значением времени, или соответствующая строка из текущей локали. Полдень считается как «PM», а полночь как «AM».
%P
Подобно %p но в нижнем регистре: «am» или «pm» или соответствующая строка из текущей локали. (GNU)
%r
Время в формате с a.m. или p.m. notation. В локали POSIX это эквивалентно %I:%M:%S %p. (SU)
%R
Время в 24-часовом формате (%H:%M). (SU) Версия с секундами описана для %T далее.
%s
Количество секунд, которое прошло с начала эпохи, 1970-01-01 00:00:00 +0000 (UTC). (TZ)
%S
Секунда в виде десятичного числа (от 00 до 60) (диапазон до 60 включительно позволяет учитывать високосные секунды).
%t
Символ табуляции. (SU)
%T
Время в 24-часовом формате (%H:%M:%S). (SU)
%u
День недели в цифровом формате (от 1 до 7), понедельник это 1. Смотрите также %w. (SU)
%U
Номер недели текущего года в виде десятичного числа (от 00 до 53), начиная с первого воскресенья как первого дня недели 01. Смотрите также %V и %W.
%V
Номер недели согласно ISO 8601 (смотрите ЗАМЕЧАНИЯ) в текущем году в виде десятичного числа (от 01 до 53), где неделя 1 это первая неделя, минимум 4 дня которой находятся в новом году. Смотрите также %U и %W. (SU)
%w
День недели в цифровом формате (от 0 до 6), воскресенье это 0. Смотрите также %u.
%W
Номер недели текущего года в виде десятичного числа (от 00 до 53), начиная с первого понедельника как первого дня недели 01.
%x
Предпочтительный формат даты без времени для текущей локали.
%X
Предпочтительный формат времени без даты для текущей локали.
%y
Год без века в виде десятичного числа (от 00 до 99).
%Y
Год с веком в виде десятичного числа.
%z
Числовой часовой пояс +hhmm или -hhmm (то есть смещение часов и минут относительно UTC). (SU)
%Z
Название или сокращение часового пояса.
%+
Дата и время в формате date(1). (TZ) (не поддерживается в glibc2)
%%
Символ '%'.

Некоторые определения преобразования могут быть изменены с помощью указания перед символом определителя преобразования модификатора E или O, который указывает на необходимость использования альтернативного формата. Если альтернативный формат или определение не существует в текущей локали, то поведение будет таким же как если бы проводилось не изменённое преобразование. (SU) В Single UNIX Specification упомянуты %Ec, %EC, %Ex, %EX, %Ey, %EY, %Od, %Oe, %OH, %OI, %Om, %OM, %OS, %Ou, %OU, %OV, %Ow, %OW, %Oy, где модификатор O означает использование альтернативных численных символов (например, римские цифры), а модификатор E отражает альтернативное представление, зависящее от текущей локали.

Структура времени tm в виде компонент определена в <time.h>. Смотрите также ctime(3).

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Предоставляя строку результата с завершающим байтом null, не превышающую max байт, функция strftime() возвращает количество символов (без учёта конечного байта null), помещённых в массив s. Если длина строки результата (включая завершающий байт null) превысила бы max байт, то strftime() возвращает 0, а содержимое массива будет неопределенным.

Заметим, что возвращаемое значение 0 не всегда означает ошибку. Например, во многих локалях %p представляет собой пустую строку. Пустая строка format считается пустой строкой.

ОКРУЖЕНИЕ

Используются переменные окружения TZ и LC_TIME.

АТРИБУТЫ

Описание терминов данного раздела смотрите в attributes(7).
ИнтерфейсАтрибутЗначение
strftime() безвредность в нитяхбезвредно (MT-Safe env locale)

СООТВЕТСТВИЕ СТАНДАРТАМ

SVr4, C89, C99. Большинство этих определителей поддерживаются стандартом ANSI C (не помечен), Single UNIX Specification (помечен как SU), пакетом часовых поясов Олсона (Olson) (помечен как TZ) и glibc (помечен как GNU), кроме %+, которое не поддерживается в glibc2. С другой стороны, в glibc2 есть несколько дополнительных расширений. В POSIX.1 есть ссылка только на ANSI C; в POSIX.2 описание date(1) содержит несколько расширений, которые можно применить и к strftime(). Преобразование %F есть в C99 и POSIX.1-2001.

В SUSv2 определитель %S позволяет использовать диапазон от 00 до 61, что даёт теоретическую возможность включать в минуту двойную високосную секунду (такой минуты ещё никогда не было).

ЗАМЕЧАНИЯ

Учёт недель в ISO 8601

Определители %G, %g и %V заменяются значениями, вычисленными из года на основе недели, который определяется стандартом ISO 8601. В этой системе недели начинаются с понедельника и нумеруются с 01 до 52 или 53. Неделя 1 — это первая неделя, где четыре или более дней находятся в новом году (иначе говоря, неделя 01 это первая неделя года, в которой есть четверг; или неделя с 4 января). Если три или менее дней первой календарной недели выпадают на новый год, то согласно системе счёта недель ISO 8601 эти дни являются частью недели 53 предыдущего года. Например, 1 января 2010 это пятница, то есть только три дня этой календарной недели выпадают на 2010. То есть согласно системе недель ISO 8601 эти три дня являются частью недели 53 (%V) 2009 года (%G); неделя 01 по ISO 8601 для 2010 года начинается с понедельника 4 января 2010 года.

Замечания по glibc

В glibc есть несколько расширений определений преобразования (эти расширения не входят в POSIX.1-2001, но в некоторых других системах есть подобные возможности). Между символом '%' и символом определителя преобразования может быть указан flag и поле width (и они указываются до модификаторов E или O).

Допускаются следующие символьные флаги:

_
(подчёркивание) Дополнять строку числового результата пробелами.
-
(перенос) Не дополнять строку числового результата.
0
Дополнять строку числового результата нулями, даже если для символа определителя преобразования по умолчанию используются пробелы для дополнения.
^
Преобразовывать алфавитные символы в строке результата в заглавные.
#
Изменять регистр символов в строке результата на противоположный (этот флаг работает только с некоторыми символами определителей преобразования, и среди них действительно реально полезно только с %Z).

После флага (возможно отсутствующего) может быть указан десятичный определитель ширины. Если реальный размер поля меньше чем эта ширина, то строка результата дополняется (слева) до указанной ширины.

ДЕФЕКТЫ

Если строка результата превысила бы max байт, значение errno не изменяется. Это делает невозможным отличить данную ошибку от случая, когда для строки format обоснованно создаётся строка результата нулевой длины. В POSIX.1-2001 не определены какие-либо значения errno для strftime().

Некоторые дефектные версии gcc(1) выдают сообщение об использовании %c: warning: `%c' yields only last 2 digits of year in some locales. Естественно, программистам предлагается использовать %c, что даёт предпочтительное представление даты и времени. Каждый, кто встречает подобного вида странные путаницы, обходит эту проблему gcc(1). Относительно понятный способ — это добавить промежуточную функцию

size_t
my_strftime(char *s, size_t max, const char *fmt,
            const struct tm *tm)
{
    return strftime(s, max, fmt, tm);
}

В настоящее время, у gcc(1) есть параметр -Wno-format-y2k, который отключает предупреждение, поэтому такой способ решения больше не нужен.

ПРИМЕР

Формат даты согласно RFC 2822 (в английской локали с %a и %b)

"%a, %d %b %Y %T %z"

Формат даты согласно RFC 822 (в английской локали с %a и %b)

"%a, %d %b %y %T %z"

Пример программы

Представленную ниже программу можно использовать для экспериментов с strftime().

Несколько примеров строк результата, созданных strftime() в реализации glibc:

$ ./a.out '%m'
Строка результата: "11"
$ ./a.out '%5m'
Строка результата: "00011"
$ ./a.out '%_5m'
Строка результата: "   11"

Исходный код программы

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
int
main(int argc, char *argv[])
{
    char outstr[200];
    time_t t;
    struct tm *tmp;
    t = time(NULL);
    tmp = localtime(&t);
    if (tmp == NULL) {
        perror("localtime");
        exit(EXIT_FAILURE);
    }
    if (strftime(outstr, sizeof(outstr), argv[1], tmp) == 0) {
        fprintf(stderr, "strftime вернула 0");
        exit(EXIT_FAILURE);
    }
    printf("Строка результата: \"%s\"\n", outstr);
    exit(EXIT_SUCCESS);
}