日期相關的操作函數是我們在日常的工作開發中最常接觸到的功能。當然,大部分同學可能最多用到的就是 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
各自媒體平臺均可搜索【硬核項目經理】