Recently I want to implement a multi-language version of strtotime function, so I read the implementation of the strtotime function in the php source code. I am very grateful for the big article "fat" (www. phppan. com201106php-strtotime), for me to analyze strtotime provides a rough idea, read this article before reading "fat" big article. First
Recently want to implement a multi-language version of the strtotime function, so read the php source code strtotime function implementation, thank you very much "fat" big article (http://www.phppan.com/2011/06/php-strtotime ), it provides me with a rough idea to analyze strtotime. Before reading this article, please read the big article "fat. First
Recently want to implement a multi-language version of the strtotime function, so read the php source code strtotime function implementation, thank you very much "fat" big article (http://www.phppan.com/2011/06/php-strtotime ), it provides me with a rough idea to analyze strtotime. Before reading this article, please read the big article "fat.
First, paste the analysis result of strtotime:
1. Use the lexical analyzer re2c to analyze the date and time descriptions of English text (scan () in/ext/date/lib/parse_date.c ()).
2. Perform the corresponding operation on the token of the analysis, that is, calculate the difference between the date and time Description of the English text and the standard time. The result contains the timelib_rel_time struct.
3. convert to a timestamp based on the difference value and standard time.
Take the example strtotime ("last Sunday") as an example.
The three key structures are as follows:
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;
The strtotime function parses the date and time description of any English text into a Unix timestamp. Since this involves English text, how can we convert this English text into a logic that can be understood by a computer? Anyone who has learned how to compile knows that there is a lexical analysis stage in the compilation process. The process of converting a string into a token Through lexical analysis. Php parses strings in English text using re2c. This lexical analysis tool supports regular expressions. In/ext/date/lib/parse_date.re, scan () is the process of lexical analysis.
Note that/ext/date/lib/parse_date.re is the original file before re2c, /ext/date/lib/parse_date.c is the file generated after re2c resolution. For source code analysis, read/ext/date/lib/parse_date.re, the/ext/date/lib/parse_date.c file contains a large number of lexical analysis code, a large number of switches, goto, and scan () functions alone, there are more than 20 thousand lines. Sorry !!!
Since re2c uses a regular expression, let's look at the regular expression representing "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" is reltexttext, and "Sunday" is reltextunit. Therefore, "last Sunday" is parsed as relativetext, and the corresponding operations in/ext/date/lib/parse_date.re are searched for 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 () is the token for analyzing the "last". The key structure is as follows:
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 }};
The Code is as follows:
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 <= 'Z') || (**ptr >= 'a' && **ptr <= 'z')) {++*ptr;}end = *ptr;word = calloc(1, end - begin + 1);memcpy(word, begin, end - begin);for (tp = timelib_reltext_lookup; tp->name; tp++) {if (strcasecmp(word, tp->name) == 0) {value = tp->value;*behavior = tp->type;}}free(word);return value;}
After running, I =-1 and behavior = 0 (note that value = tp-> value; * behavior = tp-> type ;)
Then
Static void timelib_set_relative (char ** ptr, timelib_sll amount, int behavior, interval * s) {const timelib_relunit * relunit; if (! (Relunit = timelib_lookup_relunit (ptr) {// analyze "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; brea K; 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: // The calculated difference value is stored in struct (); 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 ;}}
The key struct and code of timelib_lookup_relunit are as follows:
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;}
After running, you can get the timelib_relunit struct, where the value is {"Sunday", TIMELIB_WEEKDAY, 0 },
Finally, obtain a difference value in the following code.
Case TIMELIB_WEEKDAY: // The calculated difference value is stored in the timelib_rel_timeTIMELIB_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;
After obtaining the difference value, convert it to a timestamp using the following three functions:
Static void do_adjust_relative (timelib_time * time) // convert the difference value to the standard 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) // process the week type {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 <0 & difference <0) | (time-> relative. d> = 0 & difference <=-time-> 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. authorization = 0;} void timelib_update_ts (timelib_time * time, timelib_tzinfo * tzi) // convert to timestamp {timelib_sll res = 0; reverse (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 ;}
[Author] Zeng jiansheng
[Author email] zengjiansheng1@126.com
[Author QQ] 190678908
[Blog] http://blog.csdn.net/newjueqi