Mysql bây giờ chỉ có ngày

Trong bài viết trước, mình đã giới thiệu các bạn những khái niệm cơ bản liên quan đến ngày giờ & múi giờ. Bài viết này mình sẽ phân tích một số vấn đề thường gặp trong thực tế

Resumptions

Trước tiên chúng ta sẽ ôn lại các khái niệm cơ bản

  • chốc lát. đối xứng time
  • rtime (thời gian tương đối/đại diện). thời gian tương đối, hoặc cũng có thể gọi là thời gian chỉ để hiển thị
  • bù lại. UTC speed
  • múi/múi giờ. time

chốc lát

Là một khoảng cụ thể có thể giải thích trong dòng lịch sử

Ví dụ. Khoảng khắc Việt Nam vô địch AFF Cup vào lúc 19. 30 ngày 15/12/2018 tại sân vận động Mỹ Đình, Hà Nội

Khi biểu diễn, cần có đủ hai thành phần. ngày giờ + ngữ cảnh nơi chốn. Ngữ cảnh ở đây chính là múi giờ (vùng). Các múi giờ được đặc trưng bởi một độ lệch thời gian (bù đắp) vì vậy với giờ phân phối quốc tế UTC. Độ lệch được biểu diễn dưới dạng ±hh:mm

Việt Nam thuộc múi giờ Đông Dương (Indochina Time - ICT) có độ lệch UTC+07:00

Trong máy tính, khoảnh khắc được biểu diễn dưới dạng Epoch Time - số giây trôi qua kể từ 00. 00. 00 ngày 1 tháng 1 năm 1970 theo giờ UTC

thời gian

Thời gian chỉ mang tính hiển thị (thời gian đại diện, gọi tắt là rtime), không kèm theo múi giờ hay độ lệch, không dùng để đối chiếu so sánh

  • Tiết học bắt đầu lúc 12h45
  • Ca làm việc bắt đầu lúc 15h
  • Các ngày lễ, ngày kỷ niệm hằng năm

Khi thêm vùng hoặc bù vào thời gian, chúng ta sẽ có một lúc

moment = rtime + (zone or offset)

Nếu chưa phân biệt được rtime/moment, bạn có thể xem lại bài viết trước

3 Common Unknown Error

1. Bạn đã chọn đúng lớp để xử lý?

Việc chọn nhầm kiểu dữ liệu có thể bắt nguồn từ sự nhầm lẫn giữa thời gian và thời điểm - bạn muốn xử lý thời gian dạng rtime nhưng lại chọn kiểu thời điểm và quay ngược lại

Chúng ta cùng xem lại danh sách các lớp ngày giờ trong Java tương ứng với 2 loại thời gian

Type timeJava Classrtime(Java 1. 8) LocalDate, LocalTime, LocalDateTime khoảnh khắcjava.util.Date,
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
0

(sql)
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
1,
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
2,
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
3

(Java 1. 8)
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
4,
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
5,
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
6

Loại thời gian rtime chỉ mới xuất hiện ở Java 1. 8 - vậy trước đó xử lý kiểu gì?

Nhiều người sẽ nhận ra, trước đây họ không có ý niệm về thời gian hay khoảnh khắc - họ luôn dùng

System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
7 cho mọi trường hợp, nếu có đến múi giờ thì dùng
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
0, nếu có thời gian biểu ngữ lặp đi lặp lại thì dùng
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
9 và
moment = rtime + (zone or offset)
0

Để giải quyết vấn đề này, chúng ta phải hiểu bản chất của

System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
7 - bản chất của nó là khoảnh khắc (hiển nhiên rồi, vì nó chứa Epoch Time), nhưng cũng có thể xem nó là rtime, do Java sẽ tự động chuyển đổi về múi giờ của nó.

System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime

Nhiều người hay nhầm lẫn và không nắm được bản chất của

System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
7, dẫn đến việc vô tình tạo ra lỗi khi hệ thống múi giờ bị thay đổi

