2 คะแนน โดย GN⁺ 2024-08-16 | 1 ความคิดเห็น | แชร์ทาง WhatsApp

ส่วนขยายวันที่/เวลาแบบความละเอียดสูง

SQLite มีฟังก์ชันวันที่พื้นฐานมาให้ แต่เนื่องจากต้องการความสามารถที่มากขึ้น จึงได้สร้างส่วนขยายวันที่/เวลาแบบความละเอียดสูงชื่อ sqlean-time ขึ้นมา ส่วนขยายนี้มี API ที่เป็นโครงสร้างชัดเจนและมีฟีเจอร์หลากหลาย

หมายเหตุ การเพิ่มส่วนขยายให้ SQLite ทำได้ง่ายมาก เพียงดาวน์โหลดไฟล์แล้วรันคำสั่งฐานข้อมูลเพียงคำสั่งเดียว

แนวคิด

ส่วนขยายนี้ใช้ชนิดค่าหลัก 2 แบบ: เวลา (Time) และช่วงเวลา (Duration)

  • เวลา (Time): เป็นคู่ค่าที่ประกอบด้วยวินาทีและนาโนวินาที โดยแสดงจำนวนวินาทีหลังจากเวลา 0 (0001-01-01 00:00:00 UTC) และจำนวนนาโนวินาทีภายในวินาทีปัจจุบัน

    • สามารถเก็บเวลาในรูปแบบแทนภายในได้ ซึ่งทำให้แสดงวันที่ย้อนหลังหรือไปข้างหน้าได้หลายพันล้านปีด้วยความละเอียดระดับนาโนวินาที
    • ยังสามารถเก็บเวลาเป็นจำนวนวินาทีหลัง Unix epoch (1970-01-01 00:00:00 UTC) ได้ด้วย รวมถึงมิลลิวินาที ไมโครวินาที และนาโนวินาที
    • เวลาจะถูกเก็บและประมวลผลเป็น UTC เสมอ แต่สามารถแปลงเป็นออฟเซ็ตเขตเวลาเฉพาะได้
  • ช่วงเวลา (Duration): เป็นตัวเลข 64 บิตในหน่วยนาโนวินาที ซึ่งแทนค่าได้ยาวประมาณ 290 ปี

การสร้างค่าเวลา

  • เวลาปัจจุบัน:

    select time_fmt_iso(time_now());  -- 2024-08-06T21:22:15.431295000Z
    
  • วันที่/เวลาที่ระบุ:

    select time_fmt_iso(time_date(2011, 11, 18));  -- 2011-11-18T00:00:00Z
    select time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35));  -- 2011-11-18T15:56:35Z
    

การดึงฟิลด์ของเวลา

มีฟังก์ชันสำหรับดึงฟิลด์วันที่/เวลาหลากหลายแบบ:

select 'year  = ' || time_get_year(time_now());
select 'month  = ' || time_get_month(time_now());
select 'day   = ' || time_get_day(time_now());

เวลา Unix

ฟังก์ชันสำหรับสร้างค่าเวลาจากเวลา Unix (เวลาหลัง 1970-01-01 UTC):

select time_fmt_iso(time_unix(1321631795));  -- 2011-11-18T15:56:35Z

ฟังก์ชันสำหรับแปลงค่าเวลาเป็นเวลา Unix:

select time_to_unix(time_now());  -- 1722979335

การเปรียบเทียบเวลา

ฟังก์ชันสำหรับเปรียบเทียบค่าเวลา:

select time_after(time_now(), time_date(2011, 11, 18));  -- 1
select time_before(time_now(), time_date(2011, 11, 18));  -- 0

การคำนวณเวลา

ฟังก์ชันสำหรับบวกช่วงเวลาเข้ากับค่าเวลา:

select time_fmt_iso(time_add(time_now(), 24*dur_h()));  -- 2024-08-07T21:22:15.431295000Z

ค่าคงที่ของช่วงเวลา:

  • dur_us() - 1 ไมโครวินาที
  • dur_ms() - 1 มิลลิวินาที
  • dur_s() - 1 วินาที
  • dur_m() - 1 นาที
  • dur_h() - 1 ชั่วโมง

การปัดค่า

ฟังก์ชันสำหรับปัดค่าเวลาให้มีความละเอียดตามฟิลด์ที่กำหนด:

select 'original  = ' || time_fmt_iso(t.v) from t union all
select 'millennium = ' || time_fmt_iso(time_trunc(t.v, 'millennium')) from t;

การจัดรูปแบบ

ฟังก์ชันที่คืนค่าสตริงเวลาแบบ ISO 8601:

select time_fmt_iso(time_date(2011, 11, 18, 15, 56, 35, 666777888), 3*3600);  -- 2011-11-18T18:56:35.666777888+03:00

ค่าคงที่ของช่วงเวลา

ฟังก์ชันที่คืนค่าช่วงเวลาทั่วไปในหน่วยนาโนวินาที:

select dur_ns();  -- 1
select dur_us();  -- 1000

กิตติกรรมประกาศ

ส่วนขยายนี้พัฒนาด้วย C และออกแบบกับพัฒนาบนพื้นฐานของแพ็กเกจ time ใน standard library ของ Go (BSD 3-Clause License)

