2013-10-26 25 views
8

Trong đơn đăng ký của tôi, tôi đang tính toán thời gian còn lại cho đến nửa đêm GMT (giờ Anh). Tại thời điểm này tôi đang thực hiện việc này:Ngày giờ sử dụng múi giờ không chính xác

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

Mã đang hoạt động, tuy nhiên có vẻ sau một giờ (sử dụng GMT -1). Tại thời điểm này thời gian là 23:49 và đầu ra là thế này:

1 hours, 10 minutes and 36 seconds 

Tôi đã hai lần kiểm tra của tôi php.ini và tôi cũng có mà múi giờ thiết lập như là giờ:

date.timezone = Europe/London 

này cũng được khẳng định bằng cách kiểm tra phpinfo().

Điều gì mang lại? Tại sao ứng dụng của tôi không sử dụng múi giờ chính xác?

+0

Đồng hồ trong sự thay đổi Anh từ BST lấy múi giờ GMT đêm nay –

+0

@MarkBaker , các đồng hồ có quay ngược lại ngày hôm nay thay vì chuyển tiếp không? Và tôi khá chắc chắn rằng điều đó không xảy ra cho đến 2 giờ sáng. –

+0

Tôi có nghĩa là giờ hiện tại ở Luân Đôn là 2 phút trước nửa đêm Giờ mùa hè Anh ... GMT là 2 phút trước 11 giờ tối, vì vậy nó vẫn còn 58 phút đến nửa đêm GMT (UST) –

Trả lời

20

Tôi đã thử nghiệm tính năng này trên Linux PHP 5.5.5, với Europe/London được đặt làm múi giờ trong php.ini. Tôi thực sự thiết lập đồng hồ trở lại bốn giờ để làm điều này, quá. Mã tối thiểu tôi đã sử dụng để tái tạo là:

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

sự (đúng) đầu ra là:

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

Tôi sẽ tìm kiếm một lỗi trong PHP hay một lỗi trong dữ liệu múi giờ. Để tìm hiểu, chúng ta sẽ thấy những gì một số chương trình khác làm cho nửa đêm ở London tối nay. Epoch Converter nói với tôi điều này nên có một dấu thời gian Unix của 1382828400. Để kiểm tra lại dấu thời gian đó, tôi chạy trong PHP:

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

Nó cũng trở 1382828400. Vì vậy, chúng ta hãy xem những gì nó phải to show ...

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

Kết quả là:

Sun 27 Oct 2013 12:00:00 AM BST 

đúng! Vì vậy, tzdata là tốt. Vì vậy, hãy nhìn vào PHP.

Tôi chạy mẫu mã của bạn, cùng với lệnh date, và nhận được kết quả như sau:

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

Đây là, tất nhiên, chính xác.

Tôi nghĩ tại thời điểm này, chúng tôi đã loại trừ các lỗi trong cả tzdata và PHP, đồng thời cần xem xét các vấn đề cấu hình và kỳ vọng của lập trình viên.


Đầu tiên, như tôi đã nói trước đây, Châu Âu/Luân Đôn không phải là UTC, không có khái niệm về thời gian mùa hè và do đó không thay đổi hai lần mỗi năm. Vì nó không gây ra vấn đề như vậy, nên thực hành tốt nhất cho các máy chủ chạy trên UTC, bất kể múi giờ người dùng của họ là gì và thực hành tốt nhất cho các chương trình để sử dụng UTC trong nội bộ và sau đó chuyển đổi sang/từ múi giờ địa phương cho chỉ hiển thị và nhập người dùng.

Dự đoán tốt nhất của tôi là máy chủ của bạn đang chạy PHP thực sự được đặt để sử dụng UTC chứ không phải Châu Âu/Luân Đôn làm múi giờ mặc định của nó. Đây là cấu hình duy nhất mà tôi có thể tái tạo vấn đề của bạn.Kết quả của bài kiểm tra đó là:

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 

Trong tương lai, bạn nên làm việc tại UTC (và với timestamps Unix) bất cứ nơi nào thực tế, và chuyển đổi thành giờ cục bộ càng sớm trong chế biến đầu vào người sử dụng, và như trễ trong việc hiển thị nó, như bạn có thể. Một trường hợp cạnh như thế này, nơi thời gian mùa hè sắp kết thúc, có thể là một ngoại lệ, nhưng bạn phải cẩn thận hơn để đảm bảo rằng mỗi đối tượng mới DateTime bạn xây dựng có múi giờ chính xác được thiết lập khi bạn xây dựng nó và cũng có thể nhận thức được rằng họ sẽ có vấn đề như thế này.

Xem thêm khổng lồ và thông tin Daylight saving time and time zone best practices


Cuối cùng, để "sửa chữa" mã của bạn, chúng ta hãy làm điều này:

$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 cho $ tz = new DateTimeZone ('Europe/London'); –

Các vấn đề liên quan