Với thiết kế của bộ DateTime mới, công việc xử lý thời gian sẽ trở nên chặt chẽ hơn bằng cách đưa ra hệ thống quy chiếu thời gian/thời điểm

  1. Mọi thao tác so sánh đối chiếu thời gian, nên quy tất cả về thời điểm, cụ thể là
    System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
    System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
    // Thời gian hiển thị của Date trùng khớp với ZonedDateTime
    
    4
  2. Muốn tạo một khoảng thời gian cụ thể tại một khoảng thời gian cụ thể (thời điểm), chúng ta bắt đầu từ việc khởi động thời gian khởi động với LocalDateTime (phân tích cú pháp từ chuỗi chuỗi hoặc từ các số ngày, tháng, năm, giờ. tối thiểu. sec), sau đó gắn vào múi giờ đó để tạo ra thời điểm
    System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
    System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
    // Thời gian hiển thị của Date trùng khớp với ZonedDateTime
    
    6. (Xem chi tiết ở )

2. Bạn đã chọn đúng lớp để lưu trữ?

Cho dù đã phân biệt được 2 loại thời gian và chọn đúng lớp để thao tác, chúng ta vẫn có khả năng chọn nhầm kiểu lưu trữ bên dưới cơ sở dữ liệu

Trong Java, chúng ta sử dụng JDBC - Java Database Connectivity - một API cho phép hệ thống tương tác với cơ sở dữ liệu. Dưới đây là bảng ánh xạ kiểu dữ liệu tổng hợp của JDBC (Nguồn Oracle)

Java TypeJDBC Type_______39_______
moment = rtime + (zone or offset)
7 (chỉ dành cho Oracle)
moment = rtime + (zone or offset)
8 (all other databases)
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
1
moment = rtime + (zone or offset)
7
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
2
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
2
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
3
moment = rtime + (zone or offset)
8

"DATE, TIME, TIMESTAMP - ủa cái này quen, hồi đó mình có gặp rồi nè, nhưng mà sao có cảm giác nó không ăn nhập với 2 khái niệm rtime/moment nhỉ?"

Thôi bình tĩnh nhé. Thực hiện nó. but compiles more than as so. Ứng dụng với từng Nhà cung cấp cơ sở dữ liệu (Postgres, MySQL. ), chúng ta phải tuân theo một kiểu ánh xạ khác nhau

Mặc dù vậy, việc đưa ra hệ thống quy chiếu rtime/moment lại tương đối dễ dàng. Chúng ta xem xét 2 ví dụ về Postgres và MySQL

postgres. DẤU THỜI GIAN hay DẤU THỜI GIAN?

Postgres cung cấp 2 loại dấu thời gian là có múi giờ (TIMESTAMPZ) và không có múi giờ (TIMESTAMP). T liếc nhìn chúng ta sẽ cho rằng dấu thời gian luôn là khoảnh khắc. Tuy nhiên, Hướng dẫn Postgres giải thích

  • DẤU THỜI GIAN. when a moment was archive down, Postgres will remove the timezone section and only save the display section (ngày và giờ). Nếu thay đổi múi giờ của máy chủ cơ sở dữ liệu, giá trị dấu thời gian luôn giữ nguyên không thay đổi

  • DẤU THỜI GIAN. là dấu thời gian nhận biết vùng
    • Khi một khoảnh khắc được lưu xuống, Postgres sẽ chuyển đổi về giờ UTC và lưu dưới dạng TIMESTAMP ở trên, nghĩa là chỉ lưu phần rtime (ở đây là lý do cả 2 kiểu dữ liệu đều chỉ sử dụng 8 byte bộ nhớ). Tuy nhiên, nhờ thao tác convert về UTC, nên có thể xem đây là một khoảnh khắc ở múi giờ UTC
    • Khi đọc lên, Postgres thực hiện thao tác đảo ngược. giá trị dấu thời gian được chuyển đổi từ UTC về múi giờ của máy chủ cơ sở dữ liệu và trả về cho phụ trợ
    • Nói một cách rút gọn, khi lưu xuống ngày-giờ ở múi giờ nào thì đọc lên vẫn là ngày-giờ đó, ở múi giờ đó

