15.PHP的日期和时间

在Web程序开发时,时间发挥着重要的作用。不仅在数据存储和显示时需要日期和时间的参与,很多功能模块的开发,时间通常都是至关重要的。

1.UNIX时间戳

  • 以32位的整数表示格林尼治标准时间,例如,使用整数11230499325表示当前时间的时间戳。
  • UNIX时间戳是从1970年1月1日零点(UTC/GMT的午夜)开始起到当前时间所经过的秒数。
  • 1970年1月1日零点作为所有日期计算的基础,这个日期通常称为UNIX纪元。

因为UNIX时间戳是一个32位的数字格式,所以特别适用于计算机处理,例如计算两个时间点之间相差的天数。

另外,由于文化和地区的差异,存在不同的时间格式,以及时区的问题。

所以UNIX时间戳也是根据一个时区进行标准化而设计的一种通用格式,并且这种格式可以很容易地转换为任何格式。

也因为UNIX时间戳是一个32位的整数表示的,所以在处理1970年以前或2038年以后的事件时,将会遇到一些问题。

另外,在Windows系统下,由于时间戳不能为负数,如果使用PHP中提供的时间戳函数处理1970年之前的日期,就会发生错误。要使PHP代码具有可移植性,必须记住这一点。

1.1. 将日期和时间转变成UNIX时间戳

mktime()

原型:

mktime ([ int $hour = date("H") [, int $minute = date("i") [, int $second = date("s") [, int $month = date("n") [, int $day = date("j") [, int $year = date("Y") [, int $is_dst = -1 ]]]]]]] )

作用:将日期和时间转为UNIX时间戳

详情:https://www.php.net/manual/zh/function.mktime.php

该函数中所有参数都是可选的,如果参数为空,默认将当前时间转变成UNIX时间戳。

这样,和直接调用time()函数获取当前的UNIX时间戳功能相同。

参数也可以从右向左省略,任何省略的参数会被设置成本地日期和时间的当前值。

如果只想转变日期,对具体的时间不在乎,可以将前三个转变时间的参数都设置为0。

mktime()函数对于日期运算和验证非常有用,它可以自动校正越界的输入。如下所示:

//日期超过31天,计算后输出Jan-05-2008
echo date("M-d-Y", mktime(0, 0, 0, 12, 36, 2007))."\n";  
//月份超过12月,计算后输出Feb-01-2009
echo date("M-d-Y", mktime(0, 0, 0, 14, 1, 2008))."\n";   
//没有问题的转变,输出结果 Jan-01-2009
echo date("M-d-Y", mktime(0, 0, 0, 1, 1, 2009))."\n"; 
//会将99年转变为1999年, Jan-01-1999
echo date("M-d-Y", mktime(0, 0, 0, 1, 1, 99))."\n";      

strtotime()

原型:

int strtotime( string $time[, int $now = time()] )

作用: 将任何英文文本的日期时间描述直接解析为UNIX时间戳 。

1.2.日期的计算

在PHP中,计算两个日期之间相隔的长度,最简单的方法就是通过计算两个UNIX时间戳之差来获得。例如,在PHP脚本中接收来自HTML表单用户提交的出生日期,计算这个用户的年龄。代码如下所示:

15.PHP的日期和时间_第1张图片
image-20191207221437140.png

在以上脚本中,调用mktime()函数将从用户出生日期转变为UNIX时间戳,再调用time()函数获取当前时间的UNIX时间戳。因为这个日期的格式都是使用整数表示的,所以可以将它们相减。又将计算后获取的UNIX时间戳除以一年的秒数,将UNIX时间戳转变为以年度量的单位。

2.在PHP中获取日期和时间

PHP提供了多种获取日期和时间的函数 。

2.1. 调用getdate()函数取得日期/时间信息

2.2. 日期和时间格式化输出date()

当日期和时间需要保存或计算时,应该使用UNIX时间戳作为标准格式,这可以作为一条重要的规则。但UNIX时间戳的格式可读性比较差,所以要把时间戳格式化为可读性更好的日期和时间,或格式化为其他软件需要的格式。在PHP中可以调用date()函数格式化一个本地日期和时间 。

3.修改PHP的默认时区

每个地区都有自己的本地时间,在网上及无线电通信中,时间的转换问题显得格外突出。

整个地球分为24个时区,每个时区都有自己的本地时间。

在国际无线电或网络通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(Universal Time Coordinated,UTC),是由世界时间标准设定的全球标准时间。UTC原先也被称为格林尼治标准时间(Greenwich Mean Time,GMT),都与英国伦敦的本地时间相同。

