แปลงเวลา UTC / GMT เป็นเวลาท้องถิ่น


301

เรากำลังพัฒนาแอปพลิเคชัน C # สำหรับไคลเอนต์บริการเว็บ สิ่งนี้จะทำงานบนพีซีที่ใช้ Windows XP

หนึ่งในฟิลด์ที่ส่งคืนโดยบริการเว็บคือฟิลด์ DateTime เซิร์ฟเวอร์จะส่งคืนเขตข้อมูลในรูปแบบ GMT เช่นกับ "Z" ในตอนท้าย

อย่างไรก็ตามเราพบว่า. NET ดูเหมือนว่าจะทำการแปลงโดยนัยบางอย่างและเวลาก็มักจะหมดไป 12 ชั่วโมง

ตัวอย่างโค้ดต่อไปนี้จะแก้ไขสิ่งนี้ในระดับที่ความแตกต่าง 12 ชั่วโมงได้หายไป แต่ไม่มีการตั้งค่าเผื่อสำหรับการปรับเวลาตามฤดูกาลของนิวซีแลนด์

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

ตามไซต์วันที่นี้ :

UTC / GMT ออฟเซ็ต

โซนเวลามาตรฐาน: UTC / GMT +12 ชั่วโมงการ
ปรับเวลาตามฤดูกาล: +1 ชั่วโมง
ออฟเซ็ตเขตเวลาปัจจุบัน: UTC / GMT +13 ชั่วโมง

เราจะปรับชั่วโมงพิเศษได้อย่างไร สามารถทำได้โดยทางโปรแกรมหรือเป็นการตั้งค่าบางอย่างบนพีซีหรือไม่?


2
Zเวลาหมายถึง UTC ไม่ GMT ทั้งสองสามารถแตกต่างกันได้มากถึง 0.9 วินาที
mc0e

คำตอบ:


374

สำหรับสตริงเช่น2012-09-19 01:27:30.000, DateTime.Parseไม่สามารถบอกได้ว่าโซนเวลาวันที่และเวลาจะมาจาก

DateTimeมีคุณสมบัติชนิดซึ่งสามารถมีตัวเลือกโซนเวลาหนึ่งในสามตัวเลือก:

  • ยังไม่ระบุ
  • ในประเทศ
  • Utc

หมายเหตุ หากคุณกำลังคิดที่จะเป็นตัวแทนวัน / เวลาอื่นที่ไม่ใช่ UTC DateTimeOffsetหรือโซนเวลาท้องถิ่นของคุณแล้วคุณควรใช้


ดังนั้นสำหรับรหัสในคำถามของคุณ:

DateTime convertedDate = DateTime.Parse(dateStr);

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

คุณบอกว่าคุณรู้ว่ามันเป็นประเภทไหนดังนั้นบอกมัน

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

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

ตอนนี้เมื่อระบบทราบในเวลา UTC คุณสามารถโทรToLocalTime:

DateTime dt = convertedDate.ToLocalTime();

สิ่งนี้จะให้ผลลัพธ์ที่คุณต้องการ


19
เป็นอีกวิธีหนึ่งในการระบุชนิด:DateTime convertedTime = new DateTime(DateTime.Parse(dateStr).Ticks), DateTimeKind.Utc);
แบรด

มันไม่ได้เป็น ToLocalTime ()? @Brad - คำอุปมาของคุณไม่ตรงกัน
TrueWill

2
โซลูชันนี้บัญชีเพื่อประหยัดเวลากลางวันหรือไม่ เมื่อฉันลองฉันใช้งานไม่ถึงชั่วโมง
บ็อบฮอร์น

7
ขั้นตอนของการเปลี่ยนแปลงKindของการDateTimeจากUnspecifiedไปUTCไม่จำเป็น Unspecifiedจะถือว่ามีUTCวัตถุประสงค์เพื่อToLocalTime: msdn.microsoft.com/en-us/library/ …
CJ7

16
@ CJ7: ใช่ แต่การมีความชัดเจนมีประโยชน์อย่างยิ่งต่อผู้พัฒนารายอื่นที่อาจต้องบำรุงรักษาโค้ด
Ryan

