งานถูกต้องตามวันและเวลา การลบการชดเชยเขตเวลาออกจาก DateTimeOffset วันที่เป็นศูนย์ชดเชยเซิร์ฟเวอร์ sql

ปัญหาไม่เกี่ยวข้องกับฐานข้อมูล หากคุณตั้งค่าเบรกพอยต์หรือป้อนเอาต์พุตที่ไหนสักแห่ง คุณจะเห็นออฟเซ็ตถูกหักหลังจากโค้ดนี้ไม่นาน:

TestDateAndTime = testDateAndTime.DateTime.Date;

มาทำลายมันกัน:

  • คุณเริ่มต้นด้วยค่า DateTimeOffset 2008-05-01T08:06:32+01:00
  • จากนั้นคุณเรียกว่า .DateTime ซึ่งส่งผลให้ค่า DateTime 2008-05-01T08:06:32 พร้อมด้วย DateTimeKind.Unspecified
  • จากนั้นคุณเรียกว่า .Date ซึ่งส่งผลให้ค่า DateTime เป็น 2008-05-01T00:00:00 พร้อมด้วย DateTimeKind.Unspecified
  • คุณกำหนดผลลัพธ์ให้กับ testDateAndTime ซึ่งเป็นประเภท DateTimeOffset สิ่งนี้ทำให้เกิดการส่งโดยนัยจาก DateTime ถึง DateTimeOffset - ซึ่งใช้บังคับ ท้องถิ่นเขตเวลา- ในกรณีของคุณ ปรากฏว่าค่าชดเชยสำหรับค่านี้ในเขตเวลาท้องถิ่นของคุณคือ -04:00 ดังนั้นค่าผลลัพธ์คือ DateTimeOffset ของ 2008-05-01T00:00:00-04:00 ตามที่คุณอธิบายไว้

คุณพูดว่า:

เป้าหมายสูงสุดคือการมีวันที่โดยไม่มีการชดเชยเวลาหรือเขตเวลา

ก็มีนะ ตอนนี้ไม่ใช่ประเภทข้อมูล C# ดั้งเดิมซึ่งเป็นเพียงวันที่ที่ไม่มีเวลา มีประเภท Date ล้วนๆ เวลาของระบบใน corefxlab แต่ยังไม่พร้อมสำหรับแอปพลิเคชันการผลิตทั่วไป มี LocalDate ในไลบรารี Noda Time ที่คุณสามารถใช้ได้ในปัจจุบัน แต่คุณยังคงต้องแปลงกลับเป็นประเภทเนทิฟก่อนที่จะบันทึกลงในฐานข้อมูล ดังนั้นในระหว่างนี้ สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือ:

  • เปลี่ยน SQL Server ของคุณเพื่อใช้ประเภทวันที่ในฟิลด์
  • ในโค้ด .NET ของคุณ ให้ใช้ DateTime พร้อมเวลา 00:00:00 และ DateTimeKind.Unspecified คุณต้องจำไว้ว่าอย่าเพิกเฉยต่อส่วนของเวลา (เนื่องจากมีวันที่ที่ไม่มีเที่ยงคืนท้องถิ่นในบางโซนเวลา)
  • เปลี่ยนเสาทดสอบให้เป็น DateTime แทนที่จะเป็น DateTimeOffset

โดยทั่วไป ในขณะที่ DateTimeOffset จะเหมาะสมกับสถานการณ์จำนวนมาก (เช่น การประทับเวลาเหตุการณ์) จึงไม่เหมาะกับค่าเฉพาะวันที่เท่านั้น

ฉันต้องการ วันที่ปัจจุบันโดยมีค่าชดเชยเป็นศูนย์

ถ้าคุณ ต้องการจริงๆมันเหมือนกับ DateTimeOffset คุณสามารถทำได้:

TestDateAndTime = DateTimeOffset ใหม่ (testDateAndTime.Date, TimeSpan.Zero);

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

เกี่ยวกับคำถามเพิ่มเติมที่ถูกถามในบอร์ดของคุณ การระบุ DateTimeKind.Utc จะเปลี่ยนพฤติกรรมของการส่งโดยนัย แทนที่จะใช้เขตเวลาท้องถิ่น ระบบจะใช้เวลา UTC ซึ่งมีออฟเซ็ตเป็นศูนย์เสมอ ผลลัพธ์จะเหมือนกับมุมมองที่ชัดเจนยิ่งขึ้นที่ฉันให้ไว้ข้างต้น ฉันยังคงแนะนำสิ่งนี้ด้วยเหตุผลเดียวกัน

ลองพิจารณาตัวอย่างที่เริ่มต้นด้วย 2016-12-31T22:00:00-04:00 ตามแนวทางของคุณ คุณควรจัดเก็บ 2016-12-31T00:00:00+00:00 ไว้ในฐานข้อมูล อย่างไรก็ตาม นี่เป็นสองจุดที่แตกต่างกันของเวลา ครั้งแรกที่ปรับมาตรฐานเป็น UTC จะเป็น 2017-01-01T02:00:00+00:00 และครั้งที่สองซึ่งแปลงเป็นเขตเวลาอื่นจะเป็น 2016-12-30T20:00:00-04:00 โปรดทราบการเปลี่ยนแปลงวันที่ในการแปลง นี่อาจไม่ใช่พฤติกรรมที่คุณต้องการในแอปพลิเคชันของคุณ

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

ตามหลักเหตุผลแล้ว สามารถแยกแยะค่าประเภทต่อไปนี้ที่เกี่ยวข้องกับวันที่และเวลาได้:


พิจารณาแต่ละประเด็นแยกกันโดยไม่ลืม

วันและเวลา