PHP默认的时区设置是UTC时间,而北京正好位于时区的东八区,领先UTC 8个小时。所以在使用PHP中像time()等获取当前时间的函数时,得到的时间总是不对,其表现是和北京时间相差8个小时。如果希望正确地显示北京时间,就需要修改默认的时区设置。可以通过以下两种方式完成。

  • 如果使用的是独立的服务器,有权限修改配置文件,设置时区就可以通过修改php.ini中的date.timezone属性完成。我们可以将这个属性的值设置为Asia/Shang、Asia/Chongqing、Etc/GMT-8或PRC等中的一个,再在PHP脚本中获取的当前时间就是北京时间。
  • 如果使用的是共享服务器,没有权限修改配置文件php.ini,并且PHP版本又在5.1.0以上,也可以在输出时间之前调用date_default_timezone_set()函数设置时区。该函数需要提供一个时区标识符作为参数,和配置文件中date.timezone属性的值相同。

4. 使用微秒计算PHP脚本执行时间

在PHP中,大多数的时间格式都是以UNIX时间戳表示的,而UNIX时间戳是以s(秒)为最小的计量时间的单位。这对某些应用程序来说不够精确,所以可以调用microtime()函数返回当前UNIX时间戳和微秒数。该函数的原型如下:

mixed microtime([ bool $get_as_float] )

可以为该函数提供一个可选的布尔型参数,如果在调用时不提供这个参数,本函数以“msec sec”的格式返回一个字符串。其中sec是自UNIX纪元起到现在的秒数,而msec是微秒部分,字符串的两部分都是以秒为单位返回的。

如果给出了get_as_float参数并且其值等价于TRUE,则microtime()函数将返回一个浮点数。

在小数点前面还是以时间戳的格式表示,而小数点后面则表示微秒的值。

但要注意参数get_as_float是在PHP 5中新加的,所以在PHP 5以前的版本中,不能直接使用该参数请求一个浮点数。

在下面的例子中通过两次调用microtime()函数,计算运行PHP脚本所需的时间。代码如下所示:

class Timer {                              //声明一个计算脚本运行时间的类
    private $startTime;                //保存脚本开始执行时的时间(以微秒的形式保存)
    private $stopTime;                 //保存脚本结束执行时的时间(以微秒的形式保存)
    
    function __construct(){            //构造方法,在创建对象时初使化成员属性
        $this->startTime=0;        //初使化成员属性startTime的值为0
        $this->stopTime=0;         //初使化成员属性stopTime的值为0
    }
        
    function start(){                           //在脚本开始处调用获取脚本开始时间的微秒值
        $this->startTime = microtime(true);     //将获取的时间赋给成员属性$startTime
    }

    function stop(){                            //在脚本结束处调用获取脚本结束时间的微秒值
        $this->stopTime= microtime(true);       //将获取的时间赋给成员属性$stopTime
    }
    function spent(){                           //返回同一脚本中两次获取时间的差值
        return round(($this->stopTime- $this->startTime) , 4);  //计算后以4舍5入保留4位返回
    }
}

$timer = new Timer();                //创建Timer类的对象
$timer->start();                     //在脚本文件开始执行时调用这个方法
usleep(1000);                        //脚本的主体内容,这里以休眠一毫秒为例
$timer->stop();                      //在脚本文件结尾处调用这个方法
echo "执行该脚本用时".$timer->spent()."秒";     //输出页面执行时运行的时间

5.日历类

说到对日期和时间的处理,就一定要介绍一下日历程序的编写。但一提起编写日历,大多数读者都会认为日历的作用只是为了在页面上显示当前的日期,其实日历在我们的开发中有着更重要的作用。

例如,我们开发一个“记事本”就需要通过日历设定日期,在一些系统中需要按日期去安排任务也需要日历,等等。

本例涉及的日期和时间函数并不是很多,都是前面介绍的内容,主要是通过一个日历类的编写,巩固一下前面介绍过的面向对象的语法知识,以及时间函数应用,最主要的是可以提升初学者的思维逻辑和程序设计能力。

将日历类Calendar声明在文件calendar.class.php中,代码如下所示:

/*
    file:calendar.class.php 日历类原文件
    声明一个日历类,名称为Calendar,用来显示一个可以设置日期的日历
*/
class Calendar {
    private $year;                    //当前的年
    private $month;                   //当前的月
    private $start_weekday;           //当月的第一天对应的是周几,作为当月开始遍历日期的开始
    private $days;                    //当前月一总天数

    /* 构造方法,用来初使化一些日期属性  */
    function __construct(){
        /* 如果用户没有设置年份数,则使用当前系统时间的年份 */
        $this->year = isset($_GET["year"]) ? $_GET["year"] : date("Y");
        /* 如果用户没有设置月份数,则使用当前系统时间的用份 */
        $this->month = isset($_GET["month"]) ? $_GET["month"] : date("m");
        /* 通过具体的年份和月份,利用date()函数的w参数获取当月第一天对应的是周几 */
        $this->start_weekday = date("w", mktime(0, 0, 0, $this->month, 1, $this->year));
        /* 通过具体的年份和月份,利用date()函数的t参数获取当月的天数 */
        $this->days = date("t", mktime(0, 0, 0, $this->month, 1, $this->year));
    }
    