121

ฉันจะดูการใช้คลาส System.TimeZoneInfo ถ้าคุณอยู่ใน. NET 3.5 ดูhttp://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx สิ่งนี้ควรคำนึงถึงการเปลี่ยนแปลงการออมในเวลากลางวันอย่างถูกต้อง

// 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);

หากคุณทำงานในเขตเวลาของคุณเอง (en-NZ ในกรณีนี้) คุณไม่จำเป็นต้องจัดการกับ TimeZoneInfo มันเป็นเพียงความซับซ้อนที่ไม่จำเป็น ดูคำตอบของฉันสำหรับรายละเอียดเพิ่มเติม
Drew Noakes

11
และถ้าใครต้องการนี่คือรายการของเขตเวลาที่ฉันได้พบกับ TimeZoneInfo.FindSystemTimeZoneById - codeproject.com/Messages/3867850/ …
nikib3ro

ยอดเยี่ยม! ขอบคุณสำหรับโพสต์นี้ ฉันกำลังมองหาการแก้ไขนี้เป็นเวลา 3 วัน
เควินมัวร์

58
TimeZone.CurrentTimeZone.ToLocalTime(date);

8
มันจะทำงานได้ก็ต่อเมื่อระบบรู้ว่าวันที่ถูกแปลงเป็น UTC โปรดดูคำตอบของฉัน
Drew Noakes

1
แต่ UTC เป็นค่าเริ่มต้นใช่ไหม ดังนั้นจึงใช้งานได้กับ "ไม่ได้รับการระบุ" เช่นเดียวกับคำตอบของ CJ7
NickG

25

DateTimeวัตถุที่มีKindการUnspecifiedไปโดยปริยายซึ่งสำหรับวัตถุประสงค์ของการที่จะถือว่าToLocalTimeUTC

เพื่อให้ได้เวลาท้องถิ่นของUnspecified DateTimeวัตถุคุณเพียงแค่ทำสิ่งนี้:

convertedDate.ToLocalTime();

ขั้นตอนของการเปลี่ยนแปลงKindของการDateTimeจากUnspecifiedไปUTCไม่จำเป็น Unspecifiedจะถือว่ามีUTCวัตถุประสงค์เพื่อToLocalTime: http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx


6
และในทางกลับกัน: จะแปลงconvertedDate.FromLocalTime(); UTC
R. Schreurs

16

ฉันรู้ว่านี่เป็นคำถามที่เก่ากว่า แต่ฉันพบสถานการณ์ที่คล้ายกันและฉันต้องการแบ่งปันสิ่งที่ฉันได้พบกับผู้ค้นหาในอนาคตอาจรวมถึงตัวเองด้วย :)

DateTime.Parse()อาจเป็นเรื่องยุ่งยาก - ดูตัวอย่างที่นี่

หากDateTimeมาจากบริการเว็บหรือแหล่งอื่นที่มีรูปแบบที่รู้จักคุณอาจต้องการพิจารณาบางสิ่งเช่น

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

หรือดีกว่านั้น

DateTime.TryParseExact(...)

การAssumeUniversalตั้งค่าสถานะบอก parser ว่าวันที่ / เวลา UTC แล้ว การรวมกันของAssumeUniversalและAdjustToUniversalบอกไม่ให้แปลงผลลัพธ์เป็นเวลา "ท้องถิ่น" ซึ่งจะพยายามทำตามค่าเริ่มต้น (โดยส่วนตัวแล้วฉันพยายามที่จะจัดการกับ UTC ในชั้นธุรกิจ / แอพพลิเคชั่น / บริการอย่างไรก็ตามการเลี่ยงการแปลงเป็นเวลาท้องถิ่นยังช่วยเพิ่มความเร็วในการทดสอบได้ถึง 50% หรือมากกว่าในการทดสอบของฉันดูด้านล่าง)

นี่คือสิ่งที่เราเคยทำมาก่อน:

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