สมมติว่าห้องปฏิบัติการที่รวบรวมวัสดุสำหรับการวิเคราะห์ตั้งอยู่ในโซนเวลา +2 และสาขากลางซึ่งตรวจสอบความสมบูรณ์ของการทดสอบตามกำหนดเวลานั้นอยู่ในโซน +1 เวลาที่ระบุในตัวอย่างจะถูกบันทึกไว้เมื่อห้องปฏิบัติการแห่งแรกเก็บวัสดุ คำถามเกิดขึ้นว่าสำนักงานกลางควรดูเวลาใด? เห็นได้ชัดว่าซอฟต์แวร์ สำนักงานกลางควรแสดงวันที่ 15 มกราคม 2014 เวลา 12:17:15 น. - น้อยกว่าหนึ่งชั่วโมง เนื่องจากจากการดูเหตุการณ์ที่เกิดขึ้นในขณะนั้น

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

  1. ค่าถูกสร้างขึ้นบนไคลเอนต์ เช่น 2 มีนาคม 2016 15 :13:36 ลูกค้าอยู่ในโซนเวลา +2
  2. ค่าจะถูกแปลงเป็นการแสดงสตริงสำหรับการส่งไปยังเซิร์ฟเวอร์ - “2016-03-02T 15 :13:36+02:00”.
  3. ข้อมูลซีเรียลไลซ์จะถูกส่งไปยังเซิร์ฟเวอร์
  4. เซิร์ฟเวอร์จะทำการดีซีเรียลไลซ์เวลาเป็นออบเจ็กต์วันที่/เวลา และนำไปยังโซนเวลาปัจจุบัน ตัวอย่างเช่น หากเซิร์ฟเวอร์ทำงานที่ +1 ออบเจ็กต์จะมีวันที่ 2 มีนาคม 2016 14 :13:36.
  5. เซิร์ฟเวอร์บันทึกข้อมูลลงในฐานข้อมูล แต่ไม่มีข้อมูลใด ๆ เกี่ยวกับเขตเวลา - ประเภทวันที่/เวลาที่ใช้บ่อยที่สุดนั้นไม่รู้อะไรเลยเกี่ยวกับเขตเวลา ดังนั้นวันที่ 2 มีนาคม 2559 จะถูกบันทึกไว้ในฐานข้อมูล 14 :13:36 ในเขตเวลา "ไม่ทราบ"
  6. เซิร์ฟเวอร์อ่านข้อมูลจากฐานข้อมูลและสร้างออบเจ็กต์ที่เกี่ยวข้องด้วยค่า 2 มีนาคม 2016 14 :13:36. และเนื่องจากเซิร์ฟเวอร์ทำงานในเขตเวลา +1 ค่านี้จะถูกตีความภายในเขตเวลาเดียวกันด้วย
  7. ค่าจะถูกแปลงเป็นการแสดงสตริงสำหรับการส่งไปยังไคลเอนต์ - “2016-03-02T 14 :13:36+01:00”.
  8. ข้อมูลซีเรียลไลซ์จะถูกส่งไปยังไคลเอนต์
  9. ไคลเอนต์ทำการดีซีเรียลไลซ์ค่าที่ได้รับไปเป็นออบเจ็กต์วันที่/เวลา โดยส่งไปยังโซนเวลาปัจจุบัน ตัวอย่างเช่น หากเป็น -5 ค่าที่แสดงควรเป็นวันที่ 2 มีนาคม 2016 09 :13:36.
ดูเหมือนทุกอย่างจะสมบูรณ์ แต่ลองคิดถึงสิ่งที่อาจผิดพลาดในกระบวนการนี้ ที่จริงแล้วปัญหาที่นี่สามารถเกิดขึ้นได้เกือบทุกขั้นตอน
  • เวลาบนไคลเอนต์สามารถสร้างขึ้นได้โดยไม่ต้องมีเขตเวลาเลย - ตัวอย่างเช่น ประเภท DateTime ใน .NET ที่มี DateTimeKind.Unspecified
  • เอ็นจินการทำให้เป็นอนุกรมอาจใช้รูปแบบที่ไม่รวมออฟเซ็ตโซนเวลา
  • เมื่อทำการดีซีเรียลไลซ์ลงในออบเจ็กต์ คุณสามารถละเว้นออฟเซ็ตโซนเวลาได้ โดยเฉพาะในดีซีเรียลไลเซอร์ "แบบโฮมเมด" ทั้งบนเซิร์ฟเวอร์และบนไคลเอนต์
  • เมื่ออ่านจากฐานข้อมูล คุณสามารถสร้างออบเจ็กต์วันที่/เวลาได้โดยไม่ต้องระบุเขตเวลาเลย ตัวอย่างเช่น ประเภท DateTime ใน .NET พร้อมด้วย DateTimeKind.Unspecified ยิ่งไปกว่านั้น ในทางปฏิบัติด้วย DateTime ใน .NET นี่คือสิ่งที่เกิดขึ้นอย่างแน่นอนหากคุณไม่ได้ระบุ DateTimeKind อื่นอย่างชัดเจนทันทีหลังจากการพิสูจน์อักษร
  • หากแอปพลิเคชันเซิร์ฟเวอร์ที่ทำงานกับฐานข้อมูลทั่วไปตั้งอยู่ในเขตเวลาที่แตกต่างกัน จะเกิดความสับสนอย่างมากในการชดเชยเวลา ค่าวันที่/เวลาที่เขียนไปยังฐานข้อมูลโดยเซิร์ฟเวอร์ A และอ่านโดยเซิร์ฟเวอร์ B จะแตกต่างอย่างเห็นได้ชัดจากค่าเดิมเดียวกันที่เขียนโดยเซิร์ฟเวอร์ B และอ่านโดยเซิร์ฟเวอร์ A
  • การถ่ายโอนเซิร์ฟเวอร์แอปพลิเคชันจากโซนหนึ่งไปยังอีกโซนหนึ่งจะนำไปสู่การตีความค่าวันที่/เวลาที่เก็บไว้แล้วไม่ถูกต้อง
แต่ข้อเสียเปรียบที่ร้ายแรงที่สุดในห่วงโซ่ที่อธิบายไว้ข้างต้นคือการใช้เขตเวลาท้องถิ่นบนเซิร์ฟเวอร์ หากไม่มีการเปลี่ยนแปลงเป็นเวลาฤดูร้อน/ฤดูหนาว ก็จะไม่มีปัญหาเพิ่มเติม แต่อย่างอื่นคุณอาจได้รับความประหลาดใจอันไม่พึงประสงค์มากมาย

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

โดยคำนึงถึงข้อควรพิจารณาที่อธิบายไว้ข้างต้น เราจะกำหนดวิธีการส่งและจัดเก็บเวลาที่เชื่อถือได้และง่ายที่สุด: บนเซิร์ฟเวอร์และในฐานข้อมูลค่าทั้งหมดจะต้องถูกแปลงเป็นเขตเวลา UTC.

