วิธีจัดการ TimeZone อย่างถูกต้องใน SQL Server


34

เซิร์ฟเวอร์การพัฒนาในพื้นที่ของฉันอยู่ในตะวันออกกลาง แต่เซิร์ฟเวอร์ที่ใช้งานจริงของฉันอยู่ในสหราชอาณาจักร

ฉันต้องการแสดงวันที่ถึงผู้ใช้ในเขตเวลาของพวกเขา ตัวอย่างเช่นหากผู้ใช้อยู่ในซาอุดิอาระเบียแล้วฉันต้องแสดงเวลาตามรูปแบบซาอุดิอาระเบีย

ฉันควรสร้างตารางฐานข้อมูลใหม่ที่ชื่อว่า TimeZone และบันทึกเวลาใน UTC หรือไม่


การแปลงวันที่อย่างมีประสิทธิภาพระหว่างเวลา UTC กับเวลาท้องถิ่น (เช่น. PST) ใน SQL 2005 http://stackoverflow.com/questions/24797/effectively-converting-dates-between-utc-and-local-ie-pst-time-in- sql-2005
mehdi

นี่เป็นแอปพลิเคชันเว็บหรือแอปพลิเคชันเดสก์ท็อปหรือ ... วิธีการนำสิ่งนี้ไปปฏิบัติแตกต่างกันอย่างมากโดยกลไกการส่งมอบลูกค้าเพราะสิ่งที่คุณต้องการนั้นขึ้นอยู่กับฝั่งไคลเอ็นต์
Jon Seigel

เว็บนี้เช่นเดียวกับมือถือพื้นเมือง ใช่ผลกระทบนี้ลูกค้าที่แตกต่างกันแตกต่างกัน
user960567

1
ดูเหมือนว่าคำถามของคุณไม่เพียงเกี่ยวกับฐานข้อมูลเท่านั้น แต่ยังเกี่ยวข้องกับการพัฒนาแอปพลิเคชันด้วย คำถาม Stack Overflow นี้เป็นสิ่งที่คุณต้องอ่าน: การปรับเวลาตามฤดูกาลและแนวทางปฏิบัติที่ดีที่สุดสำหรับเขตเวลา
Gan

คำตอบ:


48

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

Anyways ที่จะได้รับในการติดตามการทำมันขวามันเป็นสิ่งสำคัญที่จะเก็บข้อมูลเขตเวลากับเวลา กล่าวอีกนัยหนึ่งการตระหนักว่าวันที่ / เวลา20130407 14:50นั้นไม่มีความหมายโดยไม่มี (a) รวมถึงออฟเซ็ต UTC ในปัจจุบันของเขตเวลา(หมายเหตุ 1)หรือ (b) ทำให้มั่นใจได้ว่าตรรกะทั้งหมดที่แทรกค่าเหล่านี้ก่อน เป็นไปได้มากที่สุด 0) โดยไม่มีสิ่งเหล่านั้นสองค่าเวลาที่กำหนดเป็นที่เปรียบมิได้และข้อมูลที่เป็นความเสียหาย (วิธีหลังเล่นด้วยไฟ(หมายเหตุ 2)โดยวิธีอย่าทำอย่างนั้น)

ใน SQL Server 2008+ คุณสามารถจัดเก็บออฟเซ็ตตามเวลาได้โดยตรงโดยใช้datetimeoffsetชนิดข้อมูล (เพื่อความสมบูรณ์ในปี 2005 และก่อนหน้านี้ฉันจะเพิ่มคอลัมน์ที่สองเพื่อจัดเก็บค่าออฟเซ็ต UTC ปัจจุบัน (เป็นนาที))

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

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

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

หมายเหตุ 1:

UTC ออฟเซ็ตของเขตเวลาไม่คงที่: พิจารณาการประหยัดในเวลากลางวันโดยที่อ็อฟเซ็ต UTC ของโซนจะแปรผันตามบวกหรือลบชั่วโมง นอกจากนี้วันที่ออมแสงในโซนยังแตกต่างกันไปตามปกติ ดังนั้นการใช้datetimeoffset(หรือคอมโพสิตของlocal timeและUTC offset at that time) ส่งผลให้เกิดการกู้คืนข้อมูลสูงสุด

โน้ต 2:

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

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

การจัดเก็บออฟเซตฆ่านกสองตัวด้วยหินก้อนเดียว และแม้ว่าจะไม่จำเป็นต้องใช้ในตอนนี้ก็ทำให้มีความเป็นไปได้ในภายหลังหากจำเป็น จริงฉันใช้พื้นที่เก็บข้อมูลเพิ่มขึ้น แต่ฉันคิดว่ามันคุ้มค่ากับการแลกเปลี่ยนเพราะข้อมูลจะหายไปหากไม่เคยบันทึกไว้ตั้งแต่แรก


3
ในขณะที่ฉันเข้าใจว่าการชดเชยไม่เหมือนกับเขตเวลา ... เวลาที่จัดเก็บไว้ในชุดข้อมูลจะสูญเสียข้อมูลเช่นการรับรู้การประหยัดเวลาตามฤดูกาล ... ข้อมูลเพิ่มเติม: stackoverflow.com/tags/timezone/info
Peter McEvoy

1
@PeterMcEvoy DST ไม่มีความเกี่ยวข้องที่นี่ datetimeoffsetหมายถึง "นี่เป็นเวลาท้องถิ่นของผู้ใช้ / คอมพิวเตอร์เมื่อเกิดเหตุการณ์" - เราไม่จำเป็นต้องทราบว่า DST มีผลหรือไม่เนื่องจาก UTC ออฟเซตที่กำหนดอยู่ที่นั่นเสมอ (ฝังอยู่ภายในdatetimeoffsetค่า)
Dai

