跟兄弟连学PHP

15.PHP的日期和时间

2019-12-07  本文已影响0人  乐乐凡凡

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

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表单用户提交的出生日期,计算这个用户的年龄。代码如下所示:

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个小时。如果希望正确地显示北京时间,就需要修改默认的时区设置。可以通过以下两种方式完成。

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 "执行该脚本用时<b>".$timer->spent()."</b>秒";     //输出页面执行时运行的时间

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 .= '<table align="center">'; //日历以表格形式打印
        $out .= $this->chageDate();       //调用内部私有方法用于用户自己设置日期
        $out .= $this->weeksList();       //调用内部私有方法打印“周”列表
        $out .= $this->daysList();        //调用内部私有方法打印“日”列表
        $out .= '</table>';               //表格结束
        return $out;                      //返回整个日历输出需要的全部字符串
    }
    
    /* 内部调用的私有方法,用于输出周列表 */
    private function weeksList(){
        $week = array('日','一','二','三','四','五','六');

        $out .= '<tr>';
        for($i = 0; $i < count($week); $i++)
            $out .= '<th class="fontb">'.$week[$i].'</th>'; //第一行以表格<th>形式输出周列表

        $out .= '</tr>';
        return $out;                      //返回周列表字符串  
    }
    
    /* 内部调用的私有方法,用于输出日列表 */
    private function daysList(){
        $out .= '<tr>';
        /* 输出空格(当前一月第一天前面要空出来) */
        for($j = 0; $j < $this->start_weekday; $j++)
            $out .= '<td>&nbsp;</td>';

        /* 将当月的所有日期循环遍历出来,如果是当前日期,为其设置深色背景 */
        for($k = 1; $k <= $this->days; $k++){
            $j++;
            if($k == date('d'))
                $out .= '<td class="fontb">'.$k.'</td>';
            else
                $out .= '<td>'.$k.'</td>';
            
            if($j%7 == 0)                        //每输出7个日期,就换一行
                $out .= '</tr><tr>';             //输出行结束和下一行开始
        }

        while($j%7 !== 0){                       //遍历完日期后,将后面用空格补齐 
            $out .= '<td>&nbsp;</td>';           //使用空格去补
            $j++;
        }

        $out .= '</tr>';
        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 .= '<tr>';
        $out .= '<td><a href="'.$url.'?'.$this->prevYear($this->year, $this->month).'">'.'<<'.'</a></td>';
        $out .= '<td><a href="'.$url.'?'.$this->prevMonth($this->year, $this->month).'">'.'<'.'</a></td>';
        
        $out .= '<td colspan="3">';
        $out .= '<form>';
        $out .= '<select name="year" onchange="window.location=\''.$url.'?year=\'+this.options[selectedIndex].value+\'&month='.$this->month.'\'">';
        for($sy=1970; $sy <= 2038; $sy++){
            $selected = ($sy==$this->year) ? "selected" : "";
            $out .= '<option '.$selected.' value="'.$sy.'">'.$sy.'</option>';
        }
        $out .= '</select>';
        $out .= '<select name="month"  onchange="window.location=\''.$url.'?year='.$this->year.'&month=\'+this.options[selectedIndex].value">';
        for($sm=1; $sm<=12; $sm++){
            $selected1 = ($sm==$this->month) ? "selected" : "";
            $out .= '<option '.$selected1.' value="'.$sm.'">'.$sm.'</option>';
        }
        $out .= '</select>';
        $out .= '</form>';  
        $out .= '</td>';

        $out .= '<td><a href="'.$url.'?'.$this->nextYear($this->year, $this->month).'">'.'>>'.'</a></td>';
        $out .= '<td><a href="'.$url.'?'.$this->nextMonth($this->year, $this->month).'">'.'>'.'</a></td>';
        $out .= '</tr>';
        return $out;                              //返回调整日期的表单
    }
}

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

<html>
    <head>
        <title>《细说PHP》日历示例</title>
        <style>
            table { border:1px solid #050;}            /*给表格加一个外边框*/
            .fontb { color:white; background:blue;}    /*设置周列表的背景和字体颜色*/
            th { width:30px;}                          /*设置单元格子的宽度*/
            td,th { height:30px;text-align:center;}    /*设置单元高度,和字段显示位置*/
            form { margin:0px;padding:0px; }           /*清除表单原有的样式*/
        </style>
    </head>
    <body>
        <?php
            require "calendar.class.php";    //加载日历类
            echo  new Calendar;              //直接输出日历对象,自动调用魔术方法__toString()打印日历
        ?>
    </body>
</html>

6.小结

本章必须掌握的知识点

本章需要了解的内容

本章的学习建议

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

上一篇 下一篇

猜你喜欢

热点阅读