Chuyển đổi giờ UTC / GMT sang giờ địa phương


301

Chúng tôi đang phát triển ứng dụng C # cho máy khách dịch vụ web. Điều này sẽ chạy trên Windows XP PC.

Một trong các trường được dịch vụ web trả về là trường DateTime. Máy chủ trả về một trường ở định dạng GMT, tức là có chữ "Z" ở cuối.

Tuy nhiên, chúng tôi thấy rằng .NET dường như thực hiện một số loại chuyển đổi ngầm định và thời gian luôn luôn là 12 giờ.

Mẫu mã sau đây giải quyết vấn đề này ở một mức độ nào đó trong trường hợp chênh lệch 12 giờ đã hết nhưng nó không cho phép tiết kiệm ánh sáng ban ngày của New Zealand.

CultureInfo ci = new CultureInfo("en-NZ");
string date = "Web service date".ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            

Theo trang web ngày này :

Bù đắp UTC / GMT

Múi giờ tiêu chuẩn: UTC / GMT +12 giờ
Thời gian tiết kiệm ánh sáng ban ngày: +1 giờ
Bù múi giờ hiện tại: UTC / GMT +13 giờ

Làm thế nào để chúng ta điều chỉnh cho thêm giờ? Điều này có thể được thực hiện theo chương trình hay đây là một loại cài đặt nào đó trên PC?


2
các Zthời gian đề cập đến UTC, không giờ GMT. Cả hai có thể khác nhau tới 0,9 giây.
mc0e

Câu trả lời:


374

Đối với các chuỗi như 2012-09-19 01:27:30.000, DateTime.Parsekhông thể cho biết ngày và giờ bắt đầu từ múi giờ nào.

DateTimecó thuộc tính Loại , có thể có một trong ba tùy chọn múi giờ:

  • Không xác định
  • Địa phương
  • Út

LƯU Ý Nếu bạn muốn đại diện cho một ngày / giờ khác với UTC hoặc múi giờ địa phương của bạn, thì bạn nên sử dụng DateTimeOffset.


Vì vậy, đối với mã trong câu hỏi của bạn:

DateTime convertedDate = DateTime.Parse(dateStr);

var kind = convertedDate.Kind; // will equal DateTimeKind.Unspecified

Bạn nói bạn biết nó là loại gì, vì vậy hãy nói với nó.

DateTime convertedDate = DateTime.SpecifyKind(
    DateTime.Parse(dateStr),
    DateTimeKind.Utc);

var kind = convertedDate.Kind; // will equal DateTimeKind.Utc

Bây giờ, khi hệ thống biết thời gian UTC, bạn có thể gọi ToLocalTime:

DateTime dt = convertedDate.ToLocalTime();

Điều này sẽ cung cấp cho bạn kết quả bạn yêu cầu.


19
chỉ là một cách khác để xác định loại:DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);
Brad

không phải là ToLocalTime () sao? @Brad - parens của bạn không phù hợp.
TrueWill

2
Liệu giải pháp này chiếm tài khoản tiết kiệm ánh sáng ban ngày? Khi tôi thử nó, tôi nghỉ một giờ.
Bob Horn

7
Các bước thay đổi Kindcủa DateTimetừ Unspecifiedđể UTClà không cần thiết. Unspecifiedđược giả định là UTCdành cho các mục đích của ToLocalTime: msdn.microsoft.com/en-us/l Library / Wiêu
CJ7

16
@ CJ7: Có, nhưng rõ ràng là đặc biệt hữu ích cho các nhà phát triển khác có thể phải duy trì mã.
Ryan

121

Tôi sẽ xem xét việc sử dụng lớp System.TimeZoneInfo nếu bạn đang ở trong .NET 3.5. Xem http://msdn.microsoft.com/en-us/l Library / system.timezoneinfo.aspx . Điều này sẽ tính đến các thay đổi tiết kiệm ánh sáng ban ngày một cách chính xác.

// Coordinated Universal Time string from 
// DateTime.Now.ToUniversalTime().ToString("u");
string date = "2009-02-25 16:13:00Z"; 
// Local .NET timeZone.
DateTime localDateTime = DateTime.Parse(date); 
DateTime utcDateTime = localDateTime.ToUniversalTime();

// ID from: 
// "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zone"
// See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx
string nzTimeZoneKey = "New Zealand Standard Time";
TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey);
DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone);

Nếu bạn đang làm việc trong múi giờ của riêng bạn (en-NZ trong trường hợp này) thì bạn không cần phải đối phó với TimeZoneInfo. Đó chỉ là sự phức tạp không cần thiết. Xem câu trả lời của tôi để biết thêm chi tiết.
vẽ Noakes

11
Và nếu có ai cần, đây là danh sách các múi giờ tôi đã tìm thấy cho TimeZoneInfo.FindSystemTimeZoneById - codeproject.com/Messages/3867850/
nikib3ro

