strftime,是一种C库函数,strftime() 函数根据区域设置格式化本地时间/日期,函数的功能将时间格式化,或者说格式化一个时间字符串。
strftime在头文件<time.h>中包含,首先看一下time.h的源代码(取自minix2.0版):
/* The <time.h> header is used by the procedures that deal with time. * Handling time is surprisingly complicated, what with GMT, local time * and other factors. Although the Bishop of Ussher (1581-1656) once * calculated that based on the Bible, the world began on 12 Oct. 4004 BC * at 9 o'clock in the morning, in the UNIX world time begins at midnight, * 1 Jan. 1970 GMT. Before that, all was NULL and (void). */ #ifndef _TIME_H #define _TIME_H #define CLOCKS_PER_SEC 60 /* MINIX always uses 60 Hz, even in Europe */ #ifdef _POSIX_SOURCE #define CLK_TCK CLOCKS_PER_SEC /* obsolescent name for CLOCKS_PER_SEC */ #endif #define NULL ((void *)0) #ifndef _SIZE_T #define _SIZE_T typedef unsigned int size_t; #endif #ifndef _TIME_T #define _TIME_T typedef long time_t; /* time in sec since 1 Jan 1970 0000 GMT */ #endif #ifndef _CLOCK_T #define _CLOCK_T typedef long clock_t; /* time in ticks since process started */ #endif struct tm { int tm_sec; /* seconds after the minute [0, 59] */ int tm_min; /* minutes after the hour [0, 59] */ int tm_hour; /* hours since midnight [0, 23] */ int tm_mday; /* day of the month [1, 31] */ int tm_mon; /* months since January [0, 11] */ int tm_year; /* years since 1900 */ int tm_wday; /* days since Sunday [0, 6] */ int tm_yday; /* days since January 1 [0, 365] */ int tm_isdst; /* Daylight Saving Time flag */ }; extern char *tzname[]; /* Function Prototypes. */ #ifndef _ANSI_H #include <ansi.h> #endif _PROTOTYPE( clock_t clock, (void) ); _PROTOTYPE( double difftime, (time_t _time1, time_t _time0) ); _PROTOTYPE( time_t mktime, (struct tm *_timeptr) ); _PROTOTYPE( time_t time, (time_t *_timeptr) ); _PROTOTYPE( char *asctime, (const struct tm *_timeptr) ); _PROTOTYPE( char *ctime, (const time_t *_timer) ); _PROTOTYPE( struct tm *gmtime, (const time_t *_timer) ); _PROTOTYPE( struct tm *localtime, (const time_t *_timer) ); _PROTOTYPE( size_t strftime, (char *_s, size_t _max, const char *_fmt, const struct tm *_timep) ); #ifdef _POSIX_SOURCE _PROTOTYPE( void tzset, (void) ); #endif #ifdef _MINIX _PROTOTYPE( int stime, (time_t *_top) ); #endif #endif /* _TIME_H */
对上面的代码做几点解释:
1、typedef long time_t;
这个time_t表示从 1970.01.01 00:00 (格林尼治时间) 开始到现在的秒数
2、typedef long clock_t;
clock_t表示进程开始到现在的滴答数
3、结构体tm
struct tm { int tm_sec; /* 秒 – 取值区间为[0,59] */ int tm_min; /* 分 - 取值区间为[0,59] */ int tm_hour; /* 时 - 取值区间为[0,23] */ int tm_mday; /* 一个月中的日期 - 取值区间为[1,31] */ int tm_mon; /* 月份(从一月开始,0代表一月) - 取值区间为[0,11] */ int tm_year; /* 年份,其值等于实际年份减去1900 */ int tm_wday; /* 星期 – 取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 */ int tm_yday; /* 从每年的1月1日开始的天数 – 取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 */ int tm_isdst; /* 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的进候,tm_isdst为0;不了解情况时,tm_isdst为负。*/ };
下面具体看一下strftime函数的源代码(strftime.c):
/* * strftime - convert a structure to a string, controlled by an argument */ /* $Header: strftime.c,v 1.3 91/04/22 13:21:03 ceriel Exp $ */ #include <time.h> #include "loc_time.h" /* The width can be -1 in both s_prnt() as in u_prnt(). This * indicates that as many characters as needed should be printed. */ static char * s_prnt(char *s, size_t maxsize, const char *str, int width) { while (width > 0 || (width < 0 && *str)) { if (!maxsize) break; *s++ = *str++; maxsize--; width--; } return s; } static char * u_prnt(char *s, size_t maxsize, unsigned val, int width) { int c; c = val % 10; val = val / 10; if (--width > 0 || (width < 0 && val != 0)) s = u_prnt(s, (maxsize ? maxsize - 1 : 0), val, width); if (maxsize) *s++ = c + '0'; return s; } size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) { size_t n; char *firsts, *olds; if (!format) return 0; _tzset(); /* for %Z conversion */ firsts = s; while (maxsize && *format) { while (maxsize && *format && *format != '%') { *s++ = *format++; maxsize--; } if (!maxsize || !*format) break; format++; olds = s; switch (*format++) { case 'a': s = s_prnt(s, maxsize, _days[timeptr->tm_wday], ABB_LEN); maxsize -= s - olds; break; case 'A': s = s_prnt(s, maxsize, _days[timeptr->tm_wday], -1); maxsize -= s - olds; break; case 'b': s = s_prnt(s, maxsize, _months[timeptr->tm_mon], ABB_LEN); maxsize -= s - olds; break; case 'B': s = s_prnt(s, maxsize, _months[timeptr->tm_mon], -1); maxsize -= s - olds; break; case 'c': n = strftime(s, maxsize, "%a %b %d %H:%M:%S %Y", timeptr); if (n) maxsize -= n; else maxsize = 0; s += n; break; case 'd': s = u_prnt(s, maxsize, timeptr->tm_mday, 2); maxsize -= s - olds; break; case 'H': s = u_prnt(s, maxsize, timeptr->tm_hour, 2); maxsize -= s - olds; break; case 'I': s = u_prnt(s, maxsize, (timeptr->tm_hour + 11) % 12 + 1, 2); maxsize -= s - olds; break; case 'j': s = u_prnt(s, maxsize, timeptr->tm_yday + 1, 3); maxsize -= s - olds; break; case 'm': s = u_prnt(s, maxsize, timeptr->tm_mon + 1, 2); maxsize -= s - olds; break; case 'M': s = u_prnt(s, maxsize, timeptr->tm_min, 2); maxsize -= s - olds; break; case 'p': s = s_prnt(s, maxsize, (timeptr->tm_hour < 12) ? "AM" : "PM", 2); maxsize -= s - olds; break; case 'S': s = u_prnt(s, maxsize, timeptr->tm_sec, 2); maxsize -= s - olds; break; case 'U': s = u_prnt(s, maxsize, /* ??? */ (timeptr->tm_yday + 7 - timeptr->tm_wday) / 7, 2); maxsize -= s - olds; break; case 'w': s = u_prnt(s, maxsize, timeptr->tm_wday, 1); maxsize -= s - olds; break; case 'W': s = u_prnt(s, maxsize, /* ??? */ (timeptr->tm_yday+7-(timeptr->tm_wday+6)%7)/7,2); maxsize -= s - olds; break; case 'x': n = strftime(s, maxsize, "%a %b %d %Y", timeptr); if (n) maxsize -= n; else maxsize = 0; s += n; break; case 'X': n = strftime(s, maxsize, "%H:%M:%S", timeptr); if (n) maxsize -= n; else maxsize = 0; s += n; break; case 'y': s = u_prnt(s, maxsize, timeptr->tm_year % 100, 2); maxsize -= s - olds; break; case 'Y': s = u_prnt(s, maxsize, timeptr->tm_year + YEAR0, -1); maxsize -= s - olds; break; case 'Z': s = s_prnt(s, maxsize, _tzname[(timeptr->tm_isdst > 0)], -1); maxsize -= s - olds; break; case '%': *s++ = '%'; maxsize--; break; default: /* A conversion error. Leave the loop. */ while (*format) format++; break; } } if (maxsize) { *s = '\0'; return s - firsts; } return 0; /* The buffer is full */ }
有个函数_tzset()引起了我的注意,这个函数的源代码在misc.c中:
void _tzset(void) { #if defined(__BSD4_2) struct timeval tv; struct timezone tz; _gettimeofday(&tv, &tz); _daylight = tz.tz_dsttime; _timezone = tz.tz_minuteswest * 60L; #elif !defined(_POSIX_SOURCE) && !defined(__USG) #if !defined(_MINIX) /* MINIX has no ftime() */ struct timeb time; _ftime(&time); _timezone = time.timezone * 60L; _daylight = time.dstflag; #endif #endif /* !_POSIX_SOURCE && !__USG */ parseTZ(getenv("TZ")); /* should go inside #if */ #if defined(__USG) || defined(_POSIX_SOURCE) tzname[0] = _tzname[0]; tzname[1] = _tzname[1]; #if defined(__USG) timezone = _timezone; daylight = _daylight; #endif #endif /* __USG || _POSIX_SOURCE */ }
参见linux文档:http://www.man7.org/linux/man-pages/man3/tzset.3.html
The tzset() function initializes the tzname variable from the TZ environment variable. This function is automatically called by the other time conversion functions that depend on the timezone. In a System-V-like environment, it will also set the variables timezone (seconds West of UTC) and daylight (to 0 if this timezone does not have any daylight saving time rules, or to nonzero if there is a time during the year when daylight saving time applies).
可见这个函数的主要是起到初始化时区环境的作用,经常配合其他函数一起使用。知道这个就可以了,太深入的话非得扯到POSIX等等
经过我注释后的代码如下:
/* * strftime - convert a structure to a string, controlled by an argument */ /* $Header: strftime.c,v 1.3 91/04/22 13:21:03 ceriel Exp $ */ #include <time.h> #include "loc_time.h" /* The width can be -1 in both s_prnt() as in u_prnt(). This * indicates that as many characters as needed should be printed. */ //将字符串str中的width个字符复制到maxsize字符串数组中 //前提:width <= strlen(str) //if(maxsize >= width)全部复制 //else 复制maxsize个字符 static char * s_prnt(char *s, size_t maxsize, const char *str, int width) { while (width > 0 || (width < 0 && *str)) { if (!maxsize) break; *s++ = *str++; maxsize--; width--; } return s; } /* val=256 width=5 maxsize=5 1 c=6 val=25 width=4 maxsize=4 2 c=5 val=2 width=3 maxsize=3 3 c=2 val=0 width=2 maxsize=2 4 c=0 val=0 width=1 maxsize=1 5 c=0 val=0 width=0 maxsize=0 */ //运用递归,将val转化为宽度为width的字符 //val的位数<width,前面补0 static char * u_prnt(char *s, size_t maxsize, unsigned val, int width) { int c; c = val % 10; //保留val除法后的尾数 val = val / 10; //保留商 if (--width > 0 || (width < 0 && val != 0)) s = u_prnt(s, (maxsize ? maxsize - 1 : 0), val, width); if (maxsize) *s++ = c + '0'; return s; } //我们可以根据format指向字符串中格式命令把timeptr中保存的时间信息放在s指向的字符串中, //最多向s中存放maxsize个字符。该函数返回向s指向的字符串中放置的字符数。 size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *timeptr) { size_t n; char *firsts, *olds; if (!format) return 0; //format为空,不显示 _tzset(); /* for %Z conversion */ firsts = s; while (maxsize && *format) { while (maxsize && *format && *format != '%') { *s++ = *format++; maxsize--; } if (!maxsize || !*format) break; //当max减为0或format结束的时候退出 format++; //确保上面的while循环是由于遇到format的%字符而退出 olds = s; switch (*format++) { //遇到相关的字符做相应的处理 case 'a': s = s_prnt(s, maxsize, _days[timeptr->tm_wday], ABB_LEN); maxsize -= s - olds; break; case 'A': s = s_prnt(s, maxsize, _days[timeptr->tm_wday], -1); maxsize -= s - olds; break; case 'b': s = s_prnt(s, maxsize, _months[timeptr->tm_mon], ABB_LEN); maxsize -= s - olds; break; case 'B': s = s_prnt(s, maxsize, _months[timeptr->tm_mon], -1); maxsize -= s - olds; break; case 'c': n = strftime(s, maxsize, "%a %b %d %H:%M:%S %Y", timeptr); if (n) maxsize -= n; else maxsize = 0; s += n; break; case 'd': s = u_prnt(s, maxsize, timeptr->tm_mday, 2); maxsize -= s - olds; break; case 'H': s = u_prnt(s, maxsize, timeptr->tm_hour, 2); maxsize -= s - olds; break; case 'I': s = u_prnt(s, maxsize, (timeptr->tm_hour + 11) % 12 + 1, 2); maxsize -= s - olds; break; case 'j': s = u_prnt(s, maxsize, timeptr->tm_yday + 1, 3); maxsize -= s - olds; break; case 'm': s = u_prnt(s, maxsize, timeptr->tm_mon + 1, 2); maxsize -= s - olds; break; case 'M': s = u_prnt(s, maxsize, timeptr->tm_min, 2); maxsize -= s - olds; break; case 'p': s = s_prnt(s, maxsize, (timeptr->tm_hour < 12) ? "AM" : "PM", 2); maxsize -= s - olds; break; case 'S': s = u_prnt(s, maxsize, timeptr->tm_sec, 2); maxsize -= s - olds; break; case 'U': s = u_prnt(s, maxsize, /* ??? */ (timeptr->tm_yday + 7 - timeptr->tm_wday) / 7, 2); maxsize -= s - olds; break; case 'w': s = u_prnt(s, maxsize, timeptr->tm_wday, 1); maxsize -= s - olds; break; case 'W': s = u_prnt(s, maxsize, /* ??? */ (timeptr->tm_yday+7-(timeptr->tm_wday+6)%7)/7,2); maxsize -= s - olds; break; case 'x': n = strftime(s, maxsize, "%a %b %d %Y", timeptr); if (n) maxsize -= n; else maxsize = 0; s += n; break; case 'X': n = strftime(s, maxsize, "%H:%M:%S", timeptr); if (n) maxsize -= n; else maxsize = 0; s += n; break; case 'y': s = u_prnt(s, maxsize, timeptr->tm_year % 100, 2); maxsize -= s - olds; break; case 'Y': s = u_prnt(s, maxsize, timeptr->tm_year + YEAR0, -1); maxsize -= s - olds; break; case 'Z': s = s_prnt(s, maxsize, _tzname[(timeptr->tm_isdst > 0)], -1); maxsize -= s - olds; break; case '%': *s++ = '%'; maxsize--; break; default: /* A conversion error. Leave the loop. */ while (*format) format++; break; } } if (maxsize) { *s = '\0'; return s - firsts; } return 0; /* The buffer is full */ }
#include<stdio.h> #include<time.h> int main() { time_t t; struct tm *pCurrentTime; char dateBuffer[32]; t = time(NULL); pCurrentTime = localtime(&t); strftime(dateBuffer, sizeof(dateBuffer), "[%Y-%m-%d %X]", pCurrentTime); printf("%s\n", dateBuffer); return 0; }
输出结果如下:
[2015-11-26 16:49:14]
非常详细