Cách tốt nhất để lưu trữ DateTime dựa trên TimeZone


16

Phát triển ứng dụng web cho phép Người dùng lên lịch hẹn dựa trên TimeZone của họ. Và tôi đang lưu trữ datetime theo lịch trình của người dùng dưới dạng datetime của máy chủ vào trường cơ sở dữ liệu. Trong khi hiển thị thông tin lịch biểu đã lấy giá trị từ Cơ sở dữ liệu và chuyển đổi thành múi giờ người dùng. xử lý trong Mã cơ sở Tôi đang chuyển đổi DateTime dựa trên múi giờ người dùng. Xin đề nghị là thực hành tốt nhất này hoặc bất kỳ cách dễ dàng đang tồn tại?

Câu trả lời:


33

Chào mừng bạn đến với một trong những vấn đề khó khăn nhất trong lập trình phi tính toán - thể hiện đúng ngày và giờ cho người dùng cuối.

Trên thực tế, dấu thời gian nên được lưu trữ trong một đại diện cố định bất kể chúng sẽ được giải thích như thế nào, bởi vì dù bạn có cố gắng thế nào, bạn sẽ luôn có những trường hợp mơ hồ và bạn không thể giải quyết chúng mà không có đại diện cố định. Và bạn đã chọn một trong những trường hợp xấu nhất trong các trường hợp sử dụng - lên lịch một cuộc hẹn. Trường hợp sử dụng phổ biến tồi tệ nhất là du lịch hàng không, trong đó một chuyến đi có thể bắt đầu ở một múi giờ và kết thúc ở một múi giờ khác, có thể vào một giờ địa phương sớm hơn.

Luôn luôn, luôn luôn, LUÔN lưu trữ trong UTC và hiển thị theo múi giờ ưa thích hoặc được chỉ định rõ ràng của người dùng. Nếu có thể, hãy làm cho người dùng cho bạn biết múi giờ họ tin rằng dấu thời gian sẽ xuất hiện khi họ nhập nó ( ví dụ: có trường múi giờ rõ ràng và điền trước vào vùng ưa thích của họ).


1
+1. Và nó linh hoạt. Có một thời gian tham chiếu tiêu chuẩn chung, nhất quán, phổ biến - bất kỳ "hội nghị" địa phương nào cũng sẽ được thực hiện chính xác.
radarbob

Trên thực tế, việc lên lịch một cuộc hẹn là một trong số ít lần bạn không thể lưu trữ nó dưới dạng UTC: Nếu quy tắc DST thay đổi (hoặc phần bù từ UTC), điều gì xảy ra với thời gian cuộc hẹn? Trong hầu hết các trường hợp, bạn muốn thời gian cuộc hẹn giữ nguyên cục bộ, điều đó có nghĩa là giá trị UTC thay đổi. Rất có thể bạn muốn lưu trữ một giá trị đại diện cho "TimeInTimeZone + TimeZone", từ đó bạn có thể lấy được UTC một cách dễ dàng. Lập lịch xuyên thời gian (như các hãng hàng không) có các ràng buộc có thể có nghĩa là sự kết hợp của cả hai được sử dụng.
Clockwork-Muse

