-
Notifications
You must be signed in to change notification settings - Fork 7
strftime(3)
strftime - 날짜 및 시간 서식화
#include <time.h>
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
strftime()
함수는 분할 시간 tm
을 서식 format
에 따라 변환해서 그 결과를 크기 max
인 문자 배열 s
에 집어넣는다. 분할 시간 구조체 tm
은 <time.h>
에 정의돼 있다. ctime(3)도 참고.
서식은 널 종료 문자열이며 변환 지정자라는 특별한 문자 열을 포함할 수 있다. 각 변환 지정자는 '%' 문자로 시작해서 변환 지정 문자라는 어떤 다른 문자로 끝난다. 그 외의 모든 문자 열은 일반 문자 열이다.
일반 문자 열의 문자들(널 바이트 포함)은 format
에서 s
로 그대로 복사한다. 하지만 변환 지정자의 문자들은 아래 목록에 나온 대로 바꿔서 넣는다. 이 목록에는 tm
구조체의 이용 필드(들)도 적혀 있다.
%a |
현재 로캘에 따른 요일 축약 이름. (tm_wday 로 계산) |
%A |
현재 로캘에 따른 요일 원래 이름. (tm_wday 로 계산) |
%b |
현재 로캘에 따른 월 축약 이름. (tm_mon 으로 계산) |
%B |
현재 로캘에 따른 월 원래 이름. (tm_mon 으로 계산) |
%c |
현재 로캘에서 많이 쓰는 날짜 및 시간 표현. |
%C |
2자리 정수로 된 세기 번호 (년/100). (SU) (tm_year 로 계산) |
%d |
십진수로 된 월 중 날짜. (01에서 31까지) (tm_mday 로 계산) |
%D |
%m/%d/%y 와 같음. (윽... 미국 전용. 다른 나라에서는 %d/%m/%y 가 꽤 흔하다는 걸 미국인들은 알아 둘 필요가 있다. 즉 국제적인 맥락에서 이 형식은 명확하지 않으므로 쓰지 않는 게 좋다.) (SU) |
%e |
%d 처럼 십진수로 된 월 중 날짜이되, 앞의 0을 공백으로 바꾼 것. (SU) (tm_mday 로 계산) |
%E |
수식자: 다른 형식 사용. 아래 참고. (SU) |
%F |
%Y-%m-%d 와 같음. (ISO 8601 날짜 형식) (C99) |
%G |
ISO 8601 주 기준의 세기 포함한 십진수로 된 연도. (NOTES 참고.) 4자리 연도가 ISO 주 번호(%V 참고)에 부합한다. 그래서 %Y 와 형식 및 값이 같되 ISO 주 번호가 지난해나 다음해에 속하면 그 연도를 쓴다. (TZ) (tm_year , tm_yday , tm_wday 로 계산) |
%g |
%G 와 같되 세기 없이. 즉 2자리 연도. (00-99) (TZ) (tm_year , tm_yday , tm_wday 로 계산) |
%h |
%b 와 같음. (SU) |
%H |
십진수로 된 24시간 시계 시간. (00에서 23까지) (tm_hour 로 계산) |
%I |
신진수로 된 12시간 시계 시간. (01에서 12까지) (tm_hour 로 계산) |
%j |
십진수로 된 연 중 날짜. (001에서 366까지) (tm_yday 로 계산) |
%k |
십진수로 된 (24시간 시계) 시간. (0에서 23까지) 1자리 수 앞에는 공백이 붙음. (%H 참고.) (tm_hour 로 계산) (TZ) |
%l |
십진수로 된 (12시간 시계) 시간. (1에서 12까지) 1자리 수 앞에는 공백이 붙음. (^I 참고.) (tm_hour 로 계산) (TZ) |
%m |
십진수로 된 월. (01에서 12까지) (tm_mon 으로 계산) |
%M |
십진수로 된 분. (00에서 59까지) (tm_min 으로 계산) |
%n |
개행 문자. (SU) |
%O |
수식자: 다른 형식 사용. 아래 참고. (SU) |
%p |
해당 시간 값에 따라 "AM"이나 "PM", 또는 현재 로캘의 해당 문자열. 정오는 "PM"으로 취급하고 자정은 "AM"으로 취급한다. (tm_hour 로 계산) |
%P |
%p 와 같되 소문자. 즉 "am"이나 "pm", 또는 현재 로캘의 해당 문자열. (tm_hour 로 계산) (GNU) |
%r |
오전 오후를 표기한 시간. POSIX 로캘에서는 %I:%M:%S %p 와 같다. (SU) |
%R |
24시간 표기법으로 된 시간 (%H:%M ). (SU) 초를 포함한 버전은 아래 %T 를 보라. |
%s |
에포크 1970-01-01 00:00:00 +0000 (UTC) 이후의 초 수. (TZ) (mktime(tm) 으로 계산) |
%S |
십진수로 된 초. (00에서 60까지) (가끔 있는 윤초를 위해 범위가 60까지임.) (tm_sec 으로 계산) |
%t |
탭 문자. (SU) |
%T |
24시간 표기법으로 된 시간 (%H:%M:%S ). (SU) |
%u |
십진수로 된 주 중 날 번호. 1에서 7까지이며 월요일이 1. %w 참고. (tm_wday 로 계산) (SU) |
%U |
십진수로 된 당해 중 주 번호. 00에서 53까지이며, 첫 번째 일요일을 01번 주 첫날로 삼는다. %V 와 %W 도 참고. (tm_yday 와 tm_wday 로 계산) |
%V |
십진수로 된 ISO 8601 방식 당해 중 주 번호. (아래 참고.) 01에서 53까지이며, 최소 4일이 새해에 있는 첫 번째 주가 1번 주이다. %U 와 %W 도 참고. (tm_year , tm_yday , tm_wday 로 계산) (SU) |
%w |
십진수로 된 주 중 날 번호. 0에서 6까지이며 일요일이 0. %u 참고. (tm_wday 로 계산) |
%W |
십진수로 된 당해 중 주 번호. 00에서 53까지이며, 첫 번째 월요일을 01번 주 첫날로 삼는다. (tm_yday 와 tm_wday 로 계산) |
%x |
현재 로캘에서 많이 쓰는 시간 없는 날짜 표현. |
%X |
현재 로캘에서 많이 쓰는 날짜 없는 시간 표현. |
%y |
세기 없이 십진수로 된 연도. (00에서 99까지) (tm_year 로 계산) |
%Y |
세기 포함한 십진수로 된 연도. (tm_year 로 계산) |
%z |
+hhmm 또는 -hhmm 숫자 형식 시간대 (즉 UTC와의 시간 및 분 차이). (SU) |
%Z |
시간대 이름 또는 축약명. |
%+ |
date(1) 형식의 날짜 및 시간. (TZ) (glibc2에서 지원하지 않음.) |
%% |
'%' 문자 자체. |
몇몇 변환 지정자들에선 변환 지정 문자 앞에 E
나 O
수식자를 붙여서 대체 형식을 써야 한다고 표시할 수 있다. 현재 로캘에서 대체 형식 내지 지정자가 존재하지 않는 경우에는 수식 없이 변환 지정자를 쓴 것처럼 동작하게 된다. (SU) 단일 유닉스 규격에서는 %Ec
, %EC
, %Ex
, %EX
, %Ey
, %EY
, %Od
, %Oe
, %OH
, %OI
, %Om
, %OM
, %OS
, %Ou
, %OU
, %OV
, %Ow
, %OW
, %Oy
를 언급하는데, O
수식자의 효과는 대체 숫자 심볼(가령 로마 숫자)을 쓰는 것이고 E
수식자의 효과는 로캘별 대체 표현을 쓰는 것이다.
결과 문자열이 종료 널 바이트를 포함해서 max
바이트를 초과하지 않는 경우에는 배열 s
에 집어넣은 (종료 널 바이트를 뺀) 바이트 수를 strftime()
이 반환한다. 결과 문자열의 (종료 널 바이트 포함) 길이가 max
바이트를 넘게 될 경우에는 strftime()
이 0을 반환하며, 이때 배열의 내용물은 규정돼 있지 않다.
참고로 반환 값 0이 반드시 오류를 나타내는 건 아니다. 예를 들어 여러 로캘에서 %p
는 빈 문자열이 된다. format
문자열이 비어 있어도 마찬가지로 빈 문자열이 나온다.
환경 변수 TZ
및 LC_TIME
을 쓴다.
이 절에서 사용하는 용어들에 대한 설명은 attributes(7)를 보라.
인터페이스 | 속성 | 값 |
---|---|---|
strftime() |
스레드 안전성 | MT-Safe env locale |
SVr4, C89, C99. ANSI C에 있는 변환(표시 없음), 단일 유닉스 규격에 있는 변환(SU 표시), Olson의 timezone 패키지에서 제공하는 변환(TZ 표시), glibc에서 제공하는 변환(GNU 표시) 사이에는 엄격한 포함 관계가 있다. 단 glibc2에서는 %+
를 지원하지 않는다. 한편으로 glibc2에는 다른 여러 확장이 있다. POSIX.1에서는 ANSI C만을 참조한다. 하지만 POSIX.2에서는 date(1)
하에서 strftime()
에도 적용될 수 있을 여러 확장들을 기술한다. %F
변환은 C99 및 POSIX.1-2001에 있다.
SUSv2에서는 %S
변환자의 허용 범위가 00에서 61까지이다. 이는 1분에 윤초가 두 번 포함될 수 있는 이론적 가능성에 대비하기 위한 것이다. (지금까지 그런 경우는 한 번도 없었다.)
%G
, %g
, %V
는 ISO 8601 표준에 정의된 주 기준 연도로부터 계산한 값들을 내놓는다. 이 체계에서는 월요일부터 한 주가 시작하며, 첫 번째 주에 01번을 붙이고 마지막 주는 52번이나 53번이 된다. 1번 주는 4일 이상이 새해에 속하는 첫 번째 주이다. (즉 01번 주는 목요일을 포함한 그해 첫째 주이기도 하고, 1월 4일을 포함한 주이기도 하다.) 새해의 달력 첫 번째 주에서 3일 이하가 그해에 속할 때 ISO 8601 주 기반 체계에서는 그 날들을 전해의 53번 주로 계산한다. 예를 들어 2010년 1월 1일은 금요일이고, 그래서 그 달력 주의 3일만 2010년에 속한다. 따라서 ISO 8601 주 기반 체계에서는 그 날들을 2009년(%G
)의 53번 주(%V
)에 포함되는 걸로 본다. 즉 ISO 8601 기준 2010년의 01번 주는 2010년 1월 4일 월요일에 시작한다.
glibc에서는 변환 지정자 확장을 몇 가지 제공한다. (이 확장들은 POSIX.1-2001에 명세돼 있지 않지만 몇몇 다른 시스템에서도 비슷한 기능을 제공한다.) '%' 문자와 변환 지정 문자 사이에 선택적으로 플래그와 필드 폭을 지정할 수 있다. (E
및 O
수식자가 있는 경우에는 그 앞에 온다.)
다음 플래그 문자들을 허용한다.
_ |
(밑줄) 수로 된 결과 문자열의 남는 공간을 공백 문자로 채운다. |
- |
(대시) 수로 된 결과 문자열의 남는 공간을 채우지 않는다. |
0 |
수로 된 결과 문자열의 남는 공간을 0으로 채운다. 그 변환 지정 문자의 기본 방식이 공백 문자로 채우기더라도 0을 쓴다. |
^ |
결과 문자열의 알파벳 문자를 대문자로 바꾼다. |
# |
결과 문자열의 대소문자를 뒤바꾼다. (이 플래그는 특정 변환 지정 문자들에만 동작하며 그 중에서도 실제로는 %Z 에서만 유용하다.) |
십진수로 된 폭 지정자가 (존재하지 않을 수도 있는) 플래그 다음에 선택적으로 올 수 있다. 필드의 원래 크기가 그 폭보다 작으면 결과 문자열 왼쪽을 채워서 지정한 폭으로 맞춘다.
출력 문자열이 max
바이트를 넘게 될 경우에 errno
를 설정하지 않는다. 이 때문에 그 오류 경우와 format
문자열이 적법하게 길이 0인 출력 문자열을 만들어 내는 경우를 구별할 수 없다. POSIX.1-2001에서는 strftime()
에 대해 어떤 errno
설정도 명세하고 있지 않다.
일부 버그 있는 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()
동작을 실험해 볼 수 있다.
다음은 glibc의 strftime()
구현이 내놓는 결과 문자열 예시이다.
$ ./a.out '%m'
Result string is "11"
$ ./a.out '%5m'
Result string is "00011"
$ ./a.out '%_5m'
Result string is " 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 returned 0");
exit(EXIT_FAILURE);
}
printf("Result string is \"%s\"\n", outstr);
exit(EXIT_SUCCESS);
}
date(1)
, time(2), ctime(3), setlocale(3), sprintf(3), strptime(3)
2019-03-06