12

ฉันได้พัฒนาโซลูชันที่ครอบคลุมสำหรับการแปลงเขตเวลาใน SQL Server ดูการสนับสนุนโซนเวลาของ SQL Server บน GitHub

มันจัดการการแปลงระหว่างโซนเวลาและจาก UTC อย่างเหมาะสมรวมถึงการปรับเวลาตามฤดูกาล

มันมีความคล้ายคลึงกับแนวคิดของ "T-SQL Toolbox" ซึ่งอธิบายไว้ในคำตอบของโฆษณา แต่ใช้โซนเวลา IANA / Olson / TZDB ทั่วไปแทน Microsoft และจะมียูทิลิตี้เพื่อรักษาข้อมูลให้เป็นรุ่นใหม่ของ TZDB ออกมา

ตัวอย่างการใช้ (เพื่อตอบ OP):

SELECT Tzdb.UtcToLocal(sysutcdatetime(), 'Asia/Riyadh') as CurrentTimeInSaudiArabia

ดูreadme บน GitHubสำหรับ API เพิ่มเติมและคำอธิบายโดยละเอียด

นอกจากนี้โปรดทราบว่าเนื่องจากนี่เป็นโซลูชันที่ใช้ UDF จึงไม่ได้ออกแบบมาพร้อมกับประสิทธิภาพเป็นวัตถุประสงค์หลัก มันจะดีกว่าถ้าฟังก์ชั่นนี้ถูกสร้างขึ้นใน SQL Server เช่นเดียวกับที่CONVERT_TZฟังก์ชั่นมีอยู่ใน Oracle และ MySQL


6

SQL Server ทำการปรับเปลี่ยนในปี 2005 เป็นต้นไปซึ่งมีการบันทึกเขตเวลาภายในใน UTC นี่คือสาเหตุหลักมาจากการจำลองแบบทางภูมิศาสตร์และโปรเจคเตอร์ HA ที่เกี่ยวข้องกับการจัดส่งบันทึกและมีการบันทึกเวลาการจัดส่งบันทึกในโซนเวลาที่แตกต่างกันทำให้มันเป็นไปไม่ได้สำหรับวิธีการเก่าที่จะเรียกคืนพวกเขา

ดังนั้นการบันทึกทุกอย่างภายในเวลา UTC ทำให้ SQL Server ทำงานได้ดีทั่วโลก นี่คือหนึ่งในสาเหตุที่การประหยัดเวลากลางวันเป็นความเจ็บปวดที่ต้องจัดการกับ Windows เนื่องจากผลิตภัณฑ์ MS อื่น ๆ เช่น Outlook ยังบันทึกวันที่ / เวลาภายในเป็น UTC และสร้างออฟเซ็ตที่จำเป็นต้องได้รับการแก้ไข

ฉันทำงานใน บริษัท ที่มีเซิร์ฟเวอร์นับพัน (ไม่ใช่เซิร์ฟเวอร์ MS SQL แต่เซิร์ฟเวอร์ทุกประเภท) กระจายไปทั่วโลกและถ้าเราไม่ได้บังคับให้ทุกอย่างเป็นไปโดย UTC เราทุกคนจะเสียสติ เร็วมาก


5

ฉันได้พัฒนาและเผยแพร่โครงการ“ T-SQL Toolbox” บน codeplexเพื่อช่วยให้ทุกคนที่ต่อสู้กับดาต้าไทม์และการจัดการเขตเวลาใน SQL Server มันเป็นโอเพ่นซอร์สและใช้งานได้อย่างสมบูรณ์ฟรี

มันมี UDF การแปลงวันที่และเวลาที่ง่ายโดยใช้ T-SQL ธรรมดาพร้อมกับตารางการกำหนดค่าเพิ่มเติมนอกกรอบ

ในตัวอย่างของคุณคุณสามารถใช้ตัวอย่างต่อไปนี้:

SELECT [DateTimeUtil].[UDF_ConvertLocalToLocalByTimezoneIdentifier] (
    'GMT Standard Time', -- the original timezone in which your datetime is stored
    'Middle East Standard Time', -- the target timezone for your user
    '2014-03-30 01:55:00' -- the original datetime you want to convert
)

นี่จะส่งคืนค่าวันที่และเวลาที่แปลงแล้วสำหรับผู้ใช้ของคุณ

รายการเขตเวลาที่รองรับทั้งหมดสามารถพบได้ในตาราง "DateTimeUtil.Timezone" นอกจากนี้ยังมีอยู่ในฐานข้อมูล T-SQL Toolbox


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

แน่นอนว่าคุณมาจากมุมมองด้านประสิทธิภาพ ตารางคิวรี UDF ไม่ได้ถูกออกแบบมาสำหรับการทำงานของข้อมูลจำนวนมาก แต่เพื่อการใช้งานที่ง่าย อย่างไรก็ตามพวกเขาทำงานได้ค่อนข้างดีถึงประมาณ 100,000 บันทึกในเครื่องของฉัน
โฆษณา

2
หากมีการดำเนินการบันทึกเพิ่มเติมในกรณีการใช้งานของเขาและเรื่องประสิทธิภาพคุณควรคิดถึงข้อมูลที่คำนวณล่วงหน้าก่อนดีกว่า แต่ถึงอย่างนั้น UDF เหล่านี้อาจช่วยรักษาค่า datatime ในเขตเวลาที่ต้องการ
โฆษณา

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

1

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

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