มาดูกันว่ากฎนี้ให้อะไรเราบ้าง:

  • เมื่อส่งข้อมูลไปยังเซิร์ฟเวอร์ ไคลเอนต์จะต้องส่งค่าชดเชยโซนเวลาเพื่อให้เซิร์ฟเวอร์สามารถแปลงเวลาเป็น UTC ได้อย่างถูกต้อง อีกทางเลือกหนึ่งคือการบังคับให้ลูกค้าทำการแปลงนี้ แต่ตัวเลือกแรกมีความยืดหยุ่นมากกว่า เมื่อรับข้อมูลกลับจากเซิร์ฟเวอร์ ไคลเอนต์จะแปลงวันที่และเวลาเป็นเขตเวลาท้องถิ่น โดยรู้ว่าไม่ว่าในกรณีใด ลูกค้าจะได้รับเวลาใน UTC
  • ไม่มีการเปลี่ยนแปลงระหว่างเวลาฤดูร้อนและฤดูหนาวใน UTC ดังนั้นปัญหาที่เกี่ยวข้องกับสิ่งนี้จะไม่เกี่ยวข้อง
  • บนเซิร์ฟเวอร์ เมื่ออ่านจากฐานข้อมูล คุณไม่จำเป็นต้องแปลงค่าเวลา คุณเพียงแค่ต้องระบุอย่างชัดเจนว่าสอดคล้องกับ UTC ตัวอย่างเช่น ใน .NET สามารถทำได้โดยการตั้งค่า DateTimeKind บนวัตถุเวลาเป็น DateTimeKind.Utc
  • ความแตกต่างในเขตเวลาระหว่างเซิร์ฟเวอร์ที่ทำงานกับฐานข้อมูลทั่วไปตลอดจนการถ่ายโอนเซิร์ฟเวอร์จากโซนหนึ่งไปยังอีกโซนหนึ่งจะไม่ส่งผลกระทบต่อความถูกต้องของข้อมูลที่ได้รับ แต่อย่างใด
หากต้องการนำกฎดังกล่าวไปใช้ ก็เพียงพอที่จะดูแลสามสิ่ง:
  1. สร้างกลไกการทำให้ซีเรียลไลซ์และดีซีเรียลไลซ์เพื่อให้ค่าวันที่/เวลาถูกแปลอย่างถูกต้องจาก UTC ไปเป็นโซนเวลาท้องถิ่นและย้อนกลับ
  2. ตรวจสอบให้แน่ใจว่าดีซีเรียลไลเซอร์ฝั่งเซิร์ฟเวอร์สร้างวัตถุวันที่/เวลาใน UTC
  3. ตรวจสอบให้แน่ใจว่าเมื่ออ่านจากฐานข้อมูล ออบเจ็กต์วันที่/เวลาจะถูกสร้างขึ้นในรูปแบบ UTC บางครั้งรายการนี้จัดทำโดยไม่มีการเปลี่ยนแปลงรหัส เพียงแต่เขตเวลาของระบบบนเซิร์ฟเวอร์ทั้งหมดจะถูกตั้งค่าเป็น UTC
ข้อควรพิจารณาและคำแนะนำข้างต้นใช้งานได้ดี เมื่อเงื่อนไขสองข้อมารวมกัน:
  • ความต้องการของระบบไม่ต้องการให้แสดงเวลาท้องถิ่นและ/หรือโซนเวลาตามที่จัดเก็บไว้ทุกประการ ตัวอย่างเช่น ตั๋วเครื่องบินจะต้องพิมพ์เวลาออกเดินทางและมาถึงในเขตเวลาที่สอดคล้องกับที่ตั้งสนามบิน หรือหากเซิร์ฟเวอร์ส่งใบแจ้งหนี้การพิมพ์ที่สร้างขึ้นในประเทศต่างๆ แต่ละใบแจ้งหนี้ควรลงเอยด้วยเวลาท้องถิ่น และไม่แปลงเป็นโซนเวลาของเซิร์ฟเวอร์
  • ค่าวันที่และเวลาทั้งหมดในระบบเป็น "สัมบูรณ์" - เช่น อธิบายช่วงเวลาในอนาคตหรืออดีตที่สอดคล้องกับค่าเดียวใน UTC ตัวอย่างเช่น “ยานพาหนะส่งเกิดขึ้นเวลา 23:00 น. ตามเวลาเคียฟ” หรือ “การประชุมจะเกิดขึ้นระหว่างเวลา 13:30 น. ถึง 14:30 น. ตามเวลามินสค์” ตัวเลขสำหรับกิจกรรมเหล่านี้จะแตกต่างกันในเขตเวลาที่ต่างกัน แต่จะอธิบายในช่วงเวลาเดียวกัน แต่อาจเกิดขึ้นได้ว่าข้อกำหนดสำหรับ ซอฟต์แวร์หมายถึงเวลาท้องถิ่นแบบ "สัมพันธ์" ในบางกรณี เช่น “รายการโทรทัศน์นี้จะออกอากาศเวลา 9.00 – 10.00 น. ในทุกประเทศที่มีช่องทีวีในเครือ” ปรากฎว่าการออกอากาศรายการไม่ใช่เหตุการณ์เดียว แต่มีหลายเหตุการณ์ และอาจเกิดขึ้นทั้งหมดในช่วงเวลาที่ต่างกันในระดับ "สัมบูรณ์"
สำหรับกรณีที่มีการละเมิดเงื่อนไขแรก ปัญหาสามารถแก้ไขได้โดยใช้ชนิดข้อมูลที่มีเขตเวลา - ทั้งในเซิร์ฟเวอร์และในฐานข้อมูล ด้านล่างนี้คือรายการตัวอย่างเล็กๆ น้อยๆ สำหรับแพลตฟอร์มและ DBMS ที่แตกต่างกัน
.สุทธิ วันที่และเวลาออฟเซ็ต
ชวา org.joda.time.DateTime, java.time.ZonedDateTime
เอ็มเอสเอสแอล วันที่และเวลาชดเชย
ออราเคิล, PostgreSQL ประทับเวลาพร้อมโซนเวลา
MySQL

