2016-03-17 53 views
2

我有一些PHP代碼發送警告並自動關閉服務,如果源數據文件已過期。在夏令時變化時,filemtime可以與strtotime進行比較嗎?

警告: filemtime($file_location) < strtotime('-15 minute')

考慮服務下來: filemtime($file_location) < strtotime('-1 hour')

1小時,上午3:00開始CDT夏令時開始時,這錯誤地報告這是使用下面的做比較文件超過15分鐘。即將結束的時候,它也會錯誤地報告文件已超過1個小時。凌晨4點,CDT事情恢復正常。

有什麼我不知道可以解釋這種行爲的filemtime()或strtotime()函數嗎?這是我的理解,都返回UNIX時間戳,並且時間戳由UTC定義,所以我不知道是什麼會導致此問題。

基於答案爲止,我跑了我們的服務器在這個測試:

`

for($i = 1; $i <=4; $i++){ 
    // let's say current time is this: 
    date_default_timezone_set('America/Winnipeg'); 
    $current_timestamp = strtotime('2016-03-13 0'.$i.':00:00'); // 1457856060 

    // DST start at 03:00 that day, so 60 minutes before the time should be 01:01 
    $ts = $current_timestamp - 1*60*60; 
    echo '-3600s: ', $ts, ", ", date('Y-m-d H:i:s', $ts), "\n"; 
    // 1457852460, 2016-10-30 02:59:00, correct 

    // now let's test strtotime with -1 hour 
    $ts = strtotime('-1 hour', $current_timestamp); 
    echo '-1 hour: ', $ts, ", ", date('Y-m-d H:i:s', $ts), "\n"; 
    // 1457856060, 2016-10-30 03:01:00, completely wrong 

    // DateTime implementation seems smarter: 
    $dt = new DateTime(); 
    $dt->setTimestamp($current_timestamp); 
    $dt->sub(new DateInterval('PT1H')); 
    echo 'sub PT1H: ', $dt->getTimestamp(), ", ", $dt->format('Y-m-d H:i:s'), "\n"; 
    // 1457856060, 2016-10-30 03:01:00, correct 

    echo "\n\n"; 
    echo 'TS  ', $current_timestamp, ", ", date('Y-m-d H:i:s', $current_timestamp), "\n"; 
    echo "\n\n"; 
} 

`

-3600s:1457848800,2016年3月13日0點00分: 00

-1小時:1457848800,2016年3月13日〇點00分00秒

子PT1H:1457848800,2016-03 -13 0時00分○○秒

TS 1457852400,2016年3月13日1點00分00秒

-3600s:1457852400,2016年3月13日1點00分00秒

-1小時:1457856000,2016年3月13日三點00分00秒

子PT1H:1457856000,2016年3月13日三點00分00秒

TS 1457856000,2016年3月13日三點00分00秒

-3600s:1457852400,2016-03-13 01:00:00

-1小時:1457856000,2016年3月13日3點00分00秒

子PT1H:1457856000,2016年3月13日3點00分00秒

TS 1457856000,2016-03- 13 3時00分00秒

-3600s:1457856000,2016年3月13日3時00分00秒

-1小時:1457856000,2016年3月13日3時00分00秒

子PT1H:1457856000,2016-03-13 03:00:00

TS 1457859600,2016-03-13 04:00:00

回答

1

這很令人困惑。在我的時區,在夏令時開始於3月27日,所有下面的表達式返回相同的UNIX時間戳:

var_dump(strtotime("27 March 2016 02:00")); 
var_dump(strtotime("- 60 minute", strtotime("27 March 2016 02:00"))); 
var_dump(strtotime("- 1 hour", strtotime("27 March 2016 02:00"))); 
var_dump(strtotime("27 March 2016 02:00 - 1 hour")); 
var_dump((new DateTime("27 March 2016 02:00"))->format("U")); 
var_dump((new DateTime("27 March 2016 02:00"))->sub(new DateInterval("PT1H"))->format("U")); 

如果我認爲有足夠的瞭解這個很難,我也許可以相信這是預期的行爲。 3月27日01:00 這裏不存在。但這當然不直觀。

更容易混淆的是,從02:00減去15分鐘,將來會給出答案,即,即02:45。即使第一個結果有點意義,如果你足夠眯眼,這個結果已成爲一個錯誤。

有關於這方面的an open bug report已有近4年未觸及過。

這給你留下兩種選擇。您可以在代碼中添加if子句,如果初始減法不減少時間戳,則可以減去2小時或75分鐘。或者,您可以簡單地從當前時間戳減去3600或900秒,而不是使用strtotime。既不是特別優雅,但我們是。

+0

開放的PHP錯誤可能是問題的根源,儘管我仍然不明白髮生了什麼。 – flergl

+0

在上面的進一步測試中,通過從時間戳中減去3600得到的答案是正確的,而其他答案在從DT中的時間減去足夠的時間以將其帶回ST後是錯誤的。因此1am -1h總是午夜,4am -1h總是凌晨3點。凌晨2點和凌晨3點是**同一時間**。時間戳-3600正確返回1am,而在兩種情況下使用strtotime或DateInterval都不正確地返回3am(即沒有時間被減去)。 –

1

好像strtotime是不夠聰明採取相對值操作時DST改變考慮:

// let's say current time is this: 
date_default_timezone_set('Europe/Berlin'); 
$current_timestamp = strtotime('2016-10-30 01:59:00'); // 1477785540 

// DST ends at 03:00 that day, so 120 minutes later the time should be 02:59 
$ts = $current_timestamp + 2*60*60; 
echo $ts, ", ", date('Y-m-d H:i:s', $ts), "\n"; 
// 1477792740, 2016-10-30 02:59:00, correct 

// now let's test strtotime with +2 hours 
$ts = strtotime('+2 hours', $current_timestamp); 
echo $ts, ", ", date('Y-m-d H:i:s', $ts), "\n"; 
// 1477796340, 2016-10-30 03:59:00, completely wrong 

// DateTime implementation seems smarter: 
$dt = new DateTime(); 
$dt->setTimestamp($current_timestamp); 
$dt->add(new DateInterval('PT2H')); 
echo $dt->getTimestamp(), ", ", $dt->format('Y-m-d H:i:s'); 
// 1477792740, 2016-10-30 02:59:00, correct 

在您的特定情況下,我只需將時間戳直接比較(差異將在幾秒鐘,顯然),但使用DateTimeTimeInterval也是一個選項。

+0

在我的測試中(添加到問題中,因爲評論的代碼太多),使用「strtotime」的「-1小時」和使用「DateTime」的「PT1H」給出相同的時間戳。不只是彼此相同,與我正在調整的基準時間戳相同。至少在問題時間內。然而,減去總秒數會給出不同的時間戳。 – flergl