เราได้ทำโปรไฟล์แอปและพบว่า DateTime.Parse เป็นตัวแทนของการใช้งาน CPU อย่างมีนัยสำคัญ (บังเอิญผู้CultureInfoสร้างไม่ได้เป็นผู้สนับสนุนที่สำคัญต่อการใช้งาน CPU)

ดังนั้นฉันจึงตั้งค่าแอพคอนโซลให้แจงสตริงวันที่ / เวลา 10,000 ครั้งในหลากหลายวิธี บรรทัดด้านล่าง:
Parse()10 วินาที
ParseExact()(แปลงไปเป็นท้องถิ่น) 20-45 มิลลิวินาที
ParseExact()(ไม่ได้แปลงไปเป็นท้องถิ่น) 10-15 มิลลิวินาที
... และใช่ผลลัพธ์ของการParse()อยู่ในวินาทีในขณะที่คนอื่น ๆ ที่อยู่ในมิลลิวินาที


14

ฉันต้องการเพิ่มข้อควรระวังทั่วไป

หากสิ่งที่คุณกำลังทำคือการได้รับเวลาปัจจุบันจากนาฬิกาภายในของคอมพิวเตอร์เพื่อวางวันที่ / เวลาบนจอแสดงผลหรือรายงานแล้วทั้งหมดก็เป็นไปด้วยดี แต่ถ้าคุณบันทึกข้อมูลวันที่ / เวลาไว้อ้างอิงในภายหลังหรือคำนวณวันที่ / เวลาระวัง!

สมมติว่าคุณเห็นว่าเรือสำราญมาถึงโฮโนลูลูในวันที่ 20 ธันวาคม 2550 เวลา 15:00 น. UTC และคุณต้องการที่จะรู้ว่าเวลาท้องถิ่นที่เป็น
1.อาจมีคนท้องถิ่นอย่างน้อยสามคนที่เกี่ยวข้อง ท้องถิ่นอาจหมายถึงโฮโนลูลูหรืออาจหมายถึงที่ตั้งคอมพิวเตอร์ของคุณหรืออาจหมายถึงที่ตั้งของลูกค้าของคุณ
2.หากคุณใช้ฟังก์ชั่นในตัวเพื่อทำการแปลงมันอาจจะผิด นี่เป็นเพราะในปัจจุบันการปรับเวลาตามฤดูกาล (อาจ) มีผลกับคอมพิวเตอร์ของคุณ แต่ไม่มีผลในเดือนธันวาคม แต่ Windows ไม่ทราบว่าสิ่งนี้ ... ทั้งหมดที่มีคือการตั้งค่าสถานะเดียวเพื่อตรวจสอบว่าเวลาออมแสงในปัจจุบันมีผลใช้งานหรือไม่ และหากมีผลบังคับใช้อยู่ในขณะนั้นจะเพิ่มชั่วโมงอย่างมีความสุขแม้กระทั่งวันที่ในเดือนธันวาคม
3การปรับเวลาตามฤดูกาลให้แตกต่างกัน (หรือไม่เลย) ในเขตการปกครองต่างๆ อย่าคิดว่าเพียงเพราะประเทศของคุณเปลี่ยนแปลงในวันที่กำหนดประเทศอื่น ๆ ก็จะเช่นกัน


6
จริงๆแล้ว # 2 นั้นไม่ถูกต้องทั้งหมด ในความเป็นจริงมีกฎเกณฑ์เกี่ยวกับ DST ในทุกเขตเวลาที่คอมพิวเตอร์ของคุณจะรู้ว่าข้อมูลนั้นได้รับการติดตั้งแล้วหรือยัง สำหรับหลาย ๆ โซนกฎเหล่านั้นจะได้รับการแก้ไข อื่น ๆ ใช้ "ไดนามิก DST" บราซิลเป็นสัตว์เลี้ยงของฉันสำหรับสิ่งนี้ โดยสรุปแล้ว coputer ของคุณสามารถทำงานได้หากเวลาท้องถิ่นของคุณเป็น DST ในเดือนธันวาคมสมมติว่าไม่มีการเปลี่ยนแปลงกฎหมายในขณะนี้
Roger Willcocks

