asctime, asctime_s

asctime, asctime_s

struct tm에 담긴 달력 시간을 사람이 읽을 수 있는 문자열로 바꾸고 싶을 때 쓰는 함수가 asctime이에요. 다만 정적 버퍼를 쓰고 스레드에 안전하지 않아서, C23부터는 폐기됐어요. <time.h>에 정의돼 있어요.

출처: cppreference

본문

asctime()은 주어진 달력 시간 tm을 다음과 같은 고정된 25자 형태의 문자열로 변환해요.

Www Mmm dd hh:mm:ss yyyy\n

각 부분의 의미는 이러해요.

  • Www — 요일을 영어 3글자로 (time_ptr->tm_wday), Mon, Tue, Wed, Thu, Fri, Sat, Sun 중 하나.
  • Mmm — 달을 영어 3글자로 (time_ptr->tm_mon), Jan~Dec 중 하나.
  • dd — 일을 2자리로 (time_ptr->tm_mday), sprintf%2d처럼.
  • hh — 시를 2자리로 (time_ptr->tm_hour), %.2d처럼.
  • mm — 분을 2자리로 (time_ptr->tm_min), %.2d처럼.
  • ss — 초를 2자리로 (time_ptr->tm_sec), %.2d처럼.
  • yyyy — 연도를 4자리로 (time_ptr->tm_year + 1900), %4d처럼.

주의할 점이 몇 가지 있어요.

  • *time_ptr의 어떤 멤버라도 정상 범위를 벗어나면 동작은 정의되지 않아요(undefined behavior).
  • time_ptr->tm_year가 가리키는 달력 연도가 4자리를 넘거나 1000년 미만이어도 동작은 정의되지 않아요.
  • 이 함수는 지역화(localization)를 지원하지 않고, 끝에 붙는 개행 문자를 뗄 수도 없어요.
  • 정적 저장소를 수정하므로 스레드에 안전하지 않아요.
  • C23부터는 폐기(deprecated)되어 새 코드에서 쓰면 안 돼요.
char*                asctime( const struct tm* time_ptr );   // (1) until C23
[[deprecated]] char* asctime( const struct tm* time_ptr );   // (1) since C23
errno_t asctime_s( char* buf, rsize_t bufsz, const struct tm* time_ptr ); // (2) since C11

(2)번 asctime_s는 (1)과 같지만, 결과를 사용자가 제공한 버퍼 buf에 쓰고 항상 null 종결을 보장해요. 다음 오류를 런타임에 감지하면 설치된 제약 처리 함수(constraint handler)를 호출해요.

  • buf 또는 time_ptr이 널 포인터
  • bufsz가 26보다 작거나 RSIZE_MAX보다 큼
  • *time_ptr의 모든 멤버가 정상 범위 안에 있지 않음
  • time_ptr->tm_year가 나타내는 연도가 0보다 작거나 9999보다 큼

대부분의 경계 검사 함수처럼, asctime_s는 구현이 __STDC_LIB_EXT1__을 정의하고 사용자가 <time.h>를 포함하기 전에 __STDC_WANT_LIB_EXT1__을 정수 상수 1로 정의했을 때만 쓸 수 있어요.

매개변수

매개변수 설명
time_ptr 출력할 시간을 담은 tm 객체를 가리키는 포인터
buf 최소 26바이트인 사용자 제공 버퍼를 가리키는 포인터
bufsz 사용자 제공 버퍼의 크기

반환값

  1. 위에서 설명한 날짜·시간 문자열을 담은 정적 null 종결 문자열을 가리키는 포인터. 이 문자열은 asctimectime이 공유할 수 있고, 두 함수 중 하나가 호출될 때마다 덮어써질 수 있어요.

  2. 성공 시 0, 실패 시 0이 아닌 값. 실패하면 buf[0]을 0으로 설정해요(buf가 널이거나 bufsz가 0 또는 RSIZE_MAX보다 큰 경우 제외).

주의

  • asctime은 정적 데이터를 가리키는 포인터를 돌려주고 스레드에 안전하지 않아요. POSIX도 이 함수를 쓸모없어졌다(obsolete)고 보고 strftime 사용을 권장해요. C 표준도 strftime이 더 유연하고 로케일을 고려하므로 asctimeasctime_s 대신 strftime을 권장해요.
  • POSIX는 정의되지 않은 동작을 "출력 문자열이 25자보다 길 때, timeptr->tm_wdaytimeptr->tm_mon이 기대 범위를 벗어났을 때, timeptr->tm_yearINT_MAX - 1990을 넘을 때"로 한정해요.
  • 일부 구현은 timeptr->tm_mday == 0을 "이전 달의 마지막 날"로 취급해요.

예시

localtime()으로 얻은 tmasctime으로 출력하는 흐름이에요. printf%s 형식 때문에 개행이 한 번 더 붙는다는 점을 주의하세요.

#define __STDC_WANT_LIB_EXT1__ 1
#include <stdio.h>
#include <time.h>

int main(void)
{
    struct tm tm = *localtime(&(time_t){time(NULL)});
    printf("%s", asctime(&tm)); // note implicit trailing '\n'

#ifdef __STDC_LIB_EXT1__
    char str[26];
    asctime_s(str, sizeof str, &tm);
    printf("%s", str);
#endif
}

가능한 출력(예시):

Tue May 26 21:51:50 2015
Tue May 26 21:51:50 2015

더 알아보기

  • time_t를 바로 문자열로 바꾸는 함수는 ctime/ctime_s를 봐요.
  • 더 유연하고 로케일을 고려하는 형식 변환은 strftime을 봐요.
  • cppreference의 asctime 원문을 참고해요.