Xuất sắc! Cảm ơn vì bài viết này Dan. Tôi đã tìm kiếm sửa chữa này trong 3 ngày.
Kevin Moore

58
TimeZone.CurrentTimeZone.ToLocalTime(date);

8
Điều này chỉ hoạt động nếu hệ thống biết rằng ngày được chuyển đổi là trong UTC. Xin vui lòng xem câu trả lời của tôi.
vẽ Noakes

1
Nhưng UTC là mặc định phải không? Do đó, nó hoạt động cho "không xác định" như trong câu trả lời của CJ7.
NickG

25

DateTimeđối tượng có Kindcủa Unspecifiedtheo mặc định, mà nhằm mục đích ToLocalTimeđược giả định là UTC.

Để có được thời gian cục bộ của một Unspecified DateTimeđối tượng, do đó bạn chỉ cần làm điều này:

convertedDate.ToLocalTime();

Các bước thay đổi Kindcủa DateTimetừ Unspecifiedđể UTClà không cần thiết. Unspecifiedđược giả định là UTCdành cho các mục đích của ToLocalTime: http://msdn.microsoft.com/en-us/l Library / system.datetime.tolocaltime.aspx


6
Và ngược lại: convertedDate.FromLocalTime();sẽ chuyển đổi sang UTC.
R. Schreurs

16

Tôi biết đây là một câu hỏi cũ hơn, nhưng tôi đã gặp phải một tình huống tương tự và tôi muốn chia sẻ những gì tôi đã tìm thấy cho những người tìm kiếm trong tương lai, có thể bao gồm cả bản thân mình :).

DateTime.Parse()có thể khó khăn - xem ở đây chẳng hạn.

Nếu DateTime dịch vụ đến từ một dịch vụ Web hoặc một số nguồn khác có định dạng đã biết, bạn có thể muốn xem xét một cái gì đó như

DateTime.ParseExact(dateString, 
                   "MM/dd/yyyy HH:mm:ss", 
                   CultureInfo.InvariantCulture, 
                   DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)

hoặc, thậm chí tốt hơn,

DateTime.TryParseExact(...)

Các AssumeUniversal lá cờ kể về phân tích cú pháp rằng ngày / giờ đã là UTC; sự kết hợp AssumeUniversalAdjustToUniversalbảo nó không chuyển đổi kết quả thành thời gian "cục bộ", mà nó sẽ cố gắng thực hiện theo mặc định. (Cá nhân tôi cố gắng xử lý độc quyền với UTC trong (các) lớp kinh doanh / ứng dụng / dịch vụ. Nhưng bỏ qua việc chuyển đổi sang giờ địa phương cũng giúp tăng tốc mọi thứ - 50% trở lên trong các thử nghiệm của tôi, xem bên dưới.)

Đây là những gì chúng tôi đã làm trước đây:

DateTime.Parse(dateString, new CultureInfo("en-US"))

Chúng tôi đã định hình ứng dụng và thấy rằng DateTime.Pude thể hiện tỷ lệ sử dụng CPU đáng kể. (Ngẫu nhiên,CultureInfo tạo không phải là một đóng góp đáng kể cho việc sử dụng CPU.)

Vì vậy, tôi đã thiết lập một ứng dụng giao diện điều khiển để phân tích chuỗi ngày / thời gian 10000 lần theo nhiều cách khác nhau. Dòng dưới cùng:
Parse()10 giây
ParseExact()(chuyển đổi sang cục bộ) 20-45 ms
ParseExact()(không chuyển đổi sang cục bộ) 10-15 ms
... và vâng, kết quả Parse()tính bằng giây , trong khi các kết quả khác tính bằng mili giây .


14

Tôi chỉ muốn thêm một lưu ý chung.

Nếu tất cả những gì bạn đang làm là lấy thời gian hiện tại từ đồng hồ bên trong của máy tính để đặt ngày / giờ trên màn hình hoặc báo cáo, thì tất cả đều ổn. Nhưng nếu bạn đang lưu thông tin ngày / giờ để tham khảo sau hoặc đang tính toán ngày / lần, hãy cẩn thận!

