PHP中的日期相关函数(一)

日期相关的操作函数是我们在日常的工作开发中最常接触到的功能。当然,大部分同学可能最多用到的就是 date() 、 time() 这两个函数,我们今天先不讲这两个函数,或许后面的文章也不太会讲它们,毕竟太常用了。本身在对手册文档的学习中,就是要发掘有意思的或者没有接触过的一些功能函数,所以我们今天的学习的函数可能是大家都没怎么用过的,甚至可能很多大家都没见过的。

时区类相关函数

首先就是时区类的一个对象。它可以帮助我们获取一些当前时区相关的信息。

$timezone = new DateTimeZone('Asia/Shanghai');
var_dump($timezone);
// object(DateTimeZone)#1 (2) {
//     ["timezone_type"]=>
//     int(3)
//     ["timezone"]=>
//     string(13) "Asia/Shanghai"
//   }

在实例化这个 DateTimeZone 时区类时,需要传递一个时区参数。这里我们给的就是通用的中国时区的设置,虽说我们的国际标准时区是东八区的北京时间,但在 PHP 中的时区格式中,我们的时区是以上海为命名的。

这个时区类可以直接获得当前指定时区的定位信息,比如 Asia/Shanghai 的定位信息就是直接定位到上海市的。

// 时区相关的定位信息
var_dump($timezone->getLocation());
// array(4) {
//     ["country_code"]=>
//     string(2) "CN"
//     ["latitude"]=>
//     float(31.23333)
//     ["longitude"]=>
//     float(121.46666)
//     ["comments"]=>
//     string(12) "Beijing Time"
//   }

通过 getLocation() 就可以获取到时区类的定位信息,经纬度的查询结果就是上海市中心,comments 字段也明确地指出了当前时区是北京时间。

// 时区名称
var_dump($timezone->getName());
// string(13) "Asia/Shanghai"

// 相对于 GMT 的时差
var_dump($timezone->getOffset(new DateTime('now', $timezone)));
// int(28800)

// 所有时区转换信息
var_dump($timezone->getTransitions(time()));
// array(1) {
//     [0]=>
//     array(5) {
//       ["ts"]=>
//       int(1601168813)
//       ["time"]=>
//       string(24) "2020-09-27T01:06:53+0000"
//       ["offset"]=>
//       int(28800)
//       ["isdst"]=>
//       bool(false)
//       ["abbr"]=>
//       string(3) "CST"
//     }
//   }

getName() 方法获取的是当前时区的名称,这个就不用多说了。getOffset() 则是获取到与国际格林尼治时间(GMT)的差值,也就是与子午线的时间间隔,这里返回的是秒,转换成小时后正好就是 8 小时。getTransitions() 函数返回的是所有时区转换的时间,我测试的时间是早上,返回的 time 字段内容是格林尼治时间,offset 字段返回的也是与GMT时间的差值。GMT时间与UTC时间是一致的,我们在日常的学习和工作中如果接触到了这两个名词可以将它们看做是相同的概念。

UTC时间的标准叫法是协调世界时间,基于国际原子时间,全世界的国家的标准时间都是以它为标准进行调校的,而GMT的本意是定位为本初子午线的平太阳时,UTC也是以这条经线为基准进行时区划分的。不过,按照严格的标准来说,它们并不是完全相等的,具体的内容大家可以自行查阅,但对于我们日常开发来说,完全可以将它们视为等同的东西。

// 包含 dst (夏令时),时差和时区信息的关联数组
var_dump(DateTimeZone::listAbbreviations());
// array(144) {
//     ["acdt"]=>
//     array(6) {
//       [0]=>
//       array(3) {
//         ["dst"]=>
//         bool(true)
//         ["offset"]=>
//         int(37800)
//         ["timezone_id"]=>
//         string(18) "Australia/Adelaide"
//       }
//       [1]=>
//       array(3) {
//         ["dst"]=>
//         bool(true)
//         ["offset"]=>
//         int(37800)
//         ["timezone_id"]=>
//         string(21) "Australia/Broken_Hill"
//       }
//     ……
//     ……

