PHP中的日期相關(guān)函數(shù)(一)
日期相關(guān)的操作函數(shù)是我們在日常的工作開發(fā)中最常接觸到的功能。當然,大部分同學可能最多用到的就是 date() 、 time() 這兩個函數(shù),我們今天先不講這兩個函數(shù),或許后面的文章也不太會講它們,畢竟太常用了。本身在對手冊文檔的學習中,就是要發(fā)掘有意思的或者沒有接觸過的一些功能函數(shù),所以我們今天的學習的函數(shù)可能是大家都沒怎么用過的,甚至可能很多大家都沒見過的。
時區(qū)類相關(guān)函數(shù)
首先就是時區(qū)類的一個對象。它可以幫助我們獲取一些當前時區(qū)相關(guān)的信息。
$timezone = new DateTimeZone('Asia/Shanghai');
var_dump($timezone);
// object(DateTimeZone)#1 (2) {
// ["timezone_type"]=>
// int(3)
// ["timezone"]=>
// string(13) "Asia/Shanghai"
// }
在實例化這個 DateTimeZone 時區(qū)類時,需要傳遞一個時區(qū)參數(shù)。這里我們給的就是通用的中國時區(qū)的設(shè)置,雖說我們的國際標準時區(qū)是東八區(qū)的北京時間,但在 PHP 中的時區(qū)格式中,我們的時區(qū)是以上海為命名的。
這個時區(qū)類可以直接獲得當前指定時區(qū)的定位信息,比如 Asia/Shanghai 的定位信息就是直接定位到上海市的。
// 時區(qū)相關(guān)的定位信息
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() 就可以獲取到時區(qū)類的定位信息,經(jīng)緯度的查詢結(jié)果就是上海市中心,comments 字段也明確地指出了當前時區(qū)是北京時間。
// 時區(qū)名稱
var_dump($timezone->getName());
// string(13) "Asia/Shanghai"
// 相對于 GMT 的時差
var_dump($timezone->getOffset(new DateTime('now', $timezone)));
// int(28800)
// 所有時區(qū)轉(zhuǎn)換信息
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() 方法獲取的是當前時區(qū)的名稱,這個就不用多說了。getOffset() 則是獲取到與國際格林尼治時間(GMT)的差值,也就是與子午線的時間間隔,這里返回的是秒,轉(zhuǎn)換成小時后正好就是 8 小時。getTransitions() 函數(shù)返回的是所有時區(qū)轉(zhuǎn)換的時間,我測試的時間是早上,返回的 time 字段內(nèi)容是格林尼治時間,offset 字段返回的也是與GMT時間的差值。GMT時間與UTC時間是一致的,我們在日常的學習和工作中如果接觸到了這兩個名詞可以將它們看做是相同的概念。
UTC時間的標準叫法是協(xié)調(diào)世界時間,基于國際原子時間,全世界的國家的標準時間都是以它為標準進行調(diào)校的,而GMT的本意是定位為本初子午線的平太陽時,UTC也是以這條經(jīng)線為基準進行時區(qū)劃分的。不過,按照嚴格的標準來說,它們并不是完全相等的,具體的內(nèi)容大家可以自行查閱,但對于我們?nèi)粘i_發(fā)來說,完全可以將它們視為等同的東西。
// 包含 dst (夏令時),時差和時區(qū)信息的關(guān)聯(lián)數(shù)組
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"
// }
// ……
// ……
// 包含了所有時區(qū)標示符的索引數(shù)組
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() 靜態(tài)方法返回的是 夏令時 相關(guān)的時差和時區(qū)信息。夏令時 和 冬令時 也是西方國家的一種生活標準,我們接觸的不多,這里就不做講解了,對于做跨境項目或者歐美外包的同學應(yīng)該不會陌生。listIdentifiers() 方法返回的是包含了所有時區(qū)標示符的索引數(shù)組,這里可以看到所有的支持的時區(qū)信息。
日期間隔操作
對時日期時間的間隔操作,或許大家多少都做過一點,比如 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)
// }
從打印的結(jié)果可以看出,diff() 對象返回的是一個 DateInterval 對象。這個就是我們這節(jié)的主角了,關(guān)于它打印出來的這些屬性內(nèi)容就不多解釋了,字段名已經(jīng)非常直觀了,值就是具體的差值。
$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)
// }
看到?jīng)]有?打印出來的內(nèi)容和上面用 diff() 方法返回的對象的內(nèi)容是一致的,但是它的構(gòu)造函數(shù)的參數(shù)很奇怪。沒錯,當我們自己去實例化一個 DateInterval 對象時,需要為它定義它的間隔信息,這個間隔信息就是我們通過構(gòu)造函數(shù)的參數(shù)傳遞進去的。P2D 的意思就是間隔 2 天,首先必須以一個 P 為開頭,然后可以有 Y、M、D 這些日期內(nèi)容,如果需要時間內(nèi)容的話,需要一個 T 然后再跟上 H、M、S 這些內(nèi)容。比如 P2Y4DT6H8M 表示的就是 2年4天6小時8分鐘 的時間間隔。具體的規(guī)則大家還是去看文檔中的說明:https://www./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)
// }
我們還可以通過字段串形式的日期數(shù)據(jù)返回間隔對象,比如:
// 從日期語句創(chuàng)建時間間隔
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() 函數(shù)一樣來格式化地輸出日期信息,而且這里用的格式符還是日期的格式符。
var_dump($interval->format('%y %d %h %i'));
// string(7) "2 4 6 8"
輸出的內(nèi)容其實就是屬性中對應(yīng)的那些日期和時間差值。
時間周期相關(guān)函數(shù)
說完時間間隔了,我們再來看看時間周期。時間周期是個什么概念呢?就比如說我們要每三天間隔一次地獲取日期,這時就可以用時間周期相關(guān)的類來進行處理。
$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
首先設(shè)定了開始時間和結(jié)束時間以及一個時間間隔對象,然后用它們做為參數(shù)來生成一個 DatePeriod 時間周期對象。它是一個實現(xiàn)了迭代器的對象,所以我們可以直接遍歷它,結(jié)果就是以 P7D ,也就是 7 天為間隔的一組日期數(shù)據(jù)。
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"
// }
它的這一堆方法其實返回的就是我們定義的那些構(gòu)造參數(shù)信息。另外,它還可以指定從開始日期往后按照時間間隔返回幾條信息。
$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 參數(shù)的作用就是按照指定的時間間隔返回幾條信息,這里我們是返回 9月1號 之后每次間隔 7 天的 4 條信息,和上面的內(nèi)容一樣。這時我們修改構(gòu)造函數(shù)的值為其它數(shù)量,比如修改為 2 ,那么就只會返回到 9月15號 的信息了。它不會受到結(jié)束日期的約束,可以返回從開始日期到指定數(shù)量之后的所有信息,大家可以自己嘗試一下。
總結(jié)
今天學習的內(nèi)容不知道大家有沒有接觸過,反正我是只用過 diff() 方法來處理過日期之間的差值問題,而且也并沒有注意到過它返回的這個對象具體的內(nèi)容。而另外兩個對象則是壓根沒有印象,完全就是沒聽說過的感覺。所以說,平常多刷刷手冊還是非常有幫助的,今天學習的內(nèi)容又讓我們知道了很多東西,而且 DatePeriod 在具體的業(yè)務(wù)實現(xiàn)中是肯定會有使用場景的。學習不止,后面我們要學習的內(nèi)容依然精彩。
測試代碼:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202009/source/12.PHP中的日期相關(guān)函數(shù)(一).php
參考文檔:
https://www./manual/zh/book.datetime.php