php的strtotime函数源码分析

2023-05-16

最近想实现一个多语言版的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 <= '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;
}

当运行完后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_relunit

static 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 < 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.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








本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

php的strtotime函数源码分析 的相关文章

  • 解决magento中guest页面cookie保存时间过短问题

    在magento新的企业版11 xff0c 有个新的功能退货 xff08 RMA xff09 xff0c 不仅能退货 xff0c 还能以guest的身份查看订单的各种信息 xff0c 但在IE下有个问题 xff1a cookies的保存时间
  • 指定magento后台使用的theme

    众所周知 xff0c magneto是能后台配置中指定前台使用的是哪个theme xff0c 但后台呢 xff1f 指定后台的theme xff0c 需要在添加如下的配置 xff1a lt config gt lt stores gt lt
  • MySQL 数据库性能优化之表结构优化

    作者 xff1a Sky Jian 可以任意转载 但转载时务必以超链接形式标明文章原始出处 和 作者信息 及 版权声明 链接 xff1a http isky000 com database mysql perfornamce tuning
  • MySQL 数据库性能优化之索引优化

    作者 xff1a Sky Jian 可以任意转载 但转载时务必以超链接形式标明文章原始出处 和 作者信息 及 版权声明 链接 xff1a http isky000 com database mysql performance tuning
  • MySQL 数据库性能优化之SQL优化

    作者 xff1a Sky Jian 可以任意转载 但转载时务必以超链接形式标明文章原始出处 和 作者信息 及 版权声明 链接 xff1a http isky000 com database mysql performance tuning
  • “巨型”的ESP8266模块,快来围观。

    作者 xff1a 晓宇 xff0c 排版 xff1a 晓宇 微信公众号 xff1a 芯片之家 xff08 ID xff1a chiphome dy xff09 01 巨型ESP8266 ESP8266几乎无人不知 xff0c 无人不晓了吧
  • MySQL数据库性能优化之存储引擎选择

    作者 xff1a Sky Jian 可以任意转载 但转载时务必以超链接形式标明文章原始出处 和 作者信息 及版权声明 链接 xff1a http isky000 com database mysql performance tuning s
  • 配置magento后台翻译

    同事在magento中添加了若干翻译 xff0c 但总是没法再后台显示 xff0c 我帮忙查了很久 xff0c 终于找到问题所在了 xff0c 原来没有配置对应module的后台翻译文件 xff0c 所以在后台显示不出翻译 这里贡献一份前后
  • apache安装新模块的方法

    1 检查是否安装 a 编译安装http bin apachectl l grep mod expires b 如果是以DSO方式编译的 xff0c 则查看 ll modules grep expires 注意 xff0c 以上两种情况不能同
  • prototype.js中hide()和show()的一个注意事项

    在项目中遇到一个问题 xff0c 用prototype js的show xff08 xff09 方法要设置某个div为display block 但总是没法成功 xff0c 后来查了手册 xff1a http api prototypejs
  • doxygentoolkit.vim 用法

    http blog chinaunix net space php uid 61 20570759 amp do 61 blog amp id 61 1922274 早就安上了这个东西 xff0c 只是一直没研究它怎么用 因为 emacs
  • 一些vim知识的摘录

    h function list 看内置函数 b name variable local to a buffer w name variable local to a window g name global variable also in
  • 为什么 Vim 使用 HJKL 键作为方向键

    出处 xff1a http blog jobbole com 18650 导读 xff1a 关于这个问题 xff0c 以前网络上有一种说法 xff0c 手指放在键盘上输入时 xff0c HJKL 比方向键距离手指更近 xff0c 自然输入效
  • 蚂蚁变大象:浅谈常规网站是如何从小变大的(一)

    http zgwangbo blog 51cto com 4977613 849529 标签 xff1a 架构 web 原创作品 xff0c 允许转载 xff0c 转载时请务必以超链接形式标明文章 原始出处 作者信息和本声明 否则将追究法律
  • 【转】高效使用vim

    出处 xff1a http www cnblogs com hyddd archive 2010 04 08 1706863 html 英文出处 xff1a jmcpherson org editing html 翻译引用 xff1a ti
  • 浅谈HTTP中Get与Post的区别

    http www cnblogs com hyddd archive 2009 03 31 1426026 html Http定义了与服务器交互的不同方法 xff0c 最基本的方法有4种 xff0c 分别是GET xff0c POST xf
  • PID算法搞不懂?看这篇文章就够了。

    点击上方 大鱼机器人 xff0c 选择 置顶 星标公众号 福利干货 xff0c 第一时间送达 xff01 转自知乎 xff1a jason 原文链接 xff1a https zhuanlan zhihu com p 74131690 1 目
  • Http Message结构学习总结

    http www cnblogs com hyddd archive 2009 04 19 1438971 html 最近做的东西需要更深入地了解Http协议 xff0c 故死磕了一下RFC2616 xff0d HTTP 1 1协议 xff
  • 浅析数字证书

    出处 xff1a http www cnblogs com hyddd archive 2009 01 07 1371292 html hyddd原创 xff0c 转载请说明出处 gt 最近看会Session hijack的东西 xff0c
  • Cookie小记

    出处 xff1a http www cnblogs com hyddd archive 2008 12 26 1363229 html 最近在工作上经常看一些安全相关的东西 xff0c Cookie以前看过 xff0c 但了解不深 xff0