// 包含了所有时区标示符的索引数组
var_dump(DateTimeZone::listIdentifiers());
// array(426) {
//     [0]=>
//     string(14) "Africa/Abidjan"
//     [1]=>
//     string(12) "Africa/Accra"
//     [2]=>
//     string(18) "Africa/Addis_Ababa"
//     [3]=>
//     string(14) "Africa/Algiers"
//     ……
//     ……

listAbbreviations() 静态方法返回的是 夏令时 相关的时差和时区信息。夏令时 和 冬令时 也是西方国家的一种生活标准,我们接触的不多,这里就不做讲解了,对于做跨境项目或者欧美外包的同学应该不会陌生。listIdentifiers() 方法返回的是包含了所有时区标示符的索引数组,这里可以看到所有的支持的时区信息。

日期间隔操作

对时日期时间的间隔操作,或许大家多少都做过一点,比如 DateTime 对象的那个 diff() 方法。

$today = new DateTime('2020-09-27');
$beforeYestoday = new DateTime("2020-09-25");
var_dump($today->diff($beforeYestoday));
// object(DateInterval)#5 (16) {
//     ["y"]=>
//     int(0)
//     ["m"]=>
//     int(0)
//     ["d"]=>
//     int(2)
//     ["h"]=>
//     int(0)
//     ["i"]=>
//     int(0)
//     ["s"]=>
//     int(0)
//     ["f"]=>
//     float(0)
//     ["weekday"]=>
//     int(0)
//     ["weekday_behavior"]=>
//     int(0)
//     ["first_last_day_of"]=>
//     int(0)
//     ["invert"]=>
//     int(1)
//     ["days"]=>
//     int(2)
//     ["special_type"]=>
//     int(0)
//     ["special_amount"]=>
//     int(0)
//     ["have_weekday_relative"]=>
//     int(0)
//     ["have_special_relative"]=>
//     int(0)
//   }

从打印的结果可以看出,diff() 对象返回的是一个 DateInterval 对象。这个就是我们这节的主角了,关于它打印出来的这些属性内容就不多解释了,字段名已经非常直观了,值就是具体的差值。

$interval = new DateInterval("P2D");
var_dump($interval);
// object(DateInterval)#2 (16) {
//     ["y"]=>
//     int(0)
//     ["m"]=>
//     int(0)
//     ["d"]=>
//     int(2)
//     ["h"]=>
//     int(0)
//     ["i"]=>
//     int(0)
//     ["s"]=>
//     int(0)
//     ["f"]=>
//     float(0)
//     ["weekday"]=>
//     int(0)
//     ["weekday_behavior"]=>
//     int(0)
//     ["first_last_day_of"]=>
//     int(0)
//     ["invert"]=>
//     int(0)
//     ["days"]=>
//     bool(false)
//     ["special_type"]=>
//     int(0)
//     ["special_amount"]=>
//     int(0)
//     ["have_weekday_relative"]=>
//     int(0)
//     ["have_special_relative"]=>
//     int(0)
//   }

看到没有?打印出来的内容和上面用 diff() 方法返回的对象的内容是一致的,但是它的构造函数的参数很奇怪。没错,当我们自己去实例化一个 DateInterval 对象时,需要为它定义它的间隔信息,这个间隔信息就是我们通过构造函数的参数传递进去的。P2D 的意思就是间隔 2 天,首先必须以一个 P 为开头,然后可以有 Y、M、D 这些日期内容,如果需要时间内容的话,需要一个 T 然后再跟上 H、M、S 这些内容。比如 P2Y4DT6H8M 表示的就是 2年4天6小时8分钟 的时间间隔。具体的规则大家还是去看文档中的说明:https://www.php.net/manual/zh/dateinterval.construct.php

$interval = new DateInterval("P2Y4DT6H8M");
var_dump($interval);
// object(DateInterval)#5 (16) {
//     ["y"]=>
//     int(2)
//     ["m"]=>
//     int(0)
//     ["d"]=>
//     int(4)
//     ["h"]=>
//     int(6)
//     ["i"]=>
//     int(8)
//     ["s"]=>
//     int(0)
//     ["f"]=>
//     float(0)
//     ["weekday"]=>
//     int(0)
//     ["weekday_behavior"]=>
//     int(0)
//     ["first_last_day_of"]=>
//     int(0)
//     ["invert"]=>
//     int(0)
//     ["days"]=>
//     bool(false)
//     ["special_type"]=>
//     int(0)
//     ["special_amount"]=>
//     int(0)
//     ["have_weekday_relative"]=>
//     int(0)
//     ["have_special_relative"]=>
//     int(0)
//   }