    /* 魔术方法用于打印整个日历 */
    function __toString(){
        $out .= ''; //日历以表格形式打印
        $out .= $this->chageDate();       //调用内部私有方法用于用户自己设置日期
        $out .= $this->weeksList();       //调用内部私有方法打印“周”列表
        $out .= $this->daysList();        //调用内部私有方法打印“日”列表
        $out .= '
'; //表格结束 return $out; //返回整个日历输出需要的全部字符串 } /* 内部调用的私有方法,用于输出周列表 */ private function weeksList(){ $week = array('日','一','二','三','四','五','六'); $out .= ''; for($i = 0; $i < count($week); $i++) $out .= ''.$week[$i].''; //第一行以表格形式输出周列表 $out .= ''; return $out; //返回周列表字符串 } /* 内部调用的私有方法,用于输出日列表 */ private function daysList(){ $out .= ''; /* 输出空格(当前一月第一天前面要空出来) */ for($j = 0; $j < $this->start_weekday; $j++) $out .= ' '; /* 将当月的所有日期循环遍历出来,如果是当前日期,为其设置深色背景 */ for($k = 1; $k <= $this->days; $k++){ $j++; if($k == date('d')) $out .= ''.$k.''; else $out .= ''.$k.''; if($j%7 == 0) //每输出7个日期,就换一行 $out .= ''; //输出行结束和下一行开始 } while($j%7 !== 0){ //遍历完日期后,将后面用空格补齐 $out .= ' '; //使用空格去补 $j++; } $out .= ''; return $out; //返回当月日期列表 } /* 内部调用的私有方法,用于处理当前年份的上一年需要的数据 */ private function prevYear($year, $month){ $year = $year-1; //上一年是当前年减1 if($year < 1970) //如果设置的年份小于1970年 $year = 1970; //年份设置最小值是1970年 return "year={$year}&month={$month}"; //返回最终的年份和月份设置参数 } /* 内部调用的私有方法,用于处理当前月份的上一月份的数据 */ private function prevMonth($year, $month){ if($month == 1) { //如果月份已经是1月 $year = $year -1; //则上一个月份,就是上一年的最后一月 if($year < 1970) //和前面一样,上一年如果是最1970年 $year = 1970; //最小年数不能小于1970年 $month=12; //如果月是1月,上一月就是上一年的最后一月 }else{ $month--; //上一月就是当前月减1 } return "year={$year}&month={$month}"; //返回最终的年份和月份设置参数 } /* 内部调用的私有方法,用于处理当前年份的下一年份的数据 */ private function nextYear($year, $month){ $year = $year + 1; //下一年是当前年加1 if($year > 2038) //如果设置的年份大于2038年 $year = 2038; //最大年份不能超过2038年 return "year={$year}&month={$month}"; //返回最终的年份和月份设置参数 } /* 内部调用的私有方法,用于处理当前月份的下一个月份的数据 */ private function nextMonth($year, $month){ if($month == 12){ //如果已经是当年的最后一个月 $year++; //下一个月就是下一年的第一个月,让年份加1年 if($year > 2038) //如果设置的年份大于2038年 $year = 2038; //最大年份不能超过2038年 $month = 1; //设置月份为下一年的第1月 }else{ $month++; //其它月份的下一个月都是当前月份加1即可 } return "year={$year}&month={$month}"; //返回最终的年份和月份设置参数 } //内部调用的私有方法,用于用户操作去调整年份和月份的设置 private function chageDate($url="index.php"){ $out .= ''; $out .= ''.'<<'.''; $out .= ''.'<'.''; $out .= ''; $out .= '
'; $out .= ''; $out .= ''; $out .= '
'; $out .= ''; $out .= ''.'>>'.''; $out .= ''.'>'.''; $out .= ''; return $out; //返回调整日期的表单 } }

在主程序中还需要先设置一下日历输出的样式,代码如下所示:


    
        《细说PHP》日历示例
        
    
    
        
    

6.小结

本章必须掌握的知识点

  • UNIX时间戳。
  • 将其他格式的日期转成UNIX时间戳的格式。
  • 基于UNIX时间戳的日期计算。
  • 获取并格式化输出日期。
  • 修改PHP的默认时间。
  • 微秒的使用。

本章需要了解的内容

  • 日历程序编写。
  • 本章没有介绍到的其他和日期及时间有关的函数。
  • 以同样的学习方法去扩展PHP的一些相似函数库学习,例如数学函数库。

本章的学习建议

多通过实例编写,熟练掌握日期和时间函数的应用,并可以灵活地在项目中使用。

你可能感兴趣的:(15.PHP的日期和时间)