php--常用的时间处理函数
天地四方曰宇,往古来今曰宙
时间是世界的重要组成部分,不论花开花落,还是云卷云舒都有它的影子。
但它源起何处?又将去向何方?没人知道答案,也不需要答案,我们需要的只是一个相对的起点来标识时间,现今世界普遍采用公元纪年法来表示。
公元纪年法以耶稣诞生日记为公元1年(没有公元0年),中国处于汉平帝刘衎(不会读。。。)登基第二年即元始元年。
关于时间的另一个概念是unix时间戳,是从1970年1月1日开始所经过的秒数,不考虑闰秒,什么是闰秒参考这里。
下面就来说说php中时间的处理方法,以获取当前时间为例
<?php
date_default_timezone_set('Asia/Shanghai');
echo "now is ".date("Y-m-d H:i:s",time())."\n";
echo "now is ".date("Y-m-d H:i:s",strtotime("now"))."\n";
$date = new DateTime();
echo "now is ".$date->format("Y-m-d H:i:s")."\n";
?>
时区设置
date_default_timezone_set用于设置时区,优先级别高于php.ini中设置的date.timezone属性,可设置的时区列表见这里,与之对应的是date_default_timezone_get获取由set函数设置的时区。
<?php
date_default_timezone_set('Asia/Shanghai');
$date_set = date_default_timezone_get();
//如果与配置文件中的时区设置相同则设置为美国时区
if($date_set == ini_get("date.timezone")){
date_default_timezone_set('America/Los_Angeles');
}
echo date("Y-m-d H:i:s")."\n";
?>
获取UNIX时间戳
常用的方法有三种:time(),microtime(),strotime("now")
<?php
error_reporting(E_ALL ^E_STRICT);
echo "time is ".time()."\n";
echo "strotime is ".strtotime("now")."\n";
echo "mktime is ".mktime()."\n";
echo "microtime is ".microtime()."\n";
//参数设置为true返回浮点数表示的时间戳
echo "in float microtime is ".microtime(true)."\n";
?>
mktime
mktime函数的参数全是关键字参数,关键字参数大家懂的可以从右到左省略,格式为时,分,秒,月,日,年
<?php //年默认2014
echo "mktime(10,10,10,10,12) is ".date("Y-m-d H:i:s",mktime(10,10,10,10,12))."\n"; //这种写法会将2014当作月数,年还是默认的2014年
echo "mktime(10,10,10,10,2014) is ".date("Y-m-d H:i:s",mktime(10,10,10,10,2014))."\n"; echo "mktime(10,10,10,10,32,2014) is ".date("Y-m-d H:i:s",mktime(10,10,10,10,32,2014))."\n";
?>
出现了一些很奇妙的事情,2014年2014月变成了2020年4月,2014年10月32号变成了11月1号,看,mktime自动计算了相差部分。乍看之下感觉很神奇,细想下来又在情理之中,毕竟日期的相互转换是通过unix时间戳进行的,我们可以通过mktime的实现源码管中窥豹一下。
该函数源码位于ext/date/php_date.c 在1500行实现,篇幅所限只贴部分代码,兴趣的朋友可以下下来自己看,地址在这里.
PHPAPI void php_mktime(INTERNAL_FUNCTION_PARAMETERS, int gmt)
{
zend_long hou = 0, min = 0, sec = 0, mon = 0, day = 0, yea = 0, dst = -1;
timelib_time *now;
timelib_tzinfo *tzi = NULL;
zend_long ts, adjust_seconds = 0;
int error; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lllllll", &hou, &min, &sec, &mon, &day, &yea, &dst) == FAILURE) {
RETURN_FALSE;
}
/* Initialize structure with current time */
now = timelib_time_ctor();
if (gmt) {
timelib_unixtime2gmt(now, (timelib_sll) time(NULL));
} else {
tzi = get_timezone_info(TSRMLS_C);
now->tz_info = tzi;
now->zone_type = TIMELIB_ZONETYPE_ID;
timelib_unixtime2local(now, (timelib_sll) time(NULL));
}
/* Fill in the new data */
switch (ZEND_NUM_ARGS()) {
case 7:
/* break intentionally missing */
case 6:
if (yea >= 0 && yea < 70) {
yea += 2000;
} else if (yea >= 70 && yea <= 100) {
yea += 1900;
}
now->y = yea;
/* break intentionally missing again */
case 5:
now->d = day;
/* break missing intentionally here too */
case 4:
now->m = mon;
/* and here */
case 3:
now->s = sec;
/* yup, this break isn't here on purpose too */
case 2:
now->i = min;
/* last intentionally missing break */
case 1:
now->h = hou;
break;
default:
php_error_docref(NULL TSRMLS_CC, E_STRICT, "You should be using the time() function instead");
}
/* Update the timestamp */
if (gmt) {
timelib_update_ts(now, NULL);
} else {
timelib_update_ts(now, tzi);
}
/* Support for the deprecated is_dst parameter */
if (dst != -1) {
php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "The is_dst parameter is deprecated");
if (gmt) {
/* GMT never uses DST */
if (dst == 1) {
adjust_seconds = -3600;
}
} else {
/* Figure out is_dst for current TS */
timelib_time_offset *tmp_offset;
tmp_offset = timelib_get_time_zone_info(now->sse, tzi);
if (dst == 1 && tmp_offset->is_dst == 0) {
adjust_seconds = -3600;
}
if (dst == 0 && tmp_offset->is_dst == 1) {
adjust_seconds = +3600;
}
timelib_time_offset_dtor(tmp_offset);
}
}
/* Clean up and return */
ts = timelib_date_to_int(now, &error);
ts += adjust_seconds;
timelib_time_dtor(now); if (error) {
RETURN_FALSE;
} else {
RETURN_LONG(ts);
}
}
阅读这段代码需知道一个重要的结构体timelib_time,在ext/date/lib/timelib_structs.h中声明
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;
现在来看看mktime,56行的timelib_update_ts函数位于ext/date/lib/tm2unixtime.c文件中,其作用是根据now中的日期信息计算相应的秒数并存入now->sse,来看看
void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi)
{
timelib_sll res = ; 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 = ;
time->have_relative = time->relative.have_weekday_relative = time->relative.have_special_relative = ;
}
8-11行计算相应时间类型的秒数,到这里已可了解mktime自动增减日期的原理,让我们看看do_days是如何实现的。day-1应该不难理解,mktime前面需要传入小时分钟等参数,在处理具体的某一天时默认为当天的0点0分0秒,所以要比实际的天数少一天。do_months,do_years等机制都相同,不再细述。
static timelib_sll do_days(timelib_ull day)
{
return ((day - ) * SECS_PER_DAY);
}
当日期处理完成,我们回到php_date.c,在80行处通过timelib_date_to_int函数将now->sse返回,该函数位于ext/date/lib/timelib.c,具体的代码就不贴了。
strtotime
聊完了mktime,再来看看strtotime,同样先来几个例子
<?php echo "strtotime('+1 day',strtotime('2014/10/19')) is ".date("Y-m-d",strtotime("+1 day",strtotime("2014/10/19")))."\n";
echo "strtotime('-30 day') is ".date("Y-m-d",strtotime("-30 day"))."\n";
echo "strtotime('+1 week') is ".date("Y-m-d",strtotime("+1 week"))."\n";
echo "strtotime('last Monday) is ".date("Y-m-d",strtotime("last Monday"))."\n";
?>
strtotime相较mktime要复杂很多,在于其对英文文本的解析过程,这里用到了词法解析工具re2c,原始文件位于ext/date/lib/parse_date.re,同目录下还有个parse_date.c是编译后的文件,parse_date.c太大,我们分析parse_date.re就可以了,以strtotime("+1 day")为例,下面是php_date.c中的实现代码
PHP_FUNCTION(strtotime)
{
char *times, *initial_ts;
size_t time_len;
int error1, error2;
struct timelib_error_container *error;
zend_long preset_ts = , ts; timelib_time *t, *now;
timelib_tzinfo *tzi; tzi = get_timezone_info(TSRMLS_C); if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sl", ×, &time_len, &preset_ts) != FAILURE) {
/* We have an initial timestamp */
now = timelib_time_ctor(); initial_ts = emalloc();
snprintf(initial_ts, , "@" ZEND_LONG_FMT " UTC", preset_ts);
t = timelib_strtotime(initial_ts, strlen(initial_ts), NULL, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper); /* we ignore the error here, as this should never fail */
timelib_update_ts(t, tzi);
now->tz_info = tzi;
now->zone_type = TIMELIB_ZONETYPE_ID;
timelib_unixtime2local(now, t->sse);
timelib_time_dtor(t);
efree(initial_ts);
} else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", ×, &time_len, &preset_ts) != FAILURE) {
/* We have no initial timestamp */
now = timelib_time_ctor();
now->tz_info = tzi;
now->zone_type = TIMELIB_ZONETYPE_ID;
timelib_unixtime2local(now, (timelib_sll) time(NULL));
} else {
RETURN_FALSE;
} if (!time_len) {
timelib_time_dtor(now);
RETURN_FALSE;
} t = timelib_strtotime(times, time_len, &error, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper);
error1 = error->error_count;
timelib_error_container_dtor(error);
timelib_fill_holes(t, now, TIMELIB_NO_CLONE);
timelib_update_ts(t, tzi);
ts = timelib_date_to_int(t, &error2); timelib_time_dtor(now);
timelib_time_dtor(t); if (error1 || error2) {
RETURN_FALSE;
} else {
RETURN_LONG(ts);
}
}
if用来解析参数,分为有第二个参数和没有第二个参数的情况,这里略过不提,主要关注timelib_strtotime函数,它在parse_date.re中定义,返回解析后的timelib_time结构体。而timelib_strtotime又会调用parse_date.re中的scan方法来解析字符串,这里有几个重要的正则表达式
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; relnumber = ([+-]*[ \t]*[-]+);
relative = relnumber space? (reltextunit | 'week' );
relativetext = (reltextnumber|reltexttext) space reltextunit;
relativetextweek = reltexttext space 'week';
"+1 day"会被解析为relative,并进行relative的相关操作
relative
{
timelib_ull i;
DEBUG_OUTPUT("relative");
TIMELIB_INIT;
TIMELIB_HAVE_RELATIVE(); while(*ptr) {
i = timelib_get_unsigned_nr((char **) &ptr, );
timelib_eat_spaces((char **) &ptr);
timelib_set_relative((char **) &ptr, i, , s);
}
TIMELIB_DEINIT;
return TIMELIB_RELATIVE;
}
timelib_get_unsigned_nr 用来判断是"+"还是"-",参数24用来指定最大的字符串长度,然后调用timelib_get_nr获取后面的具体数字,最后调用timelib_set_relative设置timelib_time 结构体中relative.d=1。
static timelib_ull timelib_get_unsigned_nr(char **ptr, int max_length)
{
timelib_ull dir = ; while (((**ptr < '') || (**ptr > '')) && (**ptr != '+') && (**ptr != '-')) {
if (**ptr == '\0') {
return TIMELIB_UNSET;
}
++*ptr;
} while (**ptr == '+' || **ptr == '-')
{
if (**ptr == '-') {
dir *= -;
}
++*ptr;
}
return dir * timelib_get_nr(ptr, max_length);
}
static void timelib_set_relative(char **ptr, timelib_sll amount, int behavior, Scanner *s)
{
const timelib_relunit* relunit; if (!(relunit = timelib_lookup_relunit(ptr))) {
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_HAVE_WEEKDAY_RELATIVE();
TIMELIB_UNHAVE_TIME();
s->time->relative.d += (amount > ? amount - : amount) * ;
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_set_relative
随后就和mktime一样调用相关处理函数转换为时间戳,有一点需要注意,timelib_set_relative的实现方式是直接在日期或月份上操作,下面这样的代码会出现问题
echo date("Y-m-d",strtotime("-1 month",strtotime("2014/03/31")))."\n";
输出结果为2014-03-03,按照上述流程,2014/03/31会转为2014/02/31处理,等价于mktime(00,00,00,2,31,2014),mktime的处理方式上文已经说过了:P
写一些常用的方法
知道了原理,用起来就很方便了。下面是使用频率较高的时间处理方法,例如获取前几周,前几天,取得本周第一天和最后一天的时间等等,不定期更新,github地址。
<?php /**
* process date
* @author huntstack
* @time 2014-10-18
*/
class Process_Date{ private $date;
private $M,$D,$Y; /**
* set the date for next operator
* @parameter date:time string or timestamp
*/
function __construct($date=""){
if($date == "" || empty($date)){
$this->date = strtotime("now");
}else if(gettype($date) == "string"){
$this->date = strtotime($date);
}else if(gettype($date) == "integer"){
$this->date = $date;
}else{
throw new Exception("paramter must be timestamp or date string or empty for current time");
}
$this->set_varibales();
} public function set_date($date){
$this->date = strtotime($date);
$this->set_varibales();
} private function set_varibales(){
$this->M = date("m",$this->date);
$this->D = date("d",$this->date);
$this->Y = date("Y",$this->date);
} /**
* get the specified weeks
* @parameter $i:numbers of week
* @parameter $flag:0->last,1->next
*/
public function get_week($i=0,$flag=1){
if($flag == 0) return date("YW",strtotime("-$i week",$this->date));
else if($flag == 1) return date("YW",strtotime("+$i week",$this->date));
} /**
* get the specified months
* @parameter $i:numbers of month
* @parameter $flag:0->last,1->next
*/
public function get_month($i=0,$flag=1){
if($flag == 0) return date("Y-m",mktime(0,0,0, $this->M-$i, 1, $this->Y));
else if($flag == 1) return date("Y-m",mktime(0,0,0, $this->M+$i, 1, $this->Y));
} /**
* get the specified days
* @parameter $i:numbers of day
* @parameter $flag:0->last,1->next
*/
public function get_day($i=0,$flag=1){
if($flag == 0) return date("Y-m-d",mktime(0,0,0, $this->M, $this->D-$i, $this->Y));
else if($flag == 1) return date("Y-m-d",mktime(0,0,0, $this->M, $this->D+$i, $this->Y));
} /**
* get the last $count days
* @parameter count:number
*/
public function get_last_days($count){
$return = array();
for($i=1;$i<=$count;$i++){
array_push($return, $this->get_day($i,0));
}
return $return;
} /**
* get the next $count days
* @parameter count:number
*/
public function get_next_days($count){
$return = array();
for($i=1;$i<=$count;$i++){
array_push($return, $this->get_day($i,1));
}
return $return;
} /**
* get the last $count weeks
* @parameter count:number
*/
public function get_last_weeks($count){
$return = array();
for($i=1;$i<=$count;$i++){
array_push($return, $this->get_week($i,0));
}
return $return;
} /**
* get the next $count weeks
* @parameter count:number
*/
public function get_next_weeks($count){
$return = array();
for($i=1;$i<=$count;$i++){
array_push($return, $this->get_week($i,1));
}
return $return;
} /**
* get the last $count months
* @parameter count:number
*/
public function get_last_month($count){
$return = array();
for($i=1;$i<=$count;$i++){
array_push($return, $this->get_month($i,0));
}
return $return;
} /**
* get the next $count months
* @parameter count:number
*/
public function get_next_month($count){
$return = array();
for($i=1;$i<=$count;$i++){
array_push($return, $this->get_month($i,1));
}
return $return;
} /**
* get the first day and the last day of a week
*/
public function get_week_begin_end(){
$return["begin"] = mktime(0,0,0, $this->M, $this->D-date("w",$this->date)+1, $this->Y);
$return["end"] = mktime(23,59,59, $this->M, $this->D-date("w",$this->date)+7, $this->Y);
return $return;
} /**
* get the first day and the last day of a month
*/
public function get_month_begin_end(){
$return["begin"] = strtotime("first day of",$this->date);
$return["end"] = strtotime("last day of",$this->date);
return $return;
}
}
?>
参考资料:
1.http://php.net/manual/zh/book.datetime.php
2.http://www.phppan.com/2011/06/php-strtotime/
php--常用的时间处理函数的更多相关文章
- MYSQL常用的时间日期函数
#时间日期函数 #获取当前日期XXXX-XX-XXSELECT CURRENT_DATE(); SELECT CURDATE();#效果与上一条相同 #获取当前日期与时间XXXX-XX-XX XX:X ...
- mysql 常用的时间日期函数小结
本文主要是总结一些常用的在实际运用中常用的一些mysql时间日期以及转换的函数 1.now() :返回当前日期和时间 select now(); //2018-04-21 09:19:21 2.cu ...
- linux常用的时间获取函数(time,gettimeofday,clock_gettime,_ftime,localtime,strftime )
time()提供了秒级的精确度 1.头文件 <time.h> 2.函数原型 time_t time(time_t * timer) 函数返回从TC1970-1-1 0:0:0开始到现在的秒 ...
- 银弹谷零代码开发V百科|使用技巧:OMG!这些时间日期函数太好用了吧,盘它
银弹谷零代码开发V百科|使用技巧:OMG!这些时间日期函数太好用了吧,盘它 Hello~everybody!小V又来咯!这次小V给大家带来的是零代码开发V平台常用的时间日期函数.小V知道我们平时常常会 ...
- MySQL最常用日期时间函数
日期和时间函数 可能的需求: 当前时间是多少.下个月的今天是星期几.统计截止到当前日期前 3 天的收入总和-- 上述需求就需要使用日期和时间函数来实现: MySQL服务器中的三种时区设置: ①系统时区 ...
- MySQL常用日期时间函数
日期和时间函数: MySQL服务器中的三种时区设置: ①系统时区---保存在系统变量system_time_zone ②服务器时区---保存在全局系统变量global.time_zone ③每个客户端 ...
- Sql 中常用时间处理函数
1.Sql 中常用时间处理函数 GETDATE() 返回当前的日期和时间 DATEPART() 返回日期/时间的单独部分 DATEADD() 返回日期中添加或减去指定的时间间隔 DATEDI ...
- PostgreSQL的时间/日期函数使用
PostgreSQL的常用时间函数使用整理如下: 一.获取系统时间函数 1.1 获取当前完整时间 select now(); david=# select now(); now ----------- ...
- C/C++常用头文件及函数汇总
转自: C/C++常用头文件及函数汇总 C/C++头文件一览 C #include <assert.h> //设定插入点#include <ctype.h> //字符处理#in ...
- 常用sql时间字符转化
这边主要用到2个函数 convert() cast() cast是对数据字符类型的转化,例如: cast(date as datetime) 这样就将date字段转化成为时间类型了 因为常用到 ...
随机推荐
- [USACO1.1.4]坏掉的项链Broken Necklace
P1203 [USACO1.1]坏掉的项链Broken Necklace 标签 搜索/枚举 USACO 难度 普及- 题目描述 你有一条由N个红色的,白色的,或蓝色的珠子组成的项链(3<=N&l ...
- 如何使用Json-lib
数组与List.Collection等都用JSONArray解析 boolean[] boolArray = new boolean[]{true,false,true}; JSONArray jso ...
- Js获取fileupload的绝对路径时总是的到C:\fakepath\+文件名称的 解决方案
解决方法: Internet选项->安全->自定义级别->将文件下载到服务器时包含本地目录路径 启用就可以了.
- ci 多个文件同时上传
// 单个文件请手册,这里多个文件中,参数设置可参考手册 view 视图 <form...> <input type="file" name="user ...
- 压缩代码加速ecshop程序页面加载速度
由于页面有很多图片,页面加载速度有点慢,本来打算减小图片的体积,后来想想这个后期还得测试下,所以暂时不打算使用google的图片优化工具,先把ecshop生成的html代码压缩下吧 压缩前:首页体积为 ...
- jinja2 宏的简单使用总结(macro)
Table of Contents 1. 简介 2. 用法 3. 参数和变量 4. 注意事项 4.1. macro的变量只能为如下三种: 4.2. 和block的关系: 5. 参考文档 1 简介 ji ...
- Python的面向对象1
今天,我们来介绍Python的面向对象编程,其实面向对象并不陌生,在C++ ,Java ,PHP中也有大量使用! 好了,我们来步入正题! 那什么是面向对象编程呢? 1. 面向对象编程是一种程序设计 ...
- python学习_数据处理编程实例(二)
在上一节python学习_数据处理编程实例(二)的基础上数据发生了变化,文件中除了学生的成绩外,新增了学生姓名和出生年月的信息,因此将要成变成:分别根据姓名输出每个学生的无重复的前三个最好成绩和出生年 ...
- 模型 - 视图 - 控制器(MVC)详解
模型视图控制器(MVC)一个相当实用且十分流行的设计模式.作为一位称职码农,你不可能没听说过吧. 不幸的是它难以让人理解. 在本文中,我将给出我认为是MVC的最简单的解释,以及为什么你应该使用它. 什 ...
- IEEE二进制浮点数算术标准(IEEE 754)
整理自IEEE 754 IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用.这个标准定义了表示浮点数的格式(包括负零-0) ...