PHP & mySQL: Năm 2038 Lỗi: Nó là gì? Làm thế nào để giải quyết nó?


116

Tôi đã nghĩ đến việc sử dụng TIMESTAMP để lưu trữ ngày + giờ, nhưng tôi đọc rằng có giới hạn của năm 2038 trên đó. Thay vì đặt câu hỏi hàng loạt, tôi muốn chia nó thành các phần nhỏ để người dùng mới làm quen cũng dễ hiểu. Vì vậy (các) câu hỏi của tôi:

  1. Chính xác thì vấn đề của Năm 2038 là gì?
  2. Tại sao nó xảy ra và điều gì xảy ra khi nó xảy ra?
  3. Làm thế nào để chúng tôi giải quyết nó?
  4. Có bất kỳ lựa chọn thay thế khả thi nào để sử dụng nó không gây ra vấn đề tương tự không?
  5. Chúng ta có thể làm gì với các ứng dụng hiện có sử dụng TIMESTAMP, để tránh cái gọi là sự cố, khi nó thực sự xảy ra?

Cảm ơn trước.


1
Điều đó vẫn còn 28 năm nữa. Bạn vẫn đang sử dụng bất kỳ công nghệ liên quan đến máy tính nào từ năm 1982? Không có khả năng. Vì vậy, đừng lo lắng, vì đến năm 2038, nó có thể sẽ không còn là vấn đề nữa.
Gordon

24
Gordon, tôi tạo một ứng dụng dự báo. Tôi tiết kiệm được bao nhiêu tiền mỗi tháng và sau đó có thể ước tính khi nào tôi sẽ trở thành triệu phú. Trong trường hợp của tôi, 28 năm không phải là nhiều, và tôi chắc chắn rằng tôi không phải là người duy nhất gặp phải vấn đề này ngay bây giờ (tôi đã giải quyết nó bằng cách sử dụng số 64-bit để đại diện cho dấu thời gian).
Emil Vikström

2
@Emil Bằng cách sử dụng số nguyên 64bit ngay bây giờ , bạn đã tìm thấy cho mình một giải pháp dễ dàng cho một vấn đề cụ thể. Không áp dụng cho (hoặc cần thiết bởi) tất cả mọi người, nhưng làm việc cho usecase của bạn. Điểm là, nếu OP không gặp vấn đề cụ thể, chẳng hạn như dự báo, thì đây có thể là một chủ đề thú vị, nhưng không có gì anh ta nên lo lắng, bởi vì giải quyết vấn đề này ở cấp độ chung không phải là vấn đề PHP (nhớ thẻ). Chỉ là 2c của tôi.
Gordon

1
Đến năm 2038, phân tích cú pháp chuỗi "YYYY-MM-DD HH: MM: SS: mmmm ..." sẽ là thao tác rẻ nhất mà bạn có thể mơ ước. Đến năm 2038, 32 Bit sẽ lỗi thời. Tôi nghi ngờ dấu thời gian Unix vì chúng ta biết rằng nó sẽ tồn tại khi đó, và nếu có, hệ thống 256 Bit của chúng tôi sẽ xử lý những ngày vượt xa thời đại mà hệ thống 4096 bit được đưa ra trong những bữa ăn vui vẻ.
Super Cat

8
Gordon, 9 năm sau của nó bây giờ. TIMESTAMPS vẫn được sử dụng. Chúng tôi vẫn sử dụng công nghệ từ 28 năm trước. Nó được gọi là world wide web.
sfscs

Câu trả lời:


149

Tôi đã đánh dấu đây là wiki cộng đồng nên bạn cứ thoải mái chỉnh sửa.

Chính xác thì vấn đề của Năm 2038 là gì?

"Sự cố năm 2038 (còn được gọi là Unix Millennium Bug, Y2K38 tương tự với sự cố Y2K) có thể khiến một số phần mềm máy tính bị lỗi trước hoặc trong năm 2038. Sự cố này ảnh hưởng đến tất cả phần mềm và hệ thống lưu trữ thời gian hệ thống dưới dạng ký 32 -bit số nguyên và giải thích con số này là số giây kể từ 00:00:00 UTC vào ngày 1 tháng 1 năm 1970. "


Tại sao nó xảy ra và điều gì xảy ra khi nó xảy ra?