การติดตั้งและวิธีใช้งาน

  1. ดาวน์โหลดรีลีสล่าสุด
  2. ใช้งานใน SQLite command-line interface:
    sqlite> .load ./time
    sqlite> select time_now();
    

สรุปโดย GN⁺

  • ส่วนขยาย sqlean-time เพิ่มความสามารถด้านวันที่/เวลาแบบความละเอียดสูงให้ SQLite ทำให้รองรับการคำนวณเวลาหลากหลายรูปแบบ
  • สามารถจัดการเวลาและช่วงเวลาในระดับนาโนวินาที จึงรองรับการคำนวณเวลาที่ละเอียดมาก
  • มีทั้งฟังก์ชันจัดรูปแบบและเปรียบเทียบเวลาหลากหลายแบบ ทำให้นักพัฒนาใช้งานได้สะดวก
  • มีความสามารถมากกว่าฟังก์ชันวันที่พื้นฐานของ SQLite อย่างชัดเจน จึงเหมาะกับโปรเจ็กต์ที่ต้องการการคำนวณเวลาที่ซับซ้อน

1 ความคิดเห็น

 
GN⁺ 2024-08-16
ความคิดเห็นบน Hacker News
  • มีคำถามว่ารองรับกรณีพิเศษของการเปลี่ยนเขตเวลาและความไม่ต่อเนื่องของเวลาท้องถิ่นที่ Jon Skeet เคยอธิบายไว้หรือไม่

    • ลิงก์ที่เกี่ยวข้อง: Stack Overflow
    • ลิงก์วิดีโออธิบายความยาว 10 นาทีจาก Computerphile: YouTube
  • ไม่ควรสร้างไลบรารีวันที่/เวลาและไลบรารีเข้ารหัสลับขึ้นมาใช้เอง

    • edge case ที่ไม่มีวันจบอาจก่อปัญหาได้
    • เป็นเหตุผลที่ทำให้รู้สึกกังขาเมื่อเจอไลบรารีใหม่
  • การแทนค่า/ขนาดเวลาที่ต่างกันสามแบบเป็นเรื่องน่าสนใจ

    • สงสัยว่ามี use case แบบไหนที่ต้องการความละเอียดระดับนาโนวินาทีตลอดช่วงเวลาหลายพันล้านปี
    • รู้สึกสับสนที่ความละเอียดระดับนาโนวินาทีให้ช่วงได้เพียง ±290 ปี
  • การระบุให้ชัดว่าใช้ signed integer หรือไม่เป็นเรื่องสำคัญ

    • อ่านเอกสารแล้วอาจตีความได้ว่าเป็น signed integer หรืออาจไม่ใช่
    • ถ้าเป็น signed integer ก็อาจมีหลาย bit string ที่แทนวันที่และเวลาเดียวกันได้
  • อยากให้ SQLite3 มีระบบชนิดข้อมูลที่ขยายต่อได้

  • มองว่านี่เจ๋งมาก พร้อมทั้งชี้ถึงความสามารถสำคัญที่ SQLite ยังขาดอยู่

  • โต้แย้งว่าฐานข้อมูลควรติดตามหน่วยด้วย

    • ตัวอย่างเช่น ควรระบุได้ว่าคอลัมน์เวลาแทนค่าเป็นหน่วยวินาทีแบบ float64
    • ฐานข้อมูลควรแปลง "2h" เป็น 7200.0 วินาทีและนำไปเปรียบเทียบระหว่าง table scan ได้
    • เคยเขียนฐานข้อมูล SQL เฉพาะทางที่จัดการหน่วยแบบนี้มาก่อน แต่หลังจากนั้นก็ไม่เคยเห็นอีก
    • ไม่ใช่แค่เวลา แต่ควรรองรับทุกหน่วย เช่น มวล ปริมาตร ข้อมูล อุณหภูมิ เป็นต้น
    • สามารถสอนให้ฐานข้อมูลปฏิเสธการคำนวณที่ไม่มีความหมายทางคณิตศาสตร์ เพื่อจับข้อผิดพลาดได้ตั้งแต่เนิ่น ๆ
  • มีคำถามว่าระหว่างการแทนค่านาโนวินาทีกับปีที่อยู่นอกช่วงระดับนาโน แบบไหนมีประโยชน์กว่ากัน

    • เพราะไม่ได้ทำวิทยาศาสตร์แบบ "แม่นยำเป๊ะ" คุณค่าของนาโนวินาทีจึงมีจำกัด
    • ดูเหมือนว่าการแทนวันที่ทางประวัติศาสตร์ได้จะจำเป็นบ่อยกว่า
  • เสนอให้ใช้ Unix timestamp แบบสไตล์ golang ในหน่วยนาโนวินาที โดยใช้ signed int64

    • แม้อาจครอบคลุมเวลาได้ไม่ถึงหลายล้านปีที่ความละเอียดระดับนาโนวินาที แต่ก็สงสัยว่าเราจำเป็นต้องใช้จริงหรือไม่
  • โต้แย้งว่าไม่ควรใช้คำว่า "วินาทีหลัง epoch" เว้นแต่จะหมายถึงสิ่งนั้นอย่างแม่นยำ

    • ตัวอย่างคิวรี: select time_sub(time_date(2011, 11, 19), time_date(1311, 11, 18));