2013-10-26 24 views
8

在我的應用我試圖計算午夜剩下GMT(英國時間)的時間。目前我正在這樣做:日期時間使用不正確時區

$now = new DateTime(); 
$timeToMidnight = $now->setTimezone(new DateTimeZone('Europe/London'))->diff(new DateTime('tomorrow'))->format('%h hours, %i minutes and %s seconds'); 

該代碼正在工作,但它似乎是一小時後(使用GMT -1)。此刻的時間是下午11:49,輸出是這樣的:

1 hours, 10 minutes and 36 seconds 

我仔細檢查過我的php.ini,我也有一個時區設置爲格林尼治標準時間:

date.timezone = Europe/London 

這也證實通過檢查phpinfo()

什麼給?爲什麼我的應用程序不使用正確的時區?

+0

時鐘從BST英國改變北京時間今晚 –

+0

@MarkBaker ,今天的時鐘不是倒退而不是轉發?我敢肯定,直到凌晨2點纔會發生。 –

+0

我的意思是現在在倫敦的時間是英國的夏令時2分鐘過去的時間...格林威治標準時間是晚上11點過後2分鐘,所以它仍然是58分鐘到午夜格林威治標準時間(UST) –

回答

20

我在Linux PHP 5.5.5上測試了這個,其中Europe/London設置爲php.ini中的時區。我實際上把時鐘設置回四個小時來做​​到這一點。我用重現的最小碼爲:

$d = new DateTime('tomorrow'); 
echo $d->format('c e'); 

的(正確的)輸出:

2013-10-27T00:00:00+01:00 Europe/London 

我要尋找在PHP或時區數據中的錯誤的錯誤。爲了找出哪些,我們將看看今晚倫敦午夜的其他節目。 Epoch Converter告訴我這應該有1382828400. Unix時間戳要仔細檢查時間戳,我跑在PHP中:

$d = new DateTime('27-10-2013'); 
echo $d->format('U'); 

它還返回1382828400.所以,讓我們來看看它的應該顯示...

TZ=Europe/London date --date="@1382828400" +%c 

產量爲:

Sun 27 Oct 2013 12:00:00 AM BST 

正確!所以tzdata很好。所以我們來看看PHP。

我跑你的示例代碼,用date命令一起,並得到了以下的輸出:

1 hours, 29 minutes and 53 seconds 
Sat Oct 26 21:30:07 UTC 2013 
Sat Oct 26 22:30:07 BST 2013 

這是當然的,正確的。

我認爲在這一點上,我們已經排除了在這兩個的tzdata和PHP錯誤,並需要看看配置問題和程序員的預期。首先,正如我之前提到的,歐洲/倫敦不是UTC,沒有夏令時的概念,因此每年不會改變兩次。由於它不會導致這樣的問題,因此無論用戶使用何種時區,服務器都是使用UTC運行的最佳做法,以及程序在內部使用UTC並轉換爲本地時區或從本地時區轉換爲本地時區的最佳做法僅顯示和用戶輸入。

我最好的猜測是,你的服務器運行PHP實際上是設置爲使用UTC而不是歐洲/倫敦作爲其默認的時區。這是我可以重現您的問題的唯一配置。該測試的結果是:

date.timezone = UTC 

2 hours, 24 minutes and 36 seconds 
Sat Oct 26 21:35:24 UTC 2013 
Sat Oct 26 22:35:24 BST 2013 

展望未來,你應該在UTC工作(與Unix的時間戳)在實際可行,並轉換爲本地時間早於處理用戶輸入,併爲後期的顯示出來,盡你所能。像這樣的邊界情況下,夏季即將結束,可能是一個例外,但您必須格外小心,以確保您構建的每個新對象在構建時都具有正確的時區設置,並且也是意識到他們會有這樣的問題。

又見巨大而翔實Daylight saving time and time zone best practices


最後,「修理」你的代碼,讓我們做到這一點:

$tz = new DateTimeZone('Europe/London'); 
$now = new DateTime('now', $tz); 
$midnight = new DateTime('tomorrow', $tz); 
$timeToMidnight = $now->diff($midnight); 
echo $timeToMidnight->format('%h hours, %i minutes and %s seconds'); 
+1

+1爲$ tz = new DateTimeZone('Europe/London'); –