Thời gian ngoài 03:14:07 UTC vào Thứ Ba, ngày 19 tháng 1 năm 2038 sẽ 'quấn quanh' và được lưu trữ nội bộ dưới dạng số âm, mà các hệ thống này sẽ giải thích là thời điểm vào ngày 13 tháng 12 năm 1901 chứ không phải vào năm 2038. Điều này là do thực tế là số giây kể từ kỷ nguyên UNIX (ngày 1 tháng 1 năm 1970 00:00:00 GMT) sẽ vượt quá giá trị tối đa của máy tính cho một số nguyên có dấu 32 bit.


Làm thế nào để chúng tôi giải quyết nó?

  • Sử dụng các kiểu dữ liệu dài (64 bit là đủ)
  • Đối với MySQL (hoặc MariaDB), nếu bạn không cần thông tin thời gian, hãy xem xét sử dụng DATEkiểu cột. Nếu bạn cần độ chính xác cao hơn, hãy sử dụng DATETIMEthay vì TIMESTAMP. Lưu ý rằng DATETIMEcác cột không lưu trữ thông tin về múi giờ, vì vậy ứng dụng của bạn sẽ phải biết múi giờ nào đã được sử dụng.
  • Các giải pháp khả thi khác được mô tả trên Wikipedia
  • Chờ các nhà phát triển MySQL sửa lỗi này được báo cáo hơn một thập kỷ trước.

Có bất kỳ lựa chọn thay thế khả thi nào để sử dụng nó không gây ra vấn đề tương tự không?

Hãy thử sử dụng các kiểu lớn để lưu trữ ngày tháng trong cơ sở dữ liệu ở mọi nơi có thể: 64-bit là đủ - một kiểu dài dài trong GNU C và POSIX / SuS, hoặc sprintf('%u'...)trong PHP hoặc phần mở rộng BCmath.


Một số trường hợp sử dụng có khả năng vi phạm mặc dù chúng ta chưa ở năm 2038 là gì?

Vì vậy, DATETIME của MySQL có phạm vi 1000-9999, nhưng TIMESTAMP chỉ có phạm vi 1970-2038. Nếu hệ thống của bạn lưu trữ ngày sinh, ngày chuyển tiếp trong tương lai (ví dụ: thế chấp 30 năm) hoặc tương tự, bạn sẽ gặp phải lỗi này. Một lần nữa, không sử dụng TIMESTAMP nếu đây sẽ là một vấn đề.


Chúng ta có thể làm gì với các ứng dụng hiện có sử dụng TIMESTAMP, để tránh cái gọi là sự cố, khi nó thực sự xảy ra?

Một số ứng dụng PHP sẽ vẫn còn tồn tại vào năm 2038, mặc dù khó có thể đoán trước được vì web hầu như không phải là một nền tảng kế thừa.

Đây là một quy trình để thay đổi một cột trong bảng cơ sở dữ liệu để chuyển đổi TIMESTAMPsang DATETIME. Nó bắt đầu với việc tạo một cột tạm thời:

# rename the old TIMESTAMP field
ALTER TABLE `myTable` CHANGE `myTimestamp` `temp_myTimestamp` int(11) NOT NULL;

# create a new DATETIME column of the same name as your old column
ALTER TABLE `myTable` ADD `myTimestamp` DATETIME NOT NULL;

# update all rows by populating your new DATETIME field
UPDATE `myTable` SET `myTimestamp` = FROM_UNIXTIME(temp_myTimestamp);

# remove the temporary column
ALTER TABLE `myTable` DROP `temp_myTimestamp`

Tài nguyên


2
Tuyệt vời. Đối với tôi, câu trả lời của bạn là đầy đủ nhất. Cảm ơn nhiều.
Devner

7
Trong MySQL, nếu phiên bản tương lai thay đổi kiểu dữ liệu lưu trữ cơ bản của TIMESTAMP thành 64-bit, thì không cần phải thay đổi mã của bạn thành DATETIME, phải không? Tôi chỉ nghĩ rằng không khuyến khích bất kỳ ai sử dụng TIMESTAMP là điều đúng đắn nên làm vì kiểu dữ liệu đó có mục đích của nó.
pixelfreak

1
Trường BIGINT đã ký của MySQL có vẻ như nó sẽ hoạt động tốt để lưu trữ dấu thời gian và tôi đã thực hiện một số thử nghiệm cục bộ trên MySQL 5.5 và xác nhận rằng nó hoạt động. Rõ ràng là sử dụng có chữ ký sẽ tốt hơn là không dấu vì bạn cũng có thể đại diện cho các ngày trong quá khứ. Có lý do gì để không sử dụng BIGINT cho dấu thời gian?
zuallauz