随机推荐

  • Session小记

    出处 xff1a http www cnblogs com hyddd archive 2008 12 29 1364646 html 看了一些Session的资料 xff0c 在这里再总结一下Session相关的知识 很多是从网上搜集的资
  • 点击<a href="#"/>后屏幕滚动问题

    问 xff1a 当 lt a href 61 34 34 gt 点击后屏幕会滚动到最上面 xff0c 有啥办法不让屏幕滚动 xff1f 答 xff1a href 61 34 javascript void 0 34 或 nclick 61
  • 内存管理知识

    原创作品 xff0c 允许转载 xff0c 转载时请务必以超链接形式标明文章 原始出处 作者信息和本声明 否则将追究法律责任 http xqtesting blog 51cto com 4626073 808548 一般的程序语言 xff0
  • 用户体验:别让我想,别让我停!

    http xqtesting blog 51cto com 4626073 813561 在交互设计中 xff0c 存在着几条普遍的法则令网页设计更有效 最重要的一条是 别让我思考 xff0c 越简洁越好 比如不要因为奇怪的表达方式强迫用户
  • MySQL慢查询的两种分析方案 slow sql

    http blog csdn net ylqmf article details 6541542 前一段日子 xff0c 我曾经设置了一次记录在MySQL数据库中对慢于1秒钟的SQL语句进行查询 想起来有几个十分设置的方法 xff0c 有几
  • 如何使用SQL Profiler 性能分析器

    http blog csdn net ylqmf article details 6541625 ysql 的 sql 性能分析器主要用途是显示 sql 执行的整个过程中各项资源的使用情况 分析器可以更好的展示出不良 SQL 的性能问题所在
  • magento中生成https链接的简单方法

    有关magento中https的基础知识 xff0c 请看 magento中的启用https 如果是在项目的后期才决定采用https xff0c 那么就要面临一个问题 xff1a 大量的生成url的代码需要修改 xff0c 这是一个很大的工
  • 树莓派无屏幕连接WiFi

    将刷好 Raspbian 系统的 SD 卡用电脑读取 在 boot 分区 xff0c 也就是树莓派的 boot 目录下新建 wpa supplicant conf 文件 xff0c 按照下面的参考格式填入内容并保存 wpa supplica
  • MySQL数据库存储引擎MyISAM和InnoDB的对比详解

    http www mysqlops com 2011 12 09 myisam E5 92 8Cinnodb E5 AF B9 E6 AF 94 E8 AF A6 E8 A7 A3 html 之前Eugene兄已经写过两篇关于myisam转
  • 为什么magento的rewrite方法对抽象类无效

    magento中 xff0c 是没法通过Mage getModel 34 xx xx 34 配合xml中的 lt rewrite gt 实现abstruct class的rewrite 为什么 xff1f 这需要详细了解一下magento中
  • magento中在.htaccess设置website code

    在 htaccess中 xff0c 添加以下的内容 xff1a SetEnvIf Host www newjueqi com MAGE RUN CODE 61 newjueqi SetEnvIf Host www newjueqi com
  • apache两种工作模式详解

    http blog chinaunix net space php uid 61 20541969 amp do 61 blog amp id 61 351485 刚接触这两个配置时很迷糊 xff0c 全部开启或全部注释没有几多变化 今天搜
  • Apache处理http请求的生命周期

    Apache请求处理循环详解 Apache请求处理循环的11个阶段都做了哪些事情呢 xff1f 1 Post Read Request阶段 在正常请求处理流程中 xff0c 这是模块可以插入钩子的第一个阶段 对于那些想很早进入处理请求的模块
  • 提高MySQL插入记录的速度

    http hi baidu com jackbillow blog item 65ea47248f645521d50742e7 html 在myisam engine下 1 尽量使用insert into table name values
  • 最常用的http状态码

    200 OK 找到了该资源 xff0c 并且一切正常 202 Accepted 服务器已接受请求 xff0c 但尚未处理 amp bsp 301 Moved Permanently 被请求的资源已永久移动到新位置 302 Found 请求的
  • shell中通过ftp批量上传文件

    为了在shell中上传文件 xff0c 需要避免在控制台中通过交互的方式输入ftp的登录密码 xff0c 这时要安装一个强大的ftp命令行工具 xff1a lftp xff0c 通过lftp登录ftp服务器的格式如下 xff1a lftp
  • 你可能不了解的strtotime函数

    出处 xff1a http www phppan com 2011 06 php strtotime 作者 xff1a 胖胖 在前面的文章中 xff0c 我们提到strtotime函数在使用strtotime 1 month 求上一个月的今
  • PHP的词法解析器:re2c

    出处 xff1a http www phppan com 2011 09 php lexical re2c 作者 xff1a 胖胖 re2c是一个扫描器制作工具 xff0c 可以创建非常快速灵活的扫描器 它可以产生高效代码 xff0c 基于
  • 由浅入深探究mysql索引结构原理、性能分析与优化

    出处 xff1a http www phpben com post 61 74 摘要 xff1a 第一部分 xff1a 基础知识 第二部分 xff1a MYISAM 和 INNODB 索引结构 1 简单介绍 B tree B 43 tree
  • php的strtotime函数源码分析

    最近想实现一个多语言版的strtotime函数 xff0c 所以阅读了php源码中strtotime函数的实现 xff0c 很感谢 胖胖 大大的文章 xff08 http www phppan com 2011 06 php strtoti