1
Ồ, nó còn tệ hơn thế nữa. Tôi thường xuyên có các cuộc họp với các đồng nghiệp ở các quốc gia khác có quy tắc thay đổi thời gian khác với tôi. Khi điều đó xảy ra, câu trả lời đúng là gì? Trên thực tế, không máy tính nào có thể biết :-(
Ross Patterson

3

Cách dễ nhất để xử lý thông tin ngày / giờ phụ thuộc vào yêu cầu của ứng dụng của bạn.

Nếu ngày và giờ được nhập bởi người dùng và máy chủ không xử lý thông tin ngày / giờ đó ngoại trừ việc lưu trữ nó trong cơ sở dữ liệu và không có hy vọng rằng thời gian đã nhập được điều chỉnh phù hợp với vị trí nơi nó được xem (ví dụ: , người dùng đã nhập "8:00 PM" và nó sẽ được hiển thị là "8:00 PM" bất kể nơi nào trên thế giới được xem), cách dễ nhất là lưu trữ DateTime dưới dạng giờ địa phương mà không có thông tin múi giờ.

Mặt khác, nếu máy chủ phải sử dụng DateTime đã nhập làm trình kích hoạt để làm gì đó hoặc nếu thời gian cần được điều chỉnh chính xác theo múi giờ hiện tại, thì tùy chọn tốt nhất của bạn là lưu trữ DateTime dưới dạng thời gian UTC và điều chỉnh nó thành cục bộ thời gian (đối với vị trí hiện tại của người dùng) mỗi lần bạn đọc nó từ cơ sở dữ liệu.


-1 cho "lưu trữ theo giờ địa phương", +1 cho "lưu trữ trong UTC".
Ross Patterson

@RossPatterson: Bạn có thể giải thích '-1 cho "lưu trữ nó trong giờ địa phương"' không? Tôi tò mò tại sao nó nên là một ý tưởng tồi, với những điều kiện tôi đưa ra với nó.
Bart van Ingen Schenau

1
Ví dụ: sử dụng thời gian cục bộ của người dùng có nghĩa là mọi giá trị thực sự là một trong vài chục loại dữ liệu khác nhau. Điều đó có nghĩa là bạn không thể thú vị thực hiện các thao tác cơ sở dữ liệu trên chúng như "TIMESTAMP> '2013-08-27T12: 00: 00'". Nó thậm chí có nghĩa là bạn không thể biết khi nào và nếu áp dụng chuyển đổi Giờ tiết kiệm ánh sáng ban ngày.
Ross Patterson

@RossPatterson: Cảm ơn bạn. Tôi đồng ý rằng giờ địa phương không phải là giải pháp phù hợp trong hầu hết các trường hợp.
Bart van Ingen Schenau

2

Điều quan trọng là bạn không giới thiệu hai khái niệm liên quan.

Nếu bạn đang thao túng một thời điểm thực tế, đó là thời gian cụ thể vào một ngày cụ thể, thì cách duy nhất để làm điều này là luôn luôn sử dụng giá trị thời gian UTC phổ quát. Không bao giờ cố gắng lưu trữ giá trị này dưới dạng giá trị liên quan đến múi giờ. Khi hiển thị giá trị thời gian này cho người dùng, luôn chuyển đổi sang múi giờ của người dùng tại thời điểm đó. (Và đừng quên cả thời gian và ngày tùy thuộc vào múi giờ.)

Khái niệm khác là "biểu thức thời gian", chẳng hạn như "8:00 tối thứ ba hàng tuần". Bạn đặc biệt đề cập đến việc cho phép mọi người lên lịch các cuộc hẹn và nếu bạn có các cuộc hẹn định kỳ, bạn cần một biểu thức như vậy. Tôi không biết bất kỳ ngôn ngữ biểu đạt tiêu chuẩn nào, nhưng bất cứ điều gì bạn sử dụng, nó sẽ liên quan đến múi giờ của người đã chỉ định nó. Tốt nhất nên làm rõ điều này, ví dụ: "8:00 tối thứ ba hàng tuần trong múi giờ Thái Bình Dương". Trong trường hợp biểu thức thời gian, bạn lưu trữ chuỗi này luôn dưới dạng chuỗi và không bao giờ liên quan đến bất kỳ định dạng thời gian hệ thống nào.

Xem thêm thảo luận về điều này trên blog của tôi


1

Nhiều câu trả lời ở đây chỉ ra việc lưu trữ như UTC. Nhưng hãy thực sự cẩn thận với điều này. Chẳng hạn, nếu bạn lên lịch một cuộc hẹn lúc 12:00 nhưng cuộc hẹn diễn ra sau khi thay đổi thời gian tiết kiệm ánh sáng ban ngày thì chuyện gì sẽ xảy ra? UTC không giữ bất kỳ thông tin nào về việc dst có hoạt động khi cuộc hẹn được lưu trữ hay không. Rất nhiều hệ thống lớn, nổi tiếng đã mắc phải lỗi này, trong đó người dùng gửi email vào lúc 9 giờ sáng mùa hè và sau đó vào mùa đông, thời gian gửi hiển thị là 8 giờ sáng, vì tính toán trở lại từ UTC phụ thuộc vào thời điểm bạn nhìn vào thời gian, không bật khi datetime được ghi lại.

Tốt hơn nhiều là giả sử người dùng của bạn muốn có những lần anh ta chọn luôn. Không chuyển đổi UTC, không chuyển đổi thời gian, không thông tin múi giờ, không có gì. Lấy hẹn từ 08:00 đến 12:00 ngày 21 tháng 3 năm 2016 chỉ có thế. Không sử dụng giờ địa phương hoặc giờ UTC, nhưng thời gian không xác định (trong json, điều này không có z hay +, về cơ bản, trong .NET, điều này có DateTime.Kind = DateTimeKind.Unspecified).

Tất nhiên, nếu trường hợp sử dụng của bạn là bạn là một công ty tổ chức các cuộc họp với ai đó từ các múi giờ khác nhau và bạn muốn xem thông tin này trong lịch công ty, nhưng cho phép người dùng xem thời gian trong múi giờ của họ, thì đó là trở nên phức tạp hơn Thời gian phải chính xác cho những người khác nhau trong các múi giờ khác nhau (nhà cung cấp và khách hàng).

Trong những trường hợp đó, bạn thậm chí có thể muốn ghi lại khi cuộc hẹn được lưu vào cơ sở dữ liệu, theo múi giờ nào có bao gồm dst hay không. Bằng cách đó, bạn luôn có thể tính lại thời gian địa phương cho bất kỳ điều gì khác, cho đến bây giờ hoặc với những gì nó được dự định là trong lịch sử. Bởi vì các múi giờ rất nhiều không tĩnh, làm cho mọi thứ thậm chí còn phức tạp hơn.

May mắn thay, đây là nơi các thư viện như http://nodatime.org/ đến và rất được khuyến khích. Họ làm việc với ngày ổn định hơn nhiều. Thậm chí sau đó tôi sẽ khuyên bạn nên gói tất cả các biến và logic thời gian của bạn trong các trình bao bọc của riêng bạn, sử dụng các giao diện để chúng có thể được chế giễu, và sau đó bạn vẫn có thể chuyển đổi logic sau này.


Tôi không hoàn toàn đồng ý với những kết luận của bạn, nhưng một cuộc họp vào buổi trưa được lên kế hoạch thay đổi tiết kiệm ánh sáng ban ngày là một thách thức đặc biệt, cũng như một cuộc họp hàng tuần vào buổi trưa được đặt trước vài năm trong tương lai.
Bent

" Tất nhiên, nếu trường hợp sử dụng của bạn là bạn là một công ty tổ chức các cuộc họp với ai đó từ các múi giờ khác nhau và bạn muốn xem thông tin này trong lịch công ty, nhưng cho phép người dùng xem thời gian trong múi giờ của họ, nó trở nên phức tạp hơn. "- đó là khá nhiều công ty. Rất ít công ty bên ngoài Trung Quốc và Ấn Độ có thể bỏ qua các cuộc hẹn xuyên thời gian.
Ross Patterson

" Bằng cách đó, bạn luôn có thể tính ngược thời gian địa phương với bất kỳ thứ gì khác, cho dù hiện tại là gì hoặc theo ý định trong lịch sử. Bởi vì các múi giờ rất không tĩnh, khiến mọi thứ trở nên phức tạp hơn. " - một lần bạn chấp nhận rằng bạn sẽ thực hiện tính toán, bạn thực sự cần phải chọn một đại diện duy nhất cho thời gian. UTC, EST (nhưng không EST5EDT), IST, bất cứ điều gì. Nhưng bạn chỉ đơn giản là không thể có các giá trị dựa trên nhiều múi giờ trong cùng một trường và thực hiện bất kỳ xử lý tổng hợp hợp pháp nào trên chúng. Hiển thị xử lý, chắc chắn. Nhưng đó là phần dễ dàng.
Ross Patterson

Ít nhất bạn vẫn có thể, chính xác, chuyển đổi sang UTC. Bạn luôn có thể tạo bảng tính toán hoặc giá trị từ thông tin này khi nhìn lại. Nhưng bạn không bao giờ có thể đi theo con đường khác. Lưu ý rằng SQL 2016 bắt đầu hỗ trợ điều này một cách tự nhiên, mặc dù không may, nó vẫn dựa vào - ít nhất là trong lịch sử - không hoàn toàn chính xác về dữ liệu đăng ký - nhưng đó là một cải tiến khác.
Arwin
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.