Một điều cần lưu ý, MySQL chỉ có cài đặt tự động thành ngày / giờ hiện tại (CURRENT_TIMESTAMP) cho các trường dấu thời gian. Hy vọng rằng chức năng này cuối cùng sẽ chuyển sang tất cả các loại ngày.
Ray

5
Hoàn toàn vô lý khi MySQL (và MariaDB) không sử dụng số nguyên 64 bit để lưu trữ dấu thời gian trên hệ thống 64 bit. Sử dụng DATETIME không phải là một giải pháp thích hợp vì chúng tôi không có ý tưởng về múi giờ. Điều này đã được báo cáo trở lại vào năm 2005 , nhưng vẫn chưa có bản sửa lỗi.
Mike

14

Khi sử dụng Dấu thời gian UNIX để lưu trữ ngày tháng, bạn thực sự đang sử dụng số nguyên 32 bit, giữ số giây kể từ 1970-01-01; xem Unix Time

Số 32 bit đó sẽ tràn vào năm 2038. Đó là vấn đề năm 2038.


Để giải quyết vấn đề đó, bạn không được sử dụng dấu thời gian UNIX 32 bit để lưu trữ ngày tháng của mình - có nghĩa là khi sử dụng MySQL, bạn không nên sử dụng TIMESTAMP, nhưng DATETIME(xem 10.3.1. Loại DATETIME, DATE và TIMESTAMP ):

Các DATETIMEloại được sử dụng khi bạn cần các giá trị mà chứa cả ngày và thông tin thời gian. Phạm vi được hỗ trợ là '1000-01-01 00:00:00'để '9999-12-31 23:59:59'.

Kiểu TIMESTAMPdữ liệu có phạm vi từ '1970-01-01 00:00:01'UTC đến '2038-01-19 03:14:07'UTC.


Điều tốt nhất (có thể) bạn có thể làm đối với ứng dụng của mình để tránh / khắc phục sự cố đó là không sử dụng TIMESTAMP, nhưng DATETIMEđối với các cột phải chứa ngày không nằm trong khoảng từ 1970 đến 2038.

Tuy nhiên, có một lưu ý nhỏ: có khả năng rất cao (nói theo thống kê) rằng đơn đăng ký của bạn sẽ được viết lại khá nhiều lần trước năm 2038 ^^ Vì vậy, có thể, nếu bạn không phải xử lý ngày tháng trong tương lai , bạn sẽ không phải quan tâm đến vấn đề đó với phiên bản ứng dụng hiện tại của mình ...


1
+1 Để biết thông tin. Nỗ lực ở đây là điều chỉnh các phương pháp hay nhất ngay từ đầu để người ta không phải lo lắng về vấn đề sau này. Vì vậy, mặc dù hiện tại năm 2038 có vẻ không phải là vấn đề, nhưng tôi chỉ muốn làm theo các phương pháp hay nhất và tuân theo nó cho từng & mọi ứng dụng mà tôi tạo, ngay từ đầu. Hy vọng điều đó có ý nghĩa.
Devner

Vâng, nó có lý, tôi thấy quan điểm của bạn. Nhưng "phương pháp hay nhất" cũng có thể có nghĩa là "câu trả lời cần thiết" - Nếu bạn biết dấu thời gian là đủ, thì không cần sử dụng ngày giờ (ví dụ: những dấu thời gian đó cần nhiều bộ nhớ hơn để lưu trữ; và điều đó có thể quan trọng nếu bạn có hàng triệu bản ghi) ;; Tôi có một số ứng dụng trong mà tôi sử dụng dấu thời gian cho một số cột (cột có chứa ngày hiện tại, ví dụ), và datetime đối với một số người khác (cột chứa ngày trong quá khứ / tương lai, ví dụ)
Pascal MARTIN

Tôi thấy thủ tục của bạn cũng có ý nghĩa và hoạt động tốt, đặc biệt là khi liên quan đến không gian lưu trữ. Nếu không sao, tôi có thể hỏi bạn cấu trúc và độ dài mà bạn thường sử dụng cho cột TIMESTAMP trong ứng dụng của mình không? Cảm ơn.
Devner

Chà, khi tôi muốn sử dụng TIMESTAMP, đó là vì / vì dữ liệu "ngày / giờ" của tôi phù hợp giữa năm 1970 và 2038 - và, vì vậy, tôi sử dụng kiểu dữ liệu MySQL TIMESTAMP.
Pascal MARTIN

