•strtotime函数的一些用法
•strtotime函数的实现基本原理
•strtotime(” month”)求值失败的原因
strtotime函数的一些用法
strtotime(”JAN”)和strtotime(”January”)
这两个用法的效果是一样的都是返回指定月份的今天如果指定月份没有今天则顺延到下一个月 如在计算二月代码:
代码如下:
echo date("Y
m
d H:i:s"
strtotime("feb"
strtotime("
")));
程序会输出: :: 从表象来看这个结果也许不一定是我们想要的但是这也算是一种解决方案这种方案是由什么决定的呢? strtotime函数在执行月份的计算时只计算了月份相当于直接将月份设置为指定的月份的值而如janjanuary都会有一个对应内部数值
first关键字
first是一个辅助型的关键字它可以与星期天等可以指定确认值的关键字组合使用如求年的第一个星期天
代码如下:
echo date("Y
m
d H:i:s"
strtotime("second sunday"
strtotime("
")))
"<br />";
在PHP的源码中对于first与星期和天的组合使用是分开的即first day对应一个处理操作 在最终的C实现中天的值指定为即time结构中的d字段指定为如下代码
代码如下:
switch (time
>relative
first_last_day_of) {
case
: /* first */
time
>d =
;
break;
case
: /* last */
time
>d =
;
time
>m++;
break;
}
previous和next关键字
与first类似previous关键字可以与星期天组合使用表示指定时间的前一个星期几或前一天如下所示代码
代码如下:
echo date("Y
m
d H:i:s"
strtotime("previous sunday"
strtotime("
")))
"<br />";
程序会输出 ::
程序求的前一个星期天
next关键字与previous相反它表示下一个星期几或后一天
last关键字
last关键字既可以作为上一个也可以作为最后一个如求上一个星期天的日期
代码如下:
echo date("Y
m
d H:i:s"
strtotime("last sunday"
strtotime("
")))
"<br />";
程序会输出 ::
当程序作为最后时其应用场景是指定日期所在月的最后一天相当于date(”t”)的结果如求年月的最后一天
复制代码 代码如下:
echo date("Y
m
d H:i:s"
strtotime("last day"
strtotime("
")))
"<br />";
firstpreviouslast和this关键字在re文件中属于同一组
back和front关键字
这两个关键字是对一天中的小时的向前和向后操作其调用格式如下
代码如下:
echo date("Y
m
d H:i:s"
strtotime("back of
"
strtotime("
")))
"<br />";
echo date("Y
m
d H:i:s"
strtotime("front of
"
strtotime("
")))
"<br />";
•back表示将时间设置指定小时值的后一个小时的分的位置如果是点则算到第二天的点分
•front表示将时间设置指定小时值的前一个小时的分的位置如果是点则算前一天的点分
上面的代码输出 :: :: 其中back of和front of后接的数组必须大于等于并且小于等于
strtotime函数的实现基本原理
官方文档对于strtotime函数的说明是这样的本函数预期接受一个包含美国英语日期格 式的字符串并尝试将其解析为 Unix 时间戳 (自 January :: GMT 起的秒数)其值相对于 now 参数给出的时间如果没有提供此参数则用系统当前时间
这是一个标准PHP内置函数从PHP起就已经存在strtotime函数是以一个扩展的方式加载进来的在ext/date目录下有其全部实现 作为一个标准的内置函数其定义格式也是标准的如下
代码如下:
PHP_FUNCTION(strtotime)
// 处理输入
对于是否有第二个参数有没的处理
// 调用相关函数
实现字符串的解析和结果计算
// 返回结果
}
在输入处理中先识别两个参数都存在的情况并进行处理如果不是此种状态则处理第二个参数不存在的情况 如果都没有则报错返回FALSE
strtotime函数的第一个参数是一个字符串对于这个字符串由于其复杂性PHP使用了其词法解析一样的工具rec 在/ext/date/lib目录下从parse_datere文件我们可以看到其原始的re文件 当用户以参数的形式传入一个字符串此字符串将交给此程序处理针对其字符串的不同匹配不同的处理函数 如strtotime(”yesterday”)调用分析字符串时将匹配yesterday字符串此字符串对应函数如下
代码如下:
yesterday
{
DEBUG_OUTPUT("yesterday");
TIMELIB_INIT;
TIMELIB_HAVE_RELATIVE();
TIMELIB_UNHAVE_TIME();
s
>time
>relative
d =
;
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
这里有几个关键的结构体
代码如下:
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; /* !
if the sse member is up to date with the date/time members */
unsigned int tim_uptodate; /* !
if the date/time members are up to date with the sse member */
unsigned int is_localtime; /*
if the current struct represents localtime
if it is in GMT */
unsigned int zone_type; /*
time offset
*
TimeZone identifier
*
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; /*
: the current day should *not* be counted when advancing forwards;
: 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(” month”)求值失败的原因
虽然strtotime(” month”)这种方法对于后一个月比前一个月的天数的情况会求值失败但是从其本质上来说这并没有错 PHP这样实现也无可厚非只是我们的需求决定了我们不能使用这种方法因此我们称其为求值失败
我们来看它的实现过程由于没有第二个参数所以程序使用默认的当前时间 第一个参数传入的是 month字符串这个字符串所对应的re文件中的正则为
代码如下:
reltextunit = ((
sec
|
second
|
min
|
minute
|
hour
|
day
|
fortnight
|
forthnight
|
month
|
year
)
s
?) |
weeks
| daytext;
relnumber = ([+
]*[ /t]*[
]+);
relative = relnumber space? (reltextunit |
week
);
最终relative会对应一系列操作程序会识别出前面的 和后面的month字符串month对应一种操作类型TIMELIB_MONTH 在此之后根据识别出来的数字和操作类型执行操作如下代码
代码如下:
case TIMELIB_MONTH: s
>time
>relative
m += amount * relunit
>multiplier; break;
如上代码则是直接记录月份的相对值减一 但是对于类似于月号这样的情况月没有号程序会自动将日期计算到下一个月