Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Vấn đề Năm 2038 là gì?

Sự cố Năm 2038 (còn được gọi là lỗi Y2K38) đề cập đến một sự cố mà một số hệ thống máy tính có thể gặp phải khi xử lý các thời điểm quá khứ 2038-01-19 03:14:07.

Nhiều hệ thống máy tính, chẳng hạn như hệ thống dựa trên Unix và Unix, không tính toán thời gian bằng lịch Gregory. Họ tính toán thời gian dưới dạng số giây kể từ ngày 1 tháng 1 năm 1970. Do đó, trong các hệ thống này, thời gian được biểu thị dưới dạng số lớn (tức là số giây đã trôi qua kể từ 1970-01-01 00:00:00). Đây thường được gọi là thời gian Epoch, Unix time, Unix Epoch time, hoặc POSIX time. Khi tôi viết điều này, thời gian Unix là 1560913841. Và khi tôi viết dòng tiếp theo này, thời gian Unix đã tăng lên 1560913879.

Sự cố năm 2038 là do nhiều hệ thống lưu trữ số này dưới dạng số nguyên nhị phân 32 bit có dấu. Phạm vi của số nguyên 32 bit có dấu là -2,147,483,648 đến 2,147,483,647. Điều này có nghĩa là thời gian của Kỷ nguyên mới nhất có thể được biểu thị là 2147483647. Điều này sẽ xảy ra lúc 03:14:07 vào Thứ Ba, ngày 19 tháng 1 năm 2038.

Sau đó, kết quả sẽ phụ thuộc phần lớn vào hệ thống. Trong nhiều hệ thống, hiện tượng tràn số nguyên sẽ xảy ra và bất kỳ khoảng thời gian nào sau đó sẽ xảy ra và được lưu trữ bên trong dưới dạng số âm. Kết quả là một giây sau, thời gian sẽ được hiểu là vào ngày 13 tháng 12 năm 1901 thay vì ngày 19 tháng 1 năm 2038.

Tuy nhiên, bạn cũng có thể nhận được các kết quả khác nhau, tùy thuộc vào ứng dụng đang sử dụng. Ngay cả khi hệ điều hành của bạn không có vấn đề gì, mã của riêng bạn vẫn có thể gặp sự cố. Ví dụ:nếu bạn đã viết mã tùy chỉnh để trả về thời gian Unix và bạn lưu trữ nó ở dạng số nguyên 4 byte có dấu, bạn sẽ gặp sự cố. Trong những trường hợp như vậy, việc viết lại mã để sử dụng số nguyên 8 byte có thể là tất cả những gì bạn cần làm.

Vì trang web này là tất cả về cơ sở dữ liệu, đây là một số ví dụ về cơ sở dữ liệu.

Ví dụ 1 - MySQL

Trong MySQL, TIMESTAMP kiểu dữ liệu hỗ trợ ngày / giờ từ ‘1970-01-01 00:00:01.000000’ UTC đến ‘2038-01-19 03:14:07.999999’. Do đó, bạn có thể nói rằng bất kỳ cơ sở dữ liệu nào sử dụng kiểu dữ liệu này đều có lỗi Y2K38.

MySQL cũng có một hàm sẵn có được gọi là UNIX_TIMESTAMP() như bạn có thể mong đợi, trả về dấu thời gian Unix.

UNIX_TIMESTAMP() hàm chấp nhận một đối số tùy chọn cho phép bạn chỉ định ngày để sử dụng cho thời gian Unix (tức là số giây từ ‘1970-01-01 00:00:00’ UTC đến thời gian mà bạn chỉ định). Phạm vi giá trị đối số hợp lệ giống như đối với TIMESTAMP kiểu dữ liệu, là ‘1970-01-01 00:00:01.000000’ UTC đến ‘2038-01-19 03:14:07.999999’ UTC. Nếu bạn chuyển một ngày nằm ngoài phạm vi cho hàm này, nó sẽ trả về 0 .

Đây là những gì sẽ xảy ra nếu bạn cố gắng sử dụng chức năng này để trả về thời gian Unix từ một ngày trong quá khứ '2038-01-19 03:14:07.999999':

Kết quả
SELECT UNIX_TIMESTAMP('2038-01-20') Result;

Kết quả:

+--------+
| Result |
+--------+
|      0 |
+--------+

Chúng tôi nhận được 0 vì đối số ngày nằm ngoài phạm vi được hỗ trợ.

Một báo cáo lỗi liên quan đã được đưa ra cho nhóm MySQL vào năm 2005 (mặc dù một số chi tiết cụ thể có vẻ khác nhau) và tính đến thời điểm viết bài này, vẫn chưa được giải quyết.

Một vấn đề tương tự cũng được đưa ra để giải quyết các hạn chế với TIMESTAMP kiểu dữ liệu, cũng chưa được giải quyết.

Ví dụ 2 - Máy chủ SQL

SQL Server hiện không có phần mềm tương đương với UNIX_TIMESTAMP của MySQL hàm số. Do đó, nếu bạn cần quay lại thời gian của Epoch, bạn sẽ cần phải làm như sau:

SELECT DATEDIFF(SECOND,'1970-01-01', GETUTCDATE());

Điều này là tốt cho những ngày trước vấn đề năm 2038. Sau ngày đó, bạn sẽ gặp sự cố vì DATEDIFF() hàm trả về kết quả dưới dạng int loại dữ liệu. int kiểu dữ liệu có phạm vi từ -2 ^ 31 (-2,147,483,648) đến 2 ^ 31-1 (2,147,483,647).

Đây là điều sẽ xảy ra nếu tôi cố gắng quay lại thời gian của Epoch muộn hơn '2038-01-19 03:14:07':

SELECT DATEDIFF(SECOND,'1970-01-01', '2038-01-19 03:14:08') AS 'Result';

Kết quả:

The datediff function resulted in an overflow. The number of dateparts separating two date/time instances is too large. Try to use datediff with a less precise datepart.

May mắn thay, cũng có một DATEDIFF_BIG() hàm, thực hiện chính xác cùng một việc, ngoại trừ nó trả về kết quả là bigint kiểu dữ liệu.

Vì vậy, chúng ta có thể viết lại ví dụ trước thành ví dụ sau để khắc phục sự cố này:

SELECT DATEDIFF_BIG(SECOND,'1970-01-01 00:00:00', '2038-01-19 03:14:08') AS 'Result';

Kết quả:

+------------+
| Result     |
|------------|
| 2147483648 |
+------------+

bigint kiểu dữ liệu sử dụng 8 byte (trái ngược với 4 byte cho int ), vì vậy bạn sẽ cần quyết định có chuyển sang DATEDIFF_BIG() hay không bây giờ hoặc sau này. Nếu ứng dụng của bạn đề cập đến các ngày trong tương lai, thì bạn nên cẩn thận làm điều đó sớm hơn muộn hơn.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Mẹo nhanh - Tăng tốc độ khôi phục chậm từ Nhật ký giao dịch

  2. Sử dụng Jenkins với Kubernetes AWS, Phần 1

  3. So sánh SQL, trình tạo truy vấn và ORM

  4. Cách kết hợp kết quả của hai truy vấn trong SQL

  5. Cân nhắc về hiệu suất phiên bản được quản lý của Azure SQL