我们还可以通过字段串形式的日期数据返回间隔对象,比如:

// 从日期语句创建时间间隔
var_dump(DateInterval::createFromDateString('2 days'));
// object(DateInterval)#3 (16) {
//     ["y"]=>
//     int(0)
//     ["m"]=>
//     int(0)
//     ["d"]=>
//     int(2)
//     ["h"]=>
//     int(0)
//     ["i"]=>
//     int(0)
//     ["s"]=>
//     int(0)
//     ["f"]=>
//     float(0)
//     ["weekday"]=>
//     int(0)
//     ["weekday_behavior"]=>
//     int(0)
//     ["first_last_day_of"]=>
//     int(0)
//     ["invert"]=>
//     int(0)
//     ["days"]=>
//     bool(false)
//     ["special_type"]=>
//     int(0)
//     ["special_amount"]=>
//     int(0)
//     ["have_weekday_relative"]=>
//     int(0)
//     ["have_special_relative"]=>
//     int(0)
//   }

此外,在获得对象后进行输出的时候,DateInterval 对象也为我们提供了一个 format() 方法,可以像 printf() 函数一样来格式化地输出日期信息,而且这里用的格式符还是日期的格式符。

var_dump($interval->format('%y %d %h %i'));
// string(7) "2 4 6 8"

输出的内容其实就是属性中对应的那些日期和时间差值。

时间周期相关函数

说完时间间隔了,我们再来看看时间周期。时间周期是个什么概念呢?就比如说我们要每三天间隔一次地获取日期,这时就可以用时间周期相关的类来进行处理。

$start = new DateTime('2020-09-01');
$interval = new DateInterval('P7D');
$end = new DateTime('2020-09-30');
$daterange = new DatePeriod($start, $interval ,$end);
var_dump($daterange);
// object(DatePeriod)#7 (6) {
//     ["start"]=>
//     object(DateTime)#8 (3) {
//       ["date"]=>
//       string(26) "2020-09-01 00:00:00.000000"
//       ["timezone_type"]=>
//       int(3)
//       ["timezone"]=>
//       string(13) "Asia/Shanghai"
//     }
//     ["current"]=>
//     NULL
//     ["end"]=>
//     object(DateTime)#9 (3) {
//       ["date"]=>
//       string(26) "2020-09-30 00:00:00.000000"
//       ["timezone_type"]=>
//       int(3)
//       ["timezone"]=>
//       string(13) "Asia/Shanghai"
//     }
//     ["interval"]=>
//     object(DateInterval)#10 (16) {
//       ["y"]=>
//       int(0)
//       ["m"]=>
//       int(0)
//       ["d"]=>
//       int(7)
//       ["h"]=>
//       int(0)
//       ["i"]=>
//       int(0)
//       ["s"]=>
//       int(0)
//       ["f"]=>
//       float(0)
//       ["weekday"]=>
//       int(0)
//       ["weekday_behavior"]=>
//       int(0)
//       ["first_last_day_of"]=>
//       int(0)
//       ["invert"]=>
//       int(0)
//       ["days"]=>
//       bool(false)
//       ["special_type"]=>
//       int(0)
//       ["special_amount"]=>
//       int(0)
//       ["have_weekday_relative"]=>
//       int(0)
//       ["have_special_relative"]=>
//       int(0)
//     }
//     ["recurrences"]=>
//     int(1)
//     ["include_start_date"]=>
//     bool(true)
//   }


foreach($daterange as $date){
    echo $date->format("Ymd"), PHP_EOL;
}
// 20200901
// 20200908
// 20200915
// 20200922
// 20200929