Giả sử bạn xác định rằng một tàu du lịch đã đến Honolulu vào ngày 20 tháng 12 năm 2007 lúc 15:00 UTC. Và bạn muốn biết giờ địa phương đó là gì.
1. Có lẽ có ít nhất ba "người dân địa phương" tham gia. Địa phương có thể có nghĩa là Honolulu, hoặc nó có thể có nghĩa là nơi máy tính của bạn được đặt, hoặc nó có thể có nghĩa là vị trí nơi khách hàng của bạn được đặt.
2. Nếu bạn sử dụng các hàm dựng sẵn để thực hiện chuyển đổi, có thể nó sẽ sai. Điều này là do thời gian tiết kiệm ánh sáng ban ngày (có thể) hiện đang có hiệu lực trên máy tính của bạn, nhưng KHÔNG có hiệu lực vào tháng 12. Nhưng Windows không biết điều này ... tất cả những gì nó có là một lá cờ để xác định xem thời gian tiết kiệm ánh sáng ban ngày có hiệu lực hay không. Và nếu nó hiện đang có hiệu lực, thì nó sẽ vui vẻ thêm một giờ thậm chí là một ngày vào tháng 12.
3.Thời gian tiết kiệm ánh sáng ban ngày được thực hiện khác nhau (hoặc hoàn toàn không) trong các phân khu chính trị khác nhau. Đừng nghĩ rằng chỉ vì quốc gia của bạn thay đổi vào một ngày cụ thể, mà các quốc gia khác cũng sẽ như vậy.


6
Trên thực tế, # 2 không hoàn toàn chính xác. Thực tế, có các quy tắc về DST trong mỗi múi giờ mà máy tính của bạn sẽ biết nếu thông tin đã được cài đặt (và cập nhật). Đối với nhiều khu vực, những quy tắc được cố định. Những người khác thực hiện "DST động". Brazil là peeve vật nuôi của tôi cho điều này. Tóm lại, máy tính của bạn có thể hoạt động nếu giờ địa phương của bạn là DST vào tháng 12, giả sử rằng không có thay đổi nào được chuyển thành luật giữa bây giờ và sau đó.
Roger Willcocks

Ngay cả khi bạn không sống ở Brazil, DST vẫn "năng động" ở chỗ các chính trị gia có thể thay đổi nó bất cứ lúc nào (như đã được thực hiện vài năm trước ở Mỹ). Vì hầu hết các phần mềm được viết với mục đích sử dụng trong tương lai, điều quan trọng là phải nhận ra rằng KHÔNG CÓ cách thực tế, có thể dự đoán hoặc thậm chí là lý thuyết để biết quy tắc DST nào sẽ có hiệu lực. Bạn có thể đến gần, nhưng hãy tiết kiệm cho mình một chút thất vọng bằng cách từ bỏ sự hoàn hảo.
DaveWalley


5

Đừng quên nếu bạn đã có một đối tượng DateTime và không chắc đó là UTC hay Local, việc sử dụng trực tiếp các phương thức trên đối tượng là đủ dễ dàng:

DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();

Làm thế nào để chúng ta điều chỉnh cho thêm giờ?

Trừ khi .net được chỉ định sẽ sử dụng cài đặt pc cục bộ. Tôi muốn đọc: http://msdn.microsoft.com/en-us/l Library / system.globalization.daylighttime.aspx

Nhìn bề ngoài, mã có thể trông giống như:

DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );

Và như đã đề cập ở trên, hãy kiểm tra xem múi giờ cài đặt máy chủ của bạn là gì. Có những bài viết trên mạng về cách ảnh hưởng an toàn đến những thay đổi trong IIS.


Hệ thống sẽ giải quyết sự hài hước này cho bạn, miễn là bạn nói cho hệ thống biết 'loại' ngày của bạn là gì (cục bộ / utc / không xác định).
vẽ Noakes

2

Trả lời đề nghị của Dana:

Mẫu mã bây giờ trông như sau:

string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);            
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);

Ngày ban đầu là 20/08/08; loại là UTC.

Cả "convertDate" và "dt" đều giống nhau:

21/08/08 10:00:26; loại là địa phương


Xin vui lòng xem câu trả lời của tôi cho một lời giải thích về điều này.
vẽ Noakes

1

Tôi gặp vấn đề với nó là trong một tập dữ liệu được đẩy qua dây (dịch vụ web đến máy khách) rằng nó sẽ tự động thay đổi vì trường DateType của DataColumn được đặt thành cục bộ. Đảm bảo bạn kiểm tra DateType là gì nếu đẩy DataSets qua.

Nếu bạn không muốn nó thay đổi, hãy đặt nó thành Không xác định


1

Tôi đã gặp câu hỏi này khi tôi gặp vấn đề với ngày UTC mà bạn quay lại thông qua API twitter (trường created_at trên trạng thái); Tôi cần chuyển đổi chúng thành DateTime. Không có câu trả lời / mẫu mã nào trong các câu trả lời trên trang này đủ để ngăn tôi nhận được lỗi "Chuỗi không được công nhận là lỗi DateTime hợp lệ" (nhưng đó là lần gần nhất tôi phải tìm câu trả lời đúng trên SO)

Đăng liên kết này ở đây trong trường hợp điều này giúp người khác - câu trả lời tôi cần đã được tìm thấy trên bài đăng trên blog này: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - về cơ bản sử dụng DateTime.PudeExact với chuỗi định dạng thay vì DateTime.Pude

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.