แม้ว่าคุณจะไม่ได้อาศัยอยู่ในบราซิล DST นั้นเป็น "ไดนามิก" ในนักการเมืองที่สามารถเปลี่ยนแปลงได้ตลอดเวลา (เหมือนที่เคยทำเมื่อไม่กี่ปีที่ผ่านมาในสหรัฐอเมริกา) เนื่องจากซอฟต์แวร์ส่วนใหญ่เขียนขึ้นโดยคำนึงถึงการใช้งานในอนาคตเป็นสิ่งสำคัญที่ต้องตระหนักว่าไม่มีวิธีการปฏิบัติคาดเดาหรือแม้แต่ทางทฤษฎีในการรู้ว่ากฎ DST ใดจะมีผลบังคับใช้ คุณสามารถเข้าใกล้ได้ แต่ช่วยตัวเองให้หงุดหงิดด้วยการยอมแพ้กับความสมบูรณ์แบบ
DaveWalley


5

อย่าลืมว่าคุณมีวัตถุ DateTime อยู่แล้วและไม่แน่ใจว่าเป็น UTC หรือ Local เป็นเรื่องง่ายพอที่จะใช้วิธีการกับวัตถุโดยตรง:

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

เราจะปรับชั่วโมงพิเศษได้อย่างไร

เว้นแต่ระบุ. net จะใช้การตั้งค่าพีซีในระบบ ฉันได้อ่าน: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx

โดยดูรหัสอาจมีลักษณะเช่น:

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

และตามที่กล่าวไว้ข้างต้นให้ตรวจสอบการตั้งค่าเขตเวลาที่เซิร์ฟเวอร์ของคุณเปิด มีบทความในสุทธิสำหรับวิธีปลอดภัยส่งผลต่อการเปลี่ยนแปลงใน IIS


ระบบจะจัดการกับความละเอียดอ่อนนี้สำหรับคุณหากคุณแจ้งให้ระบบทราบว่าวันที่ของคุณเป็นแบบไหน
Drew Noakes

2

ในการตอบข้อเสนอแนะของ Dana:

ตัวอย่างโค้ดตอนนี้ดูเหมือนว่า:

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

วันที่ดั้งเดิมคือ 20/08/08; ชนิดคือ UTC

ทั้ง "convertDate" และ "dt" เหมือนกัน:

21/08/08 10:00:26 น. ชนิดคือท้องถิ่น


โปรดดูคำตอบของฉันสำหรับคำอธิบายนี้
Drew Noakes

1

ฉันมีปัญหากับมันอยู่ในชุดข้อมูลที่ถูกส่งข้ามสาย (บริการเว็บไปยังไคลเอนต์) ว่ามันจะเปลี่ยนโดยอัตโนมัติเพราะฟิลด์ DateType ของ DataColumn ถูกตั้งค่าเป็นท้องถิ่น ตรวจสอบให้แน่ใจว่าคุณตรวจสอบว่า DateType คืออะไรถ้าคุณผลักชุดข้อมูลไป

หากคุณไม่ต้องการให้เปลี่ยนให้ตั้งเป็นไม่ระบุ


1

ฉันเจอคำถามนี้เมื่อฉันมีปัญหากับวันที่ UTC ที่คุณได้รับกลับมาผ่านทาง twitter API (ฟิลด์ created_at ในสถานะ); ฉันต้องการแปลงเป็น DateTime ไม่มีคำตอบ / ตัวอย่างโค้ดในคำตอบในหน้านี้เพียงพอที่จะหยุดฉันได้รับข้อผิดพลาด "String ไม่ได้รับการยอมรับว่าเป็น DateTime ที่ถูกต้อง" (แต่มันใกล้เคียงที่สุดที่ฉันต้องค้นหาคำตอบที่ถูกต้องใน SO)

การโพสต์ลิงค์นี้ที่นี่ในกรณีนี้ช่วยคนอื่น - คำตอบที่ฉันต้องการพบในโพสต์บล็อกนี้: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - โดยทั่วไปใช้ DateTime.ParseExact ด้วยสตริงรูปแบบแทน DateTime.Parse

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.