首先设定了开始时间和结束时间以及一个时间间隔对象,然后用它们做为参数来生成一个 DatePeriod 时间周期对象。它是一个实现了迭代器的对象,所以我们可以直接遍历它,结果就是以 P7D ,也就是 7 天为间隔的一组日期数据。

var_dump($daterange->getDateInterval());
// object(DateInterval)#11 (16) {
//     ["y"]=>
//     int(0)
//     ["m"]=>
//     int(0)
//     ["d"]=>
//     int(7)
//     ["h"]=>
//     int(0)
//     ["i"]=>
//     int(0)
//     ["s"]=>
//     int(0)
//     ["f"]=>
//     float(0)
//     ["weekday"]=>
//     int(0)
//     ["weekday_behavior"]=>
//     int(0)
//     ["first_last_day_of"]=>
//     int(0)
//     ["invert"]=>
//     int(0)
//     ["days"]=>
//     bool(false)
//     ["special_type"]=>
//     int(0)
//     ["special_amount"]=>
//     int(0)
//     ["have_weekday_relative"]=>
//     int(0)
//     ["have_special_relative"]=>
//     int(0)
//   }

var_dump($daterange->getStartDate());
// object(DateTime)#11 (3) {
//     ["date"]=>
//     string(26) "2020-09-01 00:00:00.000000"
//     ["timezone_type"]=>
//     int(3)
//     ["timezone"]=>
//     string(13) "Asia/Shanghai"
//   }
var_dump($daterange->getEndDate());
// object(DateTime)#11 (3) {
//     ["date"]=>
//     string(26) "2020-09-30 00:00:00.000000"
//     ["timezone_type"]=>
//     int(3)
//     ["timezone"]=>
//     string(13) "Asia/Shanghai"
//   }

它的这一堆方法其实返回的就是我们定义的那些构造参数信息。另外,它还可以指定从开始日期往后按照时间间隔返回几条信息。

$period = new DatePeriod($start, $interval, 4);
foreach($period as $date){
    echo $date->format("Ymd"), PHP_EOL;
}
// 20200901
// 20200908
// 20200915
// 20200922
// 20200929

var_dump($period->getRecurrences());
// int(4)

recurrences 参数的作用就是按照指定的时间间隔返回几条信息,这里我们是返回 9月1号 之后每次间隔 7 天的 4 条信息,和上面的内容一样。这时我们修改构造函数的值为其它数量,比如修改为 2 ,那么就只会返回到 9月15号 的信息了。它不会受到结束日期的约束,可以返回从开始日期到指定数量之后的所有信息,大家可以自己尝试一下。

总结

今天学习的内容不知道大家有没有接触过,反正我是只用过 diff() 方法来处理过日期之间的差值问题,而且也并没有注意到过它返回的这个对象具体的内容。而另外两个对象则是压根没有印象,完全就是没听说过的感觉。所以说,平常多刷刷手册还是非常有帮助的,今天学习的内容又让我们知道了很多东西,而且 DatePeriod 在具体的业务实现中是肯定会有使用场景的。学习不止,后面我们要学习的内容依然精彩。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202009/source/12.PHP中的日期相关函数(一).php

参考文档:

https://www.php.net/manual/zh/book.datetime.php

各自媒体平台均可搜索【硬核项目经理】

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容

  • DateTimeInterface接口[#datetimeinterface%E6%8E%A5%E5%8F%A3]...
    思考的山羊阅读 297评论 0 0
  • MySQL 获得当前日期时间(以及时间的转换) 获取当前日期+时间(date + time)函数:now()除了n...
    X_JX阅读 416评论 0 2
  • 在Web程序开发时,时间发挥着重要的作用。不仅在数据存储和显示时需要日期和时间的参与,很多功能模块的开发,时间通常...
    乐乐凡凡阅读 511评论 0 0
  • 我是黑夜里大雨纷飞的人啊 1 “又到一年六月,有人笑有人哭,有人欢乐有人忧愁,有人惊喜有人失落,有的觉得收获满满有...
    陌忘宇阅读 8,535评论 28 53
  • 信任包括信任自己和信任他人 很多时候,很多事情,失败、遗憾、错过,源于不自信,不信任他人 觉得自己做不成,别人做不...
    吴氵晃阅读 6,187评论 4 8