您好,欢迎访问一九零五行业门户网

php的strtotime函数源码分析

最近想实现一个多语言版的strtotime函数,所以阅读了php源码中strtotime函数的实现,很感谢“胖胖”大大的文章( http://www.phppan.com/2011/06/php-strtotime/ ),为本人分析strtotime提供了一个大概的思路,阅读本文前请先阅读 “胖胖”大大的文章。 先
最近想实现一个多语言版的strtotime函数,所以阅读了php源码中strtotime函数的实现,很感谢“胖胖”大大的文章(http://www.phppan.com/2011/06/php-strtotime/),为本人分析strtotime提供了一个大概的思路,阅读本文前请先阅读“胖胖”大大的文章。
先贴上strtotime的分析结果:
1. 使用词法分析器re2c对英文文本的日期时间描述进行分析(/ext/date/lib/parse_date.c中scan())。
2.针对分析的token,做出相应的操作,即计算出英文文本的日期时间描述与标准时间的差值,结果存在结构体timelib_rel_time。
3.根据差值与标准时间,转换为一个时间戳。
我们以例子strtotime(last sunday)为例来说明。
关键的三个结构如下:
typedef struct scanner { int fd; uchar *lim, *str, *ptr, *cur, *tok, *pos; unsigned int line, len; struct timelib_error_container *errors; struct timelib_time *time; const timelib_tzdb *tzdb;} scanner;typedef struct timelib_time { timelib_sll y, m, d; /* year, month, day */ timelib_sll h, i, s; /* hour, minute, second */ double f; /* fraction */ int z; /* gmt offset in minutes */ char *tz_abbr; /* timezone abbreviation (display only) */ timelib_tzinfo *tz_info; /* timezone structure */ signed int dst; /* flag if we were parsing a dst zone */ timelib_rel_time relative; timelib_sll sse; /* seconds since epoch */ unsigned int have_time, have_date, have_zone, have_relative, have_weeknr_day; unsigned int sse_uptodate; /* !0 if the sse member is up to date with the date/time members */ unsigned int tim_uptodate; /* !0 if the date/time members are up to date with the sse member */ unsigned int is_localtime; /* 1 if the current struct represents localtime, 0 if it is in gmt */ unsigned int zone_type; /* 1 time offset, * 3 timezone identifier, * 2 timezone abbreviation */} timelib_time;typedef struct timelib_rel_time { timelib_sll y, m, d; /* years, months and days */ timelib_sll h, i, s; /* hours, minutes and seconds */ int weekday; /* stores the day in 'next monday' */ int weekday_behavior; /* 0: the current day should *not* be counted when advancing forwards; 1: the current day *should* be counted */ int first_last_day_of; int invert; /* whether the difference should be inverted */ timelib_sll days; /* contains the number of *days*, instead of y-m-d differences */ timelib_special special; unsigned int have_weekday_relative, have_special_relative;} timelib_rel_time;
strtotime函数,将任何英文文本的日期时间描述解析为 unix 时间戳,既然这里涉及到英文文本,那么怎么把这个英文文本转换为计算机可以理解的逻辑呢?学过编译原理的同学都知道,在编译的过程中有词法分析阶段,通过词法分析,将字符串转换为token的过程。php解析英文文本的字符串使用了re2c,这个词法分析工具支持正则表达式,在/ext/date/lib/parse_date.re 中scan()就是负责词法分析的过程。
       这里需要特别注意的是,/ext/date/lib/parse_date.re是没被re2c前的原始文件,/ext/date/lib/parse_date.c是被re2c解析后生成的文件,源码分析时阅读/ext/date/lib/parse_date.re就好了,/ext/date/lib/parse_date.c文件中有大量的词法分析代码,一大堆switch, goto, 单是scan()函数就有两万多行,伤不起啊!!!
既然是re2c是使用正则表达式的,我们来查看一下表示last sunday的正则表达式:
reltextnumber = 'first'|'second'|'third'|'fourth'|'fifth'|'sixth'|'seventh'|'eight'|'eighth'|'ninth'|'tenth'|'eleventh'|'twelfth';reltexttext = 'next'|'last'|'previous'|'this';reltextunit = (('sec'|'second'|'min'|'minute'|'hour'|'day'|'fortnight'|'forthnight'|'month'|'year') 's'?) | 'weeks' | daytext;relativetext = (reltextnumber|reltexttext) space reltextunit;
last是reltexttext,“sunday”是reltextunit, 所以last sunday是被解析为relativetext,在/ext/date/lib/parse_date.re查找relativetext 对应的操作:
relativetext { timelib_sll i; int behavior = 0; debug_output(relativetext); timelib_init; timelib_have_relative(); while(*ptr) { i = timelib_get_relative_text((char **) &ptr, &behavior); timelib_eat_spaces((char **) &ptr); timelib_set_relative((char **) &ptr, i, behavior, s); } timelib_deinit; return timelib_relative; }
timelib_get_relative_text()是分析 “last”这个token,关键的结构如下:
typedef struct _timelib_lookup_table { const char *name; int type; int value;} timelib_lookup_table;static timelib_lookup_table const timelib_reltext_lookup[] = { { first, 0, 1 }, { next, 0, 1 }, { second, 0, 2 }, { third, 0, 3 }, { fourth, 0, 4 }, { fifth, 0, 5 }, { sixth, 0, 6 }, { seventh, 0, 7 }, { eight, 0, 8 }, { eighth, 0, 8 }, { ninth, 0, 9 }, { tenth, 0, 10 }, { eleventh, 0, 11 }, { twelfth, 0, 12 }, { last, 0, -1 }, { previous, 0, -1 }, { this, 1, 0 }, { null, 1, 0 }};
代码如下:
static timelib_sll timelib_get_relative_text(char **ptr, int *behavior){ while (**ptr == ' ' || **ptr == '\t' || **ptr == '-' || **ptr == '/') { ++*ptr; } return timelib_lookup_relative_text(ptr, behavior);}static timelib_sll timelib_lookup_relative_text(char **ptr, int *behavior){ char *word; char *begin = *ptr, *end; timelib_sll value = 0; const timelib_lookup_table *tp; while ((**ptr >= 'a' && **ptr = 'a' && **ptr name; tp++) { if (strcasecmp(word, tp->name) == 0) { value = tp->value; *behavior = tp->type; } } free(word); return value;}
当运行完后i= -1, behavior=0(请注意 value = tp->value;*behavior = tp->type; )
接着在
static void timelib_set_relative(char **ptr, timelib_sll amount, int behavior, scanner *s){ const timelib_relunit* relunit; if (!(relunit = timelib_lookup_relunit(ptr))) { //分析“ sunday” return; } switch (relunit->unit) { case timelib_second: s->time->relative.s += amount * relunit->multiplier; break; case timelib_minute: s->time->relative.i += amount * relunit->multiplier; break; case timelib_hour: s->time->relative.h += amount * relunit->multiplier; break; case timelib_day: s->time->relative.d += amount * relunit->multiplier; break; case timelib_month: s->time->relative.m += amount * relunit->multiplier; break; case timelib_year: s->time->relative.y += amount * relunit->multiplier; break; case timelib_weekday: //计算差值存放在结构体timelib_rel_time timelib_have_weekday_relative(); timelib_unhave_time(); s->time->relative.d += (amount > 0 ? amount - 1 : amount) * 7; s->time->relative.weekday = relunit->multiplier; s->time->relative.weekday_behavior = behavior; break; case timelib_special: timelib_have_special_relative(); timelib_unhave_time(); s->time->relative.special.type = relunit->multiplier; s->time->relative.special.amount = amount; }}
timelib_lookup_relunit的关键结构体和代码如下:
typedef struct _timelib_relunit { const char *name; int unit; int multiplier;} timelib_relunitstatic timelib_relunit const timelib_relunit_lookup[] = { { sec, timelib_second, 1 }, { secs, timelib_second, 1 }, { second, timelib_second, 1 }, { seconds, timelib_second, 1 }, { min, timelib_minute, 1 }, { mins, timelib_minute, 1 }, { minute, timelib_minute, 1 }, { minutes, timelib_minute, 1 }, { hour, timelib_hour, 1 }, { hours, timelib_hour, 1 }, { day, timelib_day, 1 }, { days, timelib_day, 1 }, { week, timelib_day, 7 }, { weeks, timelib_day, 7 }, { fortnight, timelib_day, 14 }, { fortnights, timelib_day, 14 }, { forthnight, timelib_day, 14 }, { forthnights, timelib_day, 14 }, { month, timelib_month, 1 }, { months, timelib_month, 1 }, { year, timelib_year, 1 }, { years, timelib_year, 1 }, { monday, timelib_weekday, 1 }, { mon, timelib_weekday, 1 }, { tuesday, timelib_weekday, 2 }, { tue, timelib_weekday, 2 }, { wednesday, timelib_weekday, 3 }, { wed, timelib_weekday, 3 }, { thursday, timelib_weekday, 4 }, { thu, timelib_weekday, 4 }, { friday, timelib_weekday, 5 }, { fri, timelib_weekday, 5 }, { saturday, timelib_weekday, 6 }, { sat, timelib_weekday, 6 }, { sunday, timelib_weekday, 0 }, { sun, timelib_weekday, 0 }, { weekday, timelib_special, timelib_special_weekday }, { weekdays, timelib_special, timelib_special_weekday }, { null, 0, 0 }};static const timelib_relunit* timelib_lookup_relunit(char **ptr){ char *word; char *begin = *ptr, *end; const timelib_relunit *tp, *value = null; while (**ptr != '\0' && **ptr != ' ' && **ptr != ',' && **ptr != '\t') { ++*ptr; } end = *ptr; word = calloc(1, end - begin + 1); memcpy(word, begin, end - begin); for (tp = timelib_relunit_lookup; tp->name; tp++) { if (strcasecmp(word, tp->name) == 0) { value = tp; break; } } free(word); return value;}
运行完,可得到结构体timelib_relunit,其中的值是{ sunday,      timelib_weekday, 0 },
最后在下面的代码中获取一个差值
case timelib_weekday: //计算差值存放在结构体timelib_rel_time timelib_have_weekday_relative(); timelib_unhave_time(); s->time->relative.d += (amount > 0 ? amount - 1 : amount) * 7; s->time->relative.weekday = relunit->multiplier; s->time->relative.weekday_behavior = behavior; break;
得到差值后,通过下面的三个函数转换为一个时间戳:
static void do_adjust_relative(timelib_time* time) //把差值转换为标准时间{ if (time->relative.have_weekday_relative) { do_adjust_for_weekday(time); } timelib_do_normalize(time); if (time->have_relative) { time->s += time->relative.s; time->i += time->relative.i; time->h += time->relative.h; time->d += time->relative.d; time->m += time->relative.m; time->y += time->relative.y; } switch (time->relative.first_last_day_of) { case 1: /* first */ time->d = 1; break; case 2: /* last */ time->d = 0; time->m++; break; } timelib_do_normalize(time);}static void do_adjust_for_weekday(timelib_time* time) //对星期类型进行处理{ timelib_sll current_dow, difference; current_dow = timelib_day_of_week(time->y, time->m, time->d); if (time->relative.weekday_behavior == 2) { if (time->relative.weekday == 0) { time->relative.weekday = 7; } time->d -= current_dow; time->d += time->relative.weekday; return; } difference = time->relative.weekday - current_dow; if ((time->relative.d relative.d >= 0 && difference relative.weekday_behavior)) { difference += 7; } if (time->relative.weekday >= 0) { time->d += difference; } else { time->d -= (7 - (abs(time->relative.weekday) - current_dow)); } time->relative.have_weekday_relative = 0;}void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi) //转换为时间戳{ timelib_sll res = 0; do_adjust_special_early(time); do_adjust_relative(time); do_adjust_special(time); res += do_years(time->y); res += do_months(time->m, time->y); res += do_days(time->d); res += do_time(time->h, time->i, time->s); time->sse = res; res += do_adjust_timezone(time, tzi); time->sse = res; time->sse_uptodate = 1; time->have_relative = time->relative.have_weekday_relative = time->relative.have_special_relative = 0;}
[文章作者]曾健生
[作者邮箱]zengjiansheng1@126.com
[作者qq]190678908
[博客]  http://blog.csdn.net/newjueqi
其它类似信息

推荐信息