Như vậy đã rõ ràng, TIMESTAMP chính là rtime, còn TIMESTAMPZ chính là khoảnh khắc. Tài liệu của Trình điều khiển JDBC cho Postgres được đưa ra ánh xạ bảng như sau

PostgreSQL™Java SE 8DATELocalDateTIMELocalTimeTIMESTAMPLocalDateTimeTIMESTAMPZOffsetDateTime

Có thể thấy, cách ánh xạ kiểu dữ liệu của Postgres hoàn toàn khớp với hệ thống quy chiếu rtime/moment

( The ta don't used

System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
6 type to mapping TIMESTAMPZ, by
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
6 has contain logic processing Time Daylight Saving Time - while the chất lượng TIMESTAMPZ does not contain information as so that)

"If

System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
7 is moment, TIMESTAMP is rtime, so tui map
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
7 with TIMESTAMP then it save down DB type? (Trước giờ tui vẫn hay làm như vậy á)"

Như đã giải thích trước đó, chúng ta phải hiểu bản chất của

System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
7

  • Tại thời điểm lưu xuống,
    System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
    System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
    // Thời gian hiển thị của Date trùng khớp với ZonedDateTime
    
    7 được chuyển đổi về múi giờ của phụ trợ, Postgres chỉ lưu phần rtime (chỉ có ngày giờ)
  • Khi thời điểm đọc lên, Postgres thực hiện thao tác quay ngược. gắn múi giờ (của phụ trợ) vào dấu thời gian và trả về một thời điểm hoàn chỉnh

Mysql bây giờ chỉ có ngày

"Ủa, vậy sao trước giờ tui sử dụng

System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
7 hoặc
System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
3 để lập bản đồ TIMESTAMP mà hệ thống của nó vẫn chạy đúng, không bị lỗi?"

Đó là vì múi giờ của máy chủ phụ trợ và cơ sở dữ liệu ở các môi trường LOCAL, DEV và PROD luôn quán nhất với nhau. Vấn đề sẽ phát sinh nếu thay đổi múi giờ và phá vỡ sự quán nhất của hệ thống hiện tại

Bạn có thể tự kiểm tra bằng cách lưu

System.out.println(ZonedDateTime.now());    // 2021-08-29T11:05:37.119+07:00[Asia/Bangkok]
System.out.println(new Date());             // Sun Aug 29 11:05:37 ICT 2021
// Thời gian hiển thị của Date trùng khớp với ZonedDateTime
7 xuống Postgres dưới dạng DẤU THỜI GIAN, sau đó thay đổi múi giờ của phụ trợ rồi đọc lại giá trị đó, chắc chắn sẽ có sự cố

mysql. Ngày giờ hay Dấu thời gian?

(Lưu ý là Dấu thời gian của MySQL và Postgres không giống nhau)

Tài liệu của mô tả MySQL

Khi lưu Dấu thời gian, giá trị sẽ được chuyển đổi từ múi giờ hiện tại về UTC. Khi đọc lên sẽ đi qua bước lùi. Quy trình này không áp dụng đối với kiểu DateTime

Vì vậy, dễ dàng kết luận, DateTime chính là rtime, còn Timestamp là khoảnh khắc. Tuy nhiên, Trình điều khiển JDBC của MySQL là Trình kết nối/J có sự khác biệt so với Postgres. Để hiểu chính xác cách ánh xạ dữ liệu, bạn có thể tham khảo tài liệu của Connector/J

Thêm nữa, giới hạn lưu trữ của Dấu thời gian trong MySQL là ±hh:mm4, vốn được biết đến với tên gọi Sự cố năm 2038 - Năm 2038 Vấn đề là sự cố xảy ra khi chúng ta lưu khoảnh khắc dưới dạng Epoch Time bằng một số nguyên 32-bit

Thực hành tốt nhất

