2013-05-02 42 views
12

Tôi lưu trữ tất cả các trường DateTime dưới dạng thời gian UTC. Khi người dùng yêu cầu trang web, tôi muốn sử dụng múi giờ địa phương ưa thích của mình (và không phải múi giờ địa phương của máy chủ) và tự động hiển thị tất cả các trường DateTime trong tất cả các biểu mẫu web dưới dạng ngày địa phương. Tất nhiên, tôi có thể áp dụng chuyển đổi trên mọi cuộc gọi DateTime.ToString() trong mọi hình thức hoặc thực hiện một số tiện ích trợ giúp nhưng nó là nhiệm vụ tốn thời gian và cũng có một số thành phần của bên thứ ba khó cấu hình các mẫu hiển thị DateTime tùy chỉnh.Chuyển đổi toàn cục UTC DateTimes thành Ngày giờ địa phương do người dùng chỉ định

Về cơ bản, tôi muốn làm cho lớp DateTime để hành xử như sau:

from this moment on for this web request, 
whenever some code calls DateTime.ToString(), convert it to the local time 
     using the timezone offset given at the very beginning of the web request, 
but if possible, please keep .NET core library DateTime.ToString() calls intact 
     (I don't want to mess up event logging timestamps etc.) 

Có cách nào để làm điều đó?

BTW, tôi đang sử dụng ASP.NET MVC 4, nếu nó quan trọng.

Trả lời

3

Câu trả lời ngắn gọn là bạn không thể. HTTP không yêu cầu (hoặc thậm chí cung cấp một cách tiêu chuẩn) cho tác nhân người dùng (trình duyệt) để cung cấp thông tin múi giờ hoặc múi giờ địa phương trong yêu cầu HTTP.

Bạn có thể cần phải

  • yêu cầu người dùng cho múi giờ ưa thích của họ, hoặc
  • có client-side javascript báo cáo nó cho bạn bằng cách nào đó (cookie? Ajax? Khác?)

Lưu ý rằng giải pháp javascript phía máy khách cũng không hoàn hảo. Javascript bị vô hiệu hóa (hoặc không tồn tại, đối với một số trình duyệt). Javascript có thể không có quyền truy cập vào thông tin múi giờ. Vv

26

Bạn không thể thực hiện trực tiếp những gì bạn đã yêu cầu, nhưng tôi sẽ đề xuất một số lựa chọn thay thế. Như Nicholas đã chỉ ra, không có gì trong HTTP sẽ cung cấp cho bạn múi giờ trực tiếp.

Lựa chọn 1

  • Thứ nhất, quyết định loại dữ liệu múi giờ bạn muốn làm việc với. Có hai loại khác nhau có sẵn, hoặc múi giờ của Microsoft mà bạn có thể truy cập với lớp TimeZoneInfo hoặc múi giờ IANA/Olson mà phần còn lại của thế giới sử dụng. Read here for more info. Đề xuất của tôi sẽ là đề xuất sau, sử dụng triển khai được cung cấp bởi NodaTime.

  • Sau đó, xác định múi giờ bạn muốn chuyển đổi. Bạn nên cho phép người dùng của bạn cài đặt ở đâu đó để chọn múi giờ của họ.

    • Bạn có thể hiển thị một danh sách thả xuống để chọn một trong nhiều múi giờ, hoặc bạn có thể làm điều gì đó hữu ích hơn, như hiển thị một bản đồ thế giới rằng họ có thể bấm vào để chọn múi giờ của họ. Có một số thư viện có thể làm điều này trong Javascript, nhưng yêu thích của tôi là this one.

    • Bạn có thể muốn đoán múi giờ mặc định để sử dụng, vì vậy bạn có thể gần chính xác nhất có thể trước khi họ chọn từ danh sách (hoặc bản đồ). Có một thư viện tuyệt vời cho điều này được gọi là jsTimeZoneDetect. Nó sẽ thẩm vấn đồng hồ của trình duyệt và đưa ra giả định phỏng đoán tốt nhất về múi giờ của nó.Nó khá tốt, nhưng nó vẫn chỉ là một phỏng đoán. Đừng sử dụng nó một cách mù quáng - nhưng hãy sử dụng nó để xác định điểm khởi đầu. Cập nhật Bạn cũng có thể làm điều này với moment.tz.guess(), trong thành phần moment-timezone của moment.js.

  • Bây giờ bạn biết múi giờ của người sử dụng, bạn có thể sử dụng giá trị đó để chuyển đổi UTC bạn DateTime giá trị cho rằng múi giờ địa phương. Thật không may, không có gì bạn có thể thiết lập trên thread sẽ làm điều đó. Khi bạn thay đổi múi giờ của hệ thống, nó là toàn cục cho tất cả các tiến trình và luồng. Vì vậy, bạn không có sự lựa chọn, nhưng để vượt qua múi giờ cho mỗi và mọi nơi bạn đang gửi nó trở lại. (Tôi tin rằng đây là câu hỏi chính của bạn.) See this almost duplicate here.

  • Trước khi chuyển đổi thành chuỗi, bạn cũng cần phải biết ngôn ngữ của người dùng (bạn có thể nhận được từ giá trị Request.UserLanguages). Bạn có thể gán nó cho chuỗi hiện tại hoặc bạn có thể chuyển nó làm tham số cho phương thức DateTime.ToString(). Điều này không thực hiện bất kỳ chuyển đổi múi giờ nào - nó chỉ đảm bảo rằng các số nằm ở vị trí chính xác, sử dụng dấu phân cách chính xác và ngôn ngữ thích hợp cho tên của các ngày trong tuần hoặc các tháng.

Lựa chọn 2

Đừng chuyển nó sang giờ địa phương trên máy chủ ở tất cả.

  • Vì bạn biết bạn đang làm việc với các giá trị tính theo giờ UTC, đảm bảo tài sản của họ là .KindUtc. Có lẽ bạn nên làm điều này khi bạn tải từ cơ sở dữ liệu của bạn, nhưng nếu bạn có để bạn có thể làm điều đó bằng tay:

    myDateTime = DateTime.SpecifyKind(myDateTime, DateTimeKind.Utc); 
    
  • Gửi nó lại cho trình duyệt như tinh khiết UTC, trong một định dạng bất biến như ISO8601. Nói cách khác:

    myDateTime.ToString("o"); // example: "2013-05-02T21:01:26.0828604Z" 
    
  • Sử dụng một số JavaScript trên trình duyệt để phân tích cú pháp dưới dạng UTC. Nó sẽ tự động nhận các cài đặt thời gian cục bộ của trình duyệt. Một cách là sử dụng được xây dựng trong Date đối tượng trong JavaScript, như thế này:

    var dt = new Date('2013-05-02T21:01:26.0828604Z'); 
    

    Tuy nhiên, điều này sẽ chỉ làm việc trong các trình duyệt mới hơn có hỗ trợ các định dạng tiêu chuẩn ISO-8601. Thay vào đó, tôi khuyên bạn nên sử dụng thư viện moment.js. Nó phù hợp trên các trình duyệt, và nó có sự hỗ trợ tốt hơn cho các ngày ISO và nội địa hoá. Ngoài ra, bạn còn nhận được rất nhiều chức năng định dạng và phân tích cú pháp hữu ích khác.

    // pass the value from your server 
    var m = moment('2013-05-02T21:01:26.0828604Z'); 
    
    // use one of the formats supported by moment.js 
    // this is locale-specific "long date time" format. 
    var s = m.format('LLLL'); 
    

Ưu điểm của Lựa chọn 1 là bạn có thể làm việc với thời gian trong bất kỳ múi giờ. Nếu bạn có thể yêu cầu người dùng cho múi giờ của họ từ danh sách thả xuống, thì bạn không cần sử dụng bất kỳ Javascript nào.

Lợi thế của Tùy chọn 2 là bạn sẽ khiến trình duyệt thực hiện một số công việc cho bạn. Đây là cách tốt nhất để đi nếu bạn đang gửi dữ liệu thô, chẳng hạn như thực hiện cuộc gọi AJAX đến WebAPI. Tuy nhiên, JavaScript chỉ biết UTC và múi giờ địa phương của trình duyệt. Vì vậy, nó không hoạt động tốt như vậy nếu bạn cần chuyển đổi thành các khu vực khác.

Bạn cũng nên lưu ý rằng nếu bạn chọn Tùy chọn số 2, bạn có thể bị ảnh hưởng bởi lỗ hổng trong thiết kế ECMAScript 5.1. Điều này xảy ra nếu bạn đang làm việc với các ngày được bao phủ bởi một bộ quy tắc thời gian tiết kiệm ánh sáng ban ngày khác nhau hiện đang có hiệu lực. Bạn có thể đọc thêm in this questionon my blog.

Sẽ dễ dàng hơn nhiều nếu chúng tôi có thông tin múi giờ trong tiêu đề HTTP, nhưng rất tiếc là chúng tôi không làm như vậy. Đây là rất nhiều hoops để nhảy qua, nhưng đó là cách tốt nhất để có cả sự linh hoạt và chính xác.

+0

Câu trả lời hay! Bạn nói điều này cũng có thể được thực hiện với đối tượng 'Date' được xây dựng sẵn trong JavaScript. Bạn vui lòng cập nhật câu trả lời của mình để cho biết cách thực hiện điều này? – Scott

+0

@Scott - Đã cập nhật. Cảm ơn. –

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