Source code analysis of strtotime functions in php

Source: Internet
Author: User
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








Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.