การละเมิดเงื่อนไขที่สองถือเป็นกรณีที่ซับซ้อนกว่า หากจำเป็นต้องจัดเก็บเวลา "สัมพัทธ์" นี้ไว้เพื่อแสดงเพียงอย่างเดียว และไม่มีงานใดที่จะระบุช่วงเวลาที่ "สัมบูรณ์" ในเวลาที่เหตุการณ์เกิดขึ้นหรือจะเกิดขึ้นในเขตเวลาที่กำหนด ก็เพียงพอที่จะปิดใช้งานการแปลงเวลาเพียงอย่างเดียว ตัวอย่างเช่น ผู้ใช้เข้าสู่การเริ่มต้นโปรแกรมสำหรับทุกสาขาของบริษัทโทรทัศน์ในวันที่ 25 มีนาคม 2559 เวลา 9:00 น. และรายการจะถูกส่ง จัดเก็บ และแสดงในรูปแบบนี้ แต่อาจเกิดขึ้นที่ตัวกำหนดเวลาบางตัวควรดำเนินการพิเศษโดยอัตโนมัติหนึ่งชั่วโมงก่อนเริ่มแต่ละโปรแกรม (ส่งการแจ้งเตือนหรือตรวจสอบการมีอยู่ของข้อมูลบางอย่างในฐานข้อมูลของบริษัททีวี) การใช้ตัวกำหนดเวลาดังกล่าวอย่างน่าเชื่อถือไม่ใช่เรื่องเล็กน้อย สมมติว่าผู้จัดกำหนดการรู้ว่าแต่ละสาขาอยู่ในเขตเวลาใด และหนึ่งในประเทศที่มีสาขาตัดสินใจเปลี่ยนเขตเวลาภายหลังเวลาผ่านไประยะหนึ่ง กรณีนี้ไม่ได้เกิดขึ้นได้ยากอย่างที่คิด - ตลอดทั้งนี้และเมื่อสองปีก่อน ฉันนับเหตุการณ์ที่คล้ายกันได้มากกว่า 10 เหตุการณ์ (http://www.timeanddate.com/news/time/) ปรากฎว่าผู้ใช้คนใดคนหนึ่งต้องปรับปรุงการเชื่อมโยงเขตเวลาให้ทันสมัยอยู่เสมอ หรือผู้กำหนดตารางเวลาจะต้องนำข้อมูลนี้มาจากแหล่งที่มาทั่วโลก เช่น Google Maps API โซนเวลา ฉันไม่รับปากที่จะเสนอวิธีแก้ปัญหาที่เป็นสากลสำหรับกรณีดังกล่าว ฉันเพียงแต่จะทราบว่าสถานการณ์ดังกล่าวจำเป็นต้องได้รับการศึกษาอย่างจริงจัง

ดังที่เห็นได้จากข้างต้น ไม่มีแนวทางเดียวที่ครอบคลุมคดีความได้ 100%- ดังนั้น อันดับแรกคุณต้องเข้าใจอย่างชัดเจนจากข้อกำหนดว่าสถานการณ์ใดที่กล่าวมาข้างต้นจะเกิดขึ้นในระบบของคุณ เป็นไปได้มากว่าทุกอย่างจะถูกจำกัดอยู่เพียงแนวทางแรกที่เสนอพร้อมพื้นที่จัดเก็บใน UTC สถานการณ์พิเศษที่อธิบายไว้ไม่ได้ยกเลิก แต่เพียงเพิ่มวิธีแก้ไขปัญหาอื่น ๆ สำหรับกรณีพิเศษ

วันที่ไม่มีเวลา

สมมติว่าเราได้จัดเรียงการแสดงวันที่และเวลาที่ถูกต้องโดยคำนึงถึงเขตเวลาของลูกค้า มาดูวันที่ไม่มีเวลาและตัวอย่างที่ให้ไว้สำหรับกรณีนี้ตั้งแต่ต้น - "สัญญาใหม่มีผลบังคับใช้ในวันที่ 2 กุมภาพันธ์ 2559" จะเกิดอะไรขึ้นหากใช้ประเภทเดียวกันและกลไกเดียวกันสำหรับค่าเช่นวันที่และเวลา "ปกติ"?

ไม่ใช่ทุกแพลตฟอร์ม ภาษา และ DBMS จะมีประเภทเฉพาะวันที่เท่านั้น ตัวอย่างเช่น ใน .NET มีเพียงประเภท DateTime เท่านั้น ไม่มีประเภท "just Date" แยกต่างหาก แม้ว่าจะระบุเฉพาะวันที่เมื่อสร้างออบเจ็กต์ดังกล่าว แต่เวลายังคงอยู่และเท่ากับ 00:00:00 น. หากเราโอนค่า “2 กุมภาพันธ์ 2559 00:00:00” จากโซนที่มีการชดเชย +2 ถึง +1 เราจะได้รับ “1 กุมภาพันธ์ 2559 23:00:00” สำหรับตัวอย่างข้างต้น จะเทียบเท่ากับสัญญาใหม่ที่เริ่มในวันที่ 2 กุมภาพันธ์ในเขตเวลาหนึ่ง และในวันที่ 1 กุมภาพันธ์ในอีกเขตเวลาหนึ่ง จากมุมมองทางกฎหมาย สิ่งนี้ไร้สาระและแน่นอนว่าไม่ควรเป็นเช่นนั้น กฎทั่วไปสำหรับวันที่ "บริสุทธิ์" นั้นง่ายมาก - ไม่ควรแปลงค่าดังกล่าวในขั้นตอนการบันทึกและการอ่านใด ๆ

มีหลายวิธีในการหลีกเลี่ยงการแปลงวันที่:

  • หากแพลตฟอร์มรองรับประเภทที่แสดงวันที่โดยไม่มีเวลา ก็ควรใช้สิ่งนั้น
  • เพิ่มแอตทริบิวต์พิเศษให้กับข้อมูลเมตาของออบเจ็กต์ที่จะบอกซีเรียลไลเซอร์ว่าควรละเว้นเขตเวลาสำหรับค่าที่กำหนด
  • ส่งผ่านวันที่จากไคลเอนต์และย้อนกลับเป็นสตริง และจัดเก็บเป็นวันที่ วิธีการนี้ไม่สะดวกหากคุณไม่เพียงแต่ต้องแสดงวันที่บนไคลเอนต์เท่านั้น แต่ยังต้องดำเนินการบางอย่างด้วย: การเปรียบเทียบการลบ ฯลฯ
  • ส่งผ่านและจัดเก็บเป็นสตริง และแปลงเป็นวันที่สำหรับการจัดรูปแบบตามการตั้งค่าภูมิภาคของไคลเอ็นต์เท่านั้น มันมีข้อเสียมากกว่าตัวเลือกก่อนหน้า - ตัวอย่างเช่นหากส่วนของวันที่ในสตริงที่เก็บไว้ไม่อยู่ในลำดับ "ปี, เดือน, วัน" ก็จะเป็นไปไม่ได้ที่จะทำการค้นหาที่จัดทำดัชนีอย่างมีประสิทธิภาพตามช่วงวันที่
แน่นอน คุณสามารถพยายามยกตัวอย่างแย้งและบอกว่าสัญญานี้สมเหตุสมผลเฉพาะภายในประเทศที่สรุปสัญญาเท่านั้น ประเทศนั้นอยู่ในเขตเวลาเดียวกัน ดังนั้นจึงสามารถกำหนดช่วงเวลาที่มีผลใช้บังคับได้อย่างไม่คลุมเครือ แต่แม้ในกรณีนี้ ผู้ใช้จากโซนเวลาอื่นจะไม่สนใจว่ากิจกรรมนี้จะเกิดขึ้นในช่วงเวลาใดตามเวลาท้องถิ่นของตน และถึงแม้ว่าจะต้องแสดงช่วงเวลานี้ในเวลานี้ ก็จะต้องแสดงไม่เพียงแต่วันที่เท่านั้น แต่ยังต้องแสดงเวลาที่ขัดแย้งกับสภาพเดิมอีกด้วย

ช่วงเวลา

ด้วยการจัดเก็บและประมวลผลช่วงเวลา ทุกอย่างจึงง่ายดาย: ค่าของพวกมันไม่ได้ขึ้นอยู่กับเขตเวลา ดังนั้นจึงไม่มีคำแนะนำพิเศษที่นี่ สามารถจัดเก็บและส่งผ่านเป็นหน่วยเวลาได้ (จำนวนเต็มหรือจุดลอยตัว ขึ้นอยู่กับความแม่นยำที่ต้องการ) หากความแม่นยำอันดับสองมีความสำคัญ จะเป็นจำนวนวินาที หากความแม่นยำระดับมิลลิวินาทีมีความสำคัญ จะเป็นจำนวนมิลลิวินาที เป็นต้น

แต่การคำนวณช่วงเวลาอาจมีข้อผิดพลาดได้ สมมติว่าเรามีตัวอย่างโค้ด C# ที่คำนวณช่วงเวลาระหว่างสองเหตุการณ์:

DateTime start = DateTime.Now; //... DateTime end = DateTime.Now; สองชั่วโมง = (สิ้นสุด - เริ่ม).TotalHours;
เมื่อมองแวบแรกไม่มีปัญหาที่นี่ แต่ก็ไม่เป็นเช่นนั้น ประการแรก อาจมีปัญหากับการทดสอบโค้ดดังกล่าวของหน่วย แต่เราจะพูดถึงเรื่องนี้ในภายหลัง ประการที่สอง ลองจินตนาการว่าช่วงเวลาแรกตรงกับช่วงฤดูหนาว และช่วงเวลาสุดท้ายตรงกับช่วงฤดูร้อน (เช่น นี่คือวิธีการวัดจำนวนชั่วโมงทำงาน และคนงานมีกะกลางคืน)

สมมติว่าโค้ดกำลังทำงานในเขตเวลาที่เวลาออมแสงในปี 2559 เกิดขึ้นในคืนวันที่ 27 มีนาคม และจำลองสถานการณ์ที่อธิบายไว้ข้างต้น:

วันที่และเวลาเริ่มต้น = DateTime.Parse("2016-03-26T20:00:15+02"); สิ้นสุด DateTime = DateTime.Parse("2016-03-27T05:00:15+03"); สองชั่วโมง = (สิ้นสุด - เริ่ม).TotalHours;
รหัสนี้จะส่งผลให้เป็นเวลา 9 ชั่วโมง แม้ว่าในความเป็นจริงจะผ่านไปแล้ว 8 ชั่วโมงระหว่างช่วงเวลาเหล่านี้ก็ตาม คุณสามารถตรวจสอบได้อย่างง่ายดายโดยเปลี่ยนรหัสดังนี้:

DateTime start = DateTime.Parse("2016-03-26T20:00:15+02").ToUniversalTime(); สิ้นสุด DateTime = DateTime.Parse("2016-03-27T05:00:15+03").ToUniversalTime(); สองชั่วโมง = (สิ้นสุด - เริ่ม).TotalHours;
ดังนั้นข้อสรุป - การดำเนินการทางคณิตศาสตร์ใด ๆ ที่มีวันที่และเวลาจะต้องดำเนินการโดยใช้ค่า UTC หรือประเภทที่เก็บข้อมูลโซนเวลา แล้วโอนกลับไปยังท้องถิ่นหากจำเป็น จากมุมมองนี้ ตัวอย่างดั้งเดิมสามารถแก้ไขได้ง่ายโดยการเปลี่ยน DateTime.Now เป็น DateTime.UtcNow

ความแตกต่างนี้ไม่ได้ขึ้นอยู่กับแพลตฟอร์มหรือภาษาเฉพาะ นี่คือโค้ดที่คล้ายกันใน Java ที่มีปัญหาเดียวกัน:

เริ่มต้น LocalDateTime = LocalDateTime.now(); //... สิ้นสุด LocalDateTime = LocalDateTime.now(); ชั่วโมงที่ยาวนาน = ChronoUnit.HOURS.between (เริ่มต้น, สิ้นสุด);
นอกจากนี้ยังสามารถแก้ไขได้อย่างง่ายดาย เช่น โดยใช้ ZonedDateTime แทน LocalDateTime

กำหนดการของเหตุการณ์ที่กำหนดไว้

การจัดกำหนดการเหตุการณ์ที่กำหนดเวลาไว้เป็นสถานการณ์ที่ซับซ้อนมากขึ้น ไม่มีประเภทสากลที่ให้คุณจัดเก็บตารางเวลาในไลบรารีมาตรฐานได้ แต่งานดังกล่าวไม่ได้เกิดขึ้นน้อยมากดังนั้นจึงสามารถหาวิธีแก้ปัญหาสำเร็จรูปได้โดยไม่มีปัญหา ตัวอย่างที่ดีคือรูปแบบตัวกำหนดเวลา cron ซึ่งใช้ในรูปแบบใดรูปแบบหนึ่งโดยโซลูชันอื่น เช่น Quartz: http://quartz-scheduler.org/api/2.2.0/org/quartz/CronExpression.html ครอบคลุมความต้องการด้านกำหนดการเกือบทั้งหมด รวมถึงตัวเลือกต่างๆ เช่น “วันศุกร์ที่สองของเดือน”

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

นอกจากคำแนะนำที่อธิบายไว้ข้างต้นเกี่ยวกับการจัดเก็บและการประมวลผลค่าเวลาประเภทต่างๆ แล้ว ยังมีคำแนะนำอื่นๆ อีกหลายประการที่ฉันอยากจะกล่าวถึงด้วย

ประการแรก เกี่ยวกับการใช้สมาชิกคลาสคงที่เพื่อรับเวลาปัจจุบัน - DateTime.UtcNow, ZonedDateTime.now() ฯลฯ ดังที่ได้กล่าวไปแล้ว การใช้โดยตรงในโค้ดอาจทำให้การทดสอบหน่วยมีความซับซ้อนอย่างมาก เนื่องจากหากไม่มีเฟรมเวิร์กการเยาะเย้ยพิเศษ จะไม่สามารถแทนที่เวลาปัจจุบันได้ ดังนั้น หากคุณวางแผนที่จะเขียนการทดสอบหน่วย คุณควรตรวจสอบให้แน่ใจว่าสามารถนำวิธีการดังกล่าวไปใช้แทนได้ มีอย่างน้อยสองวิธีในการแก้ปัญหานี้:

  • จัดเตรียมอินเทอร์เฟซ IDateTimeProvider ด้วยวิธีการเดียวที่ส่งคืนเวลาปัจจุบัน จากนั้นเพิ่มการพึ่งพาอินเทอร์เฟซนี้ในหน่วยโค้ดทั้งหมดที่คุณต้องการรับเวลาปัจจุบัน ในระหว่างการทำงานของโปรแกรมปกติ การใช้งาน "ค่าเริ่มต้น" จะถูกฉีดเข้าไปในตำแหน่งเหล่านี้ทั้งหมด ซึ่งจะส่งคืนเวลาปัจจุบันจริง และในการทดสอบหน่วย - การใช้งานอื่น ๆ ที่จำเป็น วิธีการนี้มีความยืดหยุ่นมากที่สุดจากมุมมองของการทดสอบ
  • สร้างคลาสแบบคงที่ของคุณเองด้วยวิธีการรับเวลาปัจจุบันและความสามารถในการติดตั้งการใช้งานวิธีนี้จากภายนอก ตัวอย่างเช่น ในกรณีของโค้ด C# คลาสนี้สามารถเปิดเผยคุณสมบัติ UtcNow และเมธอด SetImplementation(Func) นัย) การใช้คุณสมบัติคงที่หรือวิธีการรับเวลาปัจจุบันไม่จำเป็นต้องระบุการพึ่งพาอินเทอร์เฟซเพิ่มเติมอย่างชัดเจนทุกที่ แต่จากมุมมองของหลักการ OOP มันไม่ใช่วิธีแก้ปัญหาในอุดมคติ อย่างไรก็ตามหากตัวเลือกก่อนหน้านี้ไม่เหมาะสมด้วยเหตุผลบางประการ คุณสามารถใช้ตัวเลือกนี้ได้
ปัญหาเพิ่มเติมที่ควรได้รับการแก้ไขเมื่อย้ายไปยังการใช้งานผู้ให้บริการเวลาปัจจุบันของคุณคือการทำให้แน่ใจว่าไม่มีใครใช้คลาสมาตรฐานแบบ "ล้าสมัย" ต่อไป งานนี้แก้ไขได้ง่ายในระบบควบคุมคุณภาพโค้ดส่วนใหญ่ โดยพื้นฐานแล้ว การค้นหาสตริงย่อยที่ "ไม่ต้องการ" ในไฟล์ทั้งหมด ยกเว้นไฟล์ที่มีการประกาศการใช้งาน "ค่าเริ่มต้น"

ข้อแม้ประการที่สองในการรับเวลาปัจจุบันก็คือ ลูกค้าไม่สามารถเชื่อถือได้- เวลาปัจจุบันบนคอมพิวเตอร์ของผู้ใช้อาจแตกต่างจากเวลาจริงมากและหากมีตรรกะเชื่อมโยงอยู่ ความแตกต่างนี้สามารถทำลายทุกสิ่งได้ ทุกสถานที่ที่มีความจำเป็นต้องได้รับเวลาปัจจุบัน ถ้าเป็นไปได้ ควรทำบนฝั่งเซิร์ฟเวอร์ และตามที่กล่าวไว้ข้างต้น การดำเนินการทางคณิตศาสตร์ทั้งหมดตามเวลาจะต้องดำเนินการในค่า UTC หรือใช้ประเภทที่เก็บออฟเซ็ตโซนเวลา

และอีกอย่างที่ผมอยากพูดถึงก็คือมาตรฐาน ISO 8601 ซึ่งอธิบายรูปแบบวันที่และเวลาในการแลกเปลี่ยนข้อมูล โดยเฉพาะอย่างยิ่ง การแสดงสตริงของวันที่และเวลาที่ใช้ในการทำให้เป็นอนุกรมจะต้องเป็นไปตามมาตรฐานนี้ เพื่อป้องกันปัญหาความเข้ากันได้ที่อาจเกิดขึ้น ในทางปฏิบัติ เป็นเรื่องยากมากที่คุณจะต้องใช้การจัดรูปแบบด้วยตนเอง ดังนั้นมาตรฐานจึงมีประโยชน์เพื่อวัตถุประสงค์ในการให้ข้อมูลเป็นหลัก

แท็ก: เพิ่มแท็ก

ตัวเลือกที่มีมนุษยธรรม ([คุณต้องลงทะเบียนเพื่อดูลิงก์])

สาระสำคัญของปัญหาคือสิ่งนี้.. หากคุณปรับใช้ฐานข้อมูลบนเซิร์ฟเวอร์ SQL โดยไม่ได้ตั้งใจด้วย "การชดเชยวันที่" เป็น 0 แสดงว่าปัญหาเกิดขึ้นเมื่อฐานข้อมูลมีแอตทริบิวต์ที่มีประเภท TIME เช่น คุณลักษณะนี้ตั้งค่าเป็น 01 /01/0001 10:30:00 หรือวันที่ลงทะเบียนว่างเปล่า 01.01.0001 00:00:00 เมื่อบันทึกรายละเอียดดังกล่าวจะไม่ถูกบันทึก
บนอินเทอร์เน็ตพวกเขาแนะนำให้สร้างฐานข้อมูลใหม่โดยมีออฟเซ็ตเป็น 2,000
แต่ฉันไม่อยากสร้างฐานใหม่จริงๆ และเปลี่ยนเส้นทางไปยังฐานข้อมูลสำหรับผู้ใช้ทั้งหมด
จากนั้นฉันก็ติดตามเส้นทางที่ค่านี้เก็บไว้ใน SQL ฉันเจอมันและเปลี่ยนเป็นปี 2000 และทุกอย่างก็โอเค..
และตอนนี้ทีละขั้นตอนว่าจะเปลี่ยนแปลงที่ไหน

เตะผู้ใช้ทั้งหมด

ความสนใจ!!!

1. ทำก่อน สำเนาสำรองโดยวิธี 1C เช่น อัพโหลดไปที่ *.dt
ต้องทำก่อนที่จะเปลี่ยน “ออฟเซ็ต”
หากยังไม่เสร็จสิ้น จะมีข้อมูลอ้างอิง เอกสาร ฯลฯ ในฐานข้อมูลทั้งหมดของคุณ
มีความจำเป็นตรงไหน วันที่จะเป็น 02.10.0009
สิ่งที่ไม่ได้รับอนุญาต...
ดังนั้นคุณได้อัปโหลดไปที่ *.dt

2. ไปที่ Studio จัดการเซิร์ฟเวอร์ SQL
ค้นหาฐานของคุณในรายการแล้วกดเครื่องหมายบวก
ค้นหาโฟลเดอร์ "Tables" ที่นั่นแล้วเปิดขึ้นมา
โต๊ะจำนวนมากจะเปิดขึ้น ไปที่ด้านล่างสุดแล้วค้นหาโต๊ะ
_YearOffset ยืนบนนั้นแล้วเลือกรายการ "เปิดตาราง" ด้วยปุ่มขวา ดูรูปที่ 1
เปลี่ยนค่า 0 เป็น 2000
ปิด Studio จัดการเซิร์ฟเวอร์ SQL

3. ไปที่ตัวกำหนดค่าและโหลดฐานข้อมูลที่บันทึกไว้ก่อนหน้านี้

หากยังไม่เสร็จสิ้น วันที่ทั้งหมดจะเป็นปี 0009
หลังจากโหลดฐานข้อมูลแล้ว... คุณสามารถไปที่ 1C และตรวจสอบให้แน่ใจว่าวันที่เป็นปกติ
ผลลัพธ์คือเราเปลี่ยน "การชดเชยวันที่จาก 0 เป็น 2000"

บางครั้งมันเกิดขึ้นว่าตัวเลือกนี้ไม่สามารถใช้ได้ด้วยเหตุผลใดก็ตาม มีตัวเลือกที่ฮาร์ดคอร์กว่านี้ ([คุณต้องลงทะเบียนเพื่อดูลิงก์]):

ประกาศเคอร์เซอร์ TablesAndFields สำหรับ

เลือก object.name เป็น Tablename, columns.name เป็น columnname
จาก dbo.sysobjects เช่นวัตถุ
ปล่อยให้เข้าร่วม dbo.syscolumns เป็นคอลัมน์บน object.id = columns.id
โดยที่ object.xtype = "U" และ columns.xtype = 61

เปิด TablesAndFields

ในขณะที่ @@FETCH_STATUS = 0
เริ่มต้นดำเนินการ (" อัปเดต"+ @ชื่อตาราง + "
ชุด " + @ชื่อคอลัมน์ + " = ""2000- 01- 01 00:00:00" "
ที่ไหน " + @ชื่อคอลัมน์ + " > ""3999- 12- 31 23:59:59" "")

สิ่งนี้จะถูกดำเนินการตราบเท่าที่การดึงข้อมูลครั้งก่อนสำเร็จ
ดึงข้อมูลถัดไปจาก TablesAndFields ลงใน @TableName, @ColumnName
จบ

ปิด TablesAndFields
ยกเลิกการจัดสรรTablesAndFields
ไป

ก่อนดำเนินการใด ๆ อย่าลืมทำสำเนาฐานข้อมูล!

DateTimeOffset testDateAndTime = DateTimeOffset ใหม่ (2008, 5, 1, 8, 6, 32, TimeSpan ใหม่ (1, 0, 0)); // เวลาและวันที่สะอาด testDateAndTime = testDateAndTime.DateTime.Date; var dateTableEntry = db.DatesTable.First(dt => dt.Id == someTestId); DateTableEntry.test= testDateAndTime; db.SaveChangesAsync();

ผลลัพธ์ในฐานข้อมูล: 2008-05-01 00:00:00.0000000 -04:00

วิธีเปิดใช้งาน -4:00 ถึง +00:00 (จากโค้ดก่อนบันทึก)

ฉันเหนื่อย:

งานสาธารณะ SetTimeZoneOffsetToZero(DateTimeOffset dateTimeOffSetObj) ( TimeSpan zeroOffsetTimeSpan = TimeSpan ใหม่ (0, 0, 0, 0, 0); กลับ dateTimeOffSetObj.ToOffset (zeroOffsetTimeSpan); )

เขาไม่ทำอะไรเลย

เป้าหมายสุดท้ายคือการมีวันที่โดยไม่มีการชดเชยเวลาหรือเขตเวลา ฉันไม่ต้องการแปลงเวลาเป็นเขตเวลาอื่น (เช่น 00:00:00.0000000 ฉันไม่ต้องการให้ลบ 4 ชั่วโมงจากเวลา 00:00:00.0000000 และ 00:00:00.0000000 ชดเชยเวลาที่ตั้งไว้ด้วย +00 :00 ฉันแค่อยากให้มันตั้งค่าออฟเซ็ตเป็น +00:00) ฉันต้องการวันที่ปัจจุบันที่มีการชดเชยเป็นศูนย์

แก้ไข:

นี่คือสิ่งที่จะนำเสนอในที่อื่น:

DateTimeOffset testDateAndTime = DateTimeOffset ใหม่ (2008, 5, 1, 8, 6, 32, TimeSpan ใหม่ (1, 0, 0)); testDateAndTime = testDateAndTime.DateTime.Date; // ส่วนเวลาเป็นศูนย์ testDateAndTime = DateTime.SpecifyKind (testDateAndTime.Date, DateTimeKind.Utc); // ส่วนออฟเซ็ต "Zero out"

ฉันแน่ใจว่า SpecifyKind จะ SpecifyKind dateTimeOffset ของฉัน เช่น เปลี่ยนทั้งการชดเชยเวลาและเขตเวลา แต่เมื่อทดสอบแล้ว ดูเหมือนว่าจะเปลี่ยนการชดเชยเขตเวลา ซึ่งเป็นสิ่งที่ฉันต้องการ มีปัญหากับเรื่องนี้หรือไม่?

1 คำตอบ

ปัญหาไม่เกี่ยวข้องกับฐานข้อมูล หากคุณตั้งค่าเบรกพอยต์หรือลงทะเบียนเอาต์พุตไว้ที่ไหนสักแห่ง คุณจะเห็นออฟเซ็ตถูกผูกไว้หลังจากโค้ดนี้ไม่นาน:

TestDateAndTime = testDateAndTime.DateTime.Date;

มาทำลายมันกัน:

  • คุณเริ่มต้นด้วย DateTimeOffset 2008-05-01T08:06:32+01:00
  • จากนั้นคุณเรียกว่า .DateTime ส่งผลให้ค่า DateTime เป็น 2008-05-01T08:06:32 พร้อมด้วย DateTimeKind.Unspecified
  • จากนั้นคุณเรียกว่า .Date ซึ่งส่งผลให้ค่า DateTime เป็น 2008-05-01T00:00:00 พร้อมด้วย DateTimeKind.Unspecified
  • คุณส่งคืนผลลัพธ์ใน testDateAndTime ซึ่งเป็นประเภท DateTimeOffset ซึ่งทำให้มีการแปลงโดยนัยจาก DateTime เป็น DateTimeOffset ซึ่งใช้เขตเวลาท้องถิ่น- ในกรณีของคุณ ปรากฏว่าการชดเชยสำหรับค่านี้ในเขตเวลาท้องถิ่นของคุณคือ -04:00 ดังนั้นค่าผลลัพธ์คือ DateTimeOffset 2008-05-01T00:00:00-04:00 ตามที่คุณอธิบาย

คุณพูดว่า:

เป้าหมายสุดท้ายคือการมีวันที่โดยไม่มีการชดเชยเวลาหรือเขตเวลา

ขณะนี้ไม่มีประเภทข้อมูล C# ดั้งเดิมที่เป็นเพียงวันที่ที่ไม่มีเวลา มีประเภท Date ล้วนๆ ในแพ็คเกจ System.Time ใน corefxlab แต่ยังไม่พร้อมสำหรับแอปพลิเคชันการผลิตทั่วไป มี LocalDate ในไลบรารีเวลาของ Noda ซึ่งคุณสามารถใช้ได้ในปัจจุบัน แต่คุณยังคงต้องแปลงกลับเป็นประเภทเนทิฟก่อนที่จะจัดเก็บไว้ในฐานข้อมูล ดังนั้นในระหว่างนี้ สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือ:

  • เปลี่ยน SQL Server ของคุณเพื่อใช้ประเภทวันที่ในฟิลด์นี้
  • ในโค้ด .NET ของคุณ ให้ใช้ DateTime พร้อมเวลา 00:00:00 และ DateTimeKind.Unspecified คุณต้องจำไว้ว่าอย่าเพิกเฉยต่อส่วนของเวลา (เนื่องจากมีวันที่ที่ไม่มีเที่ยงคืนท้องถิ่นในบางโซนเวลา)
  • เปลี่ยนการแจ้งเตือนการทดสอบให้เป็น DateTime แทนที่จะเป็น DateTimeOffset

โดยทั่วไปแล้ว DateTimeOffset จะเหมาะสำหรับ ปริมาณมากสถานการณ์ (เช่น เหตุการณ์การประทับเวลา) ซึ่งไม่เหมาะกับค่าเฉพาะวันที่เท่านั้น

ฉันต้องการวันที่ปัจจุบันที่มีการชดเชยเป็นศูนย์

หากคุณต้องการเหมือน DateTimeOffset จริงๆ คุณจะต้องทำ:

TestDateAndTime = DateTimeOffset ใหม่ (testDateAndTime.Date, TimeSpan.Zero);

อย่างไรก็ตาม ฉันไม่แนะนำให้ทำเช่นนี้ การทำเช่นนี้แสดงว่าคุณกำลังใช้วันที่ท้องถิ่นของค่าดั้งเดิมและยืนยันว่าอยู่ใน UTC หากออฟเซ็ตเดิมเป็นอย่างอื่นที่ไม่ใช่ศูนย์ นี่จะเป็นข้อความเท็จ มันจะนำไปสู่ข้อผิดพลาดอื่นๆ ในภายหลัง เนื่องจากคุณกำลังพูดถึงช่วงเวลาที่แตกต่างกัน (ซึ่งอาจเป็นวันที่ที่แตกต่างออกไป) มากกว่าที่คุณสร้างขึ้น

เกี่ยวกับคำถามเพิ่มเติมที่ถามในการแก้ไขของคุณ - การระบุ DateTimeKind.Utc จะเปลี่ยนพฤติกรรมของการส่งโดยนัย แทนที่จะใช้เขตเวลาท้องถิ่น ระบบจะใช้เวลา UTC ซึ่งมีออฟเซ็ตเป็นศูนย์เสมอ ผลลัพธ์จะเหมือนกับมุมมองที่ชัดเจนยิ่งขึ้นที่ฉันให้ไว้ข้างต้น ฉันยังคงแนะนำสิ่งนี้ด้วยเหตุผลเดียวกัน

มาดูตัวอย่างการเริ่มต้นตั้งแต่ 2016-12-31T22:00:00-04:00. ตามแนวทางของคุณ คุณควรจัดเก็บ 2016-12-31T00:00:00+00:00 ไว้ในฐานข้อมูล อย่างไรก็ตาม นี่เป็นสองจุดที่แตกต่างกันของเวลา ครั้งแรกที่ปรับมาตรฐานเป็น UTC จะเป็น 2017-01-01T02:00:00+00:00 และครั้งที่สองซึ่งแปลงเป็นเขตเวลาอื่นจะเป็น 2016-12-30T20:00:00-04:00 โปรดทราบการเปลี่ยนแปลงวันที่ในการแปลง นี่อาจไม่ใช่พฤติกรรมที่คุณต้องการในแอปพลิเคชันของคุณ