Đối với thời điểm hiện tại, chúng ta sẽ đi theo hướng dẫn của trình điều khiển JDBC mà chọn kiểu dữ liệu phù hợp (ở phụ trợ cũng như trong cơ sở dữ liệu)

Đối với rtime (thời gian có tính năng lặp lại, ví dụ. time lock biểu tượng, lịch làm việc, ngày lễ kỷ niệm. ), if you don't cover datetime of Java 1. 8 thì tốt nhất nên lưu dạng string vì tính dễ đọc, dễ debug và không gây hiểu lầm. Chúng ta sẽ cần thêm vài bước trung gian để phân tích cú pháp và gắn zone/offset vào, tuy nhiên chuyện đó không tốn nhiều công sức. Nếu không sử dụng chuỗi, thì hãy đảm bảo tuân theo hướng dẫn từ nhà phát triển trình điều khiển JDBC

3. Bạn đang thiết kế giao diện đúng cách?

Vâng, bạn không nghe nhầm đâu. Có phải bạn đã từng thấy một điều khiển chọn ngày giờ như thế này đúng không?

Mysql bây giờ chỉ có ngày

(xdsoft. mạng - plugin jQuery)

Tất nhiên rồi, đây là một biến thiết kế phổ biến, không có vấn đề gì với chiếc datetimepicker này cả

Nhưng mình có một vấn đề. Giả sử chúng ta đang thiết kế một cuộc họp ứng dụng trực tuyến. Để chọn thời gian bắt đầu cuộc họp, chúng ta sử dụng datetimepicker như hình trên. Theo bạn, ngày giờ mà người dùng chọn có đáng tin cậy hay không?

Câu trả lời. hên xui

Phần lớn datetimepicker của thư viện sẽ trả về thời gian dựa trên múi giờ đang được cài đặt trên máy khách. Cố gắng đặt trường hợp người dùng đang sử dụng máy tính thuê của ai đó, với một khoảng thời gian khác. Lúc này bạn sẽ không biết thời gian mà người dùng chọn và gửi về từ frontend có đáng tin cậy hay không

  1. The first power. người dùng không nhận ra sự khác biệt về thời gian. Họ chỉ tin vào những thứ họ nhìn thấy trong ứng dụng. Chắc chắn giá trị ngày giờ sẽ sai lệch

  2. hai chức năng. người dùng nhận ra sự khác biệt giữa các múi giờ trong máy tính họ đang sử dụng. Họ có thể sửa lại múi giờ cho đúng. Nếu không thể sửa đổi (không có quyền hạn), họ sẽ cố gắng quy đổi về đúng giá trị mong muốn. Tuy nhiên, sau tất cả, họ vẫn rối và tự hỏi, "cái thời gian mà mình chọn cuối cuộc là giờ ở đâu nhỉ?"

Giải pháp

Hướng giải quyết của chúng ta rất đơn giản - hãy tham khảo giải pháp của những "ông lớn"

Mysql bây giờ chỉ có ngày

Màn hình tạo cuộc họp của Outlook

Mysql bây giờ chỉ có ngày

Màn hình tạo sự kiện của Facebook

Chúng ta thấy, hệ thống của Outlook phục vụ cho người dùng của toàn cầu, làm việc đó bổ sung vào lựa chọn múi giờ cũng là chuyện dễ hiểu. Còn đối với Facebook, họ chỉ sử dụng loại truyền thống datetimepicker, nhưng kèm theo đó hiển thị rõ ràng giá trị thời gian mà người dùng chọn sẽ thuộc về múi giờ nào (múi giờ của máy tính họ đang sử dụng - như ví dụ trong GMT +

Điểm chung của 2 hướng tiếp giáp này chính là tính chất rõ ràng của thời gian - người dùng sẽ biết cách chọn đúng giá trị mà họ muốn

Đối với những hệ thống chỉ dùng ở một địa phương hoặc một quốc gia, tốt nhất nên theo hướng tiếp cận như Facebook (hiển thị rõ múi giờ cho người dùng), để tránh những hiểu nhầm không mong muốn