Cảm ơn bạn về thông tin. Từ phản hồi của bạn, tôi hiểu rằng chỉ cần khai báo cột là TIMESTAMP là đủ và chúng tôi không cần cung cấp bất kỳ độ dài nào cho nó (không giống như và int hoặc var, nơi chúng tôi cung cấp độ dài). Tôi nói đúng chứ?
Devner

7

Tìm kiếm nhanh trên Google sẽ giải quyết được vấn đề: Vấn đề năm 2038

  1. Sự cố năm 2038 (còn được gọi là Unix Millennium Bug, Y2K38 tương tự với sự cố Y2K) có thể khiến một số phần mềm máy tính bị lỗi trước hoặc trong năm 2038
  2. Sự cố ảnh hưởng đến tất cả phần mềm và hệ thống lưu trữ thời gian hệ thống dưới dạng số nguyên 32 bit có dấu và diễn giải số này là số giây kể từ 00:00:00 UTC vào ngày 1 tháng 1 năm 1970. Thời gian mới nhất có thể được biểu diễn theo cách này là 03:14:07 UTC vào Thứ Ba, ngày 19 tháng 1 năm 2038. Thời gian sau thời điểm này sẽ "quấn quanh" và được lưu trữ nội bộ dưới dạng số âm, mà các hệ thống này sẽ hiểu là ngày vào năm 1901 chứ không phải năm 2038
  3. Không có cách khắc phục dễ dàng cho sự cố này cho các kết hợp CPU / OS hiện có, hệ thống tệp hiện có hoặc các định dạng dữ liệu nhị phân hiện có

2

http://en.wikipedia.org/wiki/Year_2038_problem có hầu hết các chi tiết

Tóm tắt:

1) + 2) Vấn đề là nhiều hệ thống lưu trữ thông tin ngày tháng dưới dạng int 32-bit có dấu bằng số giây kể từ ngày 1/1/1970. Ngày mới nhất có thể được lưu trữ như thế này là 03:14:07 UTC vào Thứ Ba, ngày 19 tháng 1 năm 2038. Khi điều này xảy ra, int sẽ "quấn quanh" và được lưu trữ dưới dạng số âm sẽ được hiểu là ngày vào năm 1901. Chính xác thì điều gì sẽ xảy ra sau đó, thay đổi theo từng hệ thống nhưng đủ để nói rằng nó có thể sẽ không tốt cho bất kỳ hệ thống nào!

Đối với các hệ thống chỉ lưu trữ ngày trong quá khứ, thì tôi đoán bạn không cần phải lo lắng trong một thời gian! Vấn đề chính là với các hệ thống hoạt động với ngày tháng trong tương lai. Nếu hệ thống của bạn cần hoạt động với các ngày 28 năm trong tương lai thì bạn nên bắt đầu lo lắng ngay bây giờ!

3) Sử dụng một trong các định dạng ngày thay thế có sẵn hoặc chuyển sang hệ thống 64 bit và sử dụng số nguyên 64 bit. Hoặc đối với cơ sở dữ liệu sử dụng định dạng dấu thời gian thay thế (ví dụ: đối với MySQL, sử dụng DATETIME)

4) Xem 3!

5) Xem 4 !!! ;)


Điểm 4 & 5 của bạn nhắc nhở tôi về 'Gọi bằng tham chiếu'. Điều đó mang lại một nụ cười trên khuôn mặt của tôi. Cảm ơn & +1 cho cùng một.
Devner

1

Thưa ông, nếu bạn cần sử dụng PHP để hiển thị dấu thời gian, đây là giải pháp PHP TỐT NHẤT mà không cần thay đổi từ định dạng UNIX_TIMESTAMP.

Sử dụng một hàm custom_date (). Bên trong nó, hãy sử dụng DateTime. Đây là giải pháp DateTime .

Miễn là bạn có UNSIGNED BIGINT (8) làm dấu thời gian của bạn trong cơ sở dữ liệu. Miễn là bạn có PHP 5.2.0 ++


-1

Vì tôi không muốn nâng cấp bất cứ thứ gì nên tôi đã yêu cầu chương trình phụ trợ (MSSQL) của mình thực hiện công việc này thay vì PHP!

$qry = "select DATEADD(month, 1, :date) next_date ";
$rs_tmp = $pdo->prepare($qry);
$rs_tmp->bindValue(":date", '2038/01/15');
$rs_tmp->execute();
$row_tmp = $rs_tmp->fetch(PDO::FETCH_ASSOC);

echo $row_tmp['next_date'];

Có thể không phải là một cách hiệu quả, nhưng nó hoạt động.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.