Sqlite: CURRENT_TIMESTAMP อยู่ใน GMT ไม่ใช่เขตเวลาของเครื่อง


118

ฉันมีตาราง sqlite (v3) พร้อมคำจำกัดความคอลัมน์นี้:

"timestamp" DATETIME DEFAULT CURRENT_TIMESTAMP

เซิร์ฟเวอร์ที่ฐานข้อมูลนี้อาศัยอยู่อยู่ในเขตเวลา CST เมื่อฉันแทรกลงในตารางของฉันโดยไม่รวมคอลัมน์การประทับเวลา sqlite จะเติมข้อมูลในฟิลด์นั้นโดยอัตโนมัติด้วยการประทับเวลาปัจจุบันใน GMT ไม่ใช่ CST

มีวิธีแก้ไขคำสั่งแทรกของฉันเพื่อบังคับให้ประทับเวลาที่จัดเก็บไว้เป็น CST หรือไม่ ในทางกลับกันมันน่าจะดีกว่าที่จะจัดเก็บไว้ใน GMT (ในกรณีที่ฐานข้อมูลถูกย้ายไปยังเขตเวลาอื่นเป็นต้น) ดังนั้นมีวิธีใดบ้างที่ฉันสามารถแก้ไข SQL ที่เลือกเพื่อแปลงการประทับเวลาที่เก็บไว้เป็น CST ได้เมื่อฉัน แยกมันออกจากตาราง?


24
การจัดเก็บการประทับเวลาใน UTC ถือเป็นแนวทางปฏิบัติที่ดีที่สุด แปลงเป็นเวลาท้องถิ่นเมื่อนำเสนอพวกเขา
csl

2
POSIX กำหนดการประทับเวลาเป็น UTC: stackoverflow.com/a/34107910/895245
Ciro Santilli 郝海东冠状病六四事件法轮功

1
ไม่แน่ใจว่าปัญหาประเภทนี้ต้องเผชิญกับฐานข้อมูลประเภทอื่นหรือไม่ ลองนึกดูว่าถ้าคุณสร้างฐานข้อมูลในญี่ปุ่นคุณสามารถใช้ข้อมูลในสหราชอาณาจักรได้!
NoChance

คำตอบ:


145

ฉันพบในเอกสาร sqlite ( https://www.sqlite.org/lang_datefunc.html ) ข้อความนี้:

คำนวณวันที่และเวลาด้วยการประทับเวลา Unix 1092941466 และชดเชยเขตเวลาท้องถิ่นของคุณ

SELECT datetime(1092941466, 'unixepoch', 'localtime');

มันดูไม่ตรงกับความต้องการของฉันฉันเลยลองเปลี่ยนฟังก์ชั่น "datetime" สักหน่อยแล้วก็จัดการกับสิ่งนี้:

select datetime(timestamp, 'localtime')

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


64

เพียงใช้เวลาท้องถิ่นเป็นค่าเริ่มต้น:

CREATE TABLE whatever(
     ....
     timestamp DATE DEFAULT (datetime('now','localtime')),
     ...
);

5
ปัญหานี้มีปัญหาในการโอนข้ามเขตเวลาไม่ได้ใช่หรือไม่?
Vadim Peretokin

ใช่มันใช้ไม่ได้สำหรับฉันเช่นกันฉันใช้ sqlite ใน ZF2
Rohutech

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

1
@xFighter ฉันรู้ว่าแอปพลิเคชันของฉันจะไม่ต้องการวิธีอื่น ... จนกว่าคุณจะลืมสิ่งนั้นหรือมีคนอื่นใช้งานอยู่
MKesper

16

ตามกฎแล้วคุณควรทิ้งการประทับเวลาไว้ในฐานข้อมูลใน GMT และแปลงเป็น / จากเวลาท้องถิ่นในอินพุต / เอาต์พุตเมื่อคุณสามารถแปลงเป็นการประทับเวลาท้องถิ่นของผู้ใช้ (ไม่ใช่ของเซิร์ฟเวอร์)

คงจะดีไม่น้อยหากคุณสามารถทำสิ่งต่อไปนี้ได้

SELECT DATETIME(col, 'PDT')

... เพื่อส่งออกการประทับเวลาสำหรับผู้ใช้ใน Pacific Daylight Time น่าเสียดายที่ไม่ได้ผล อย่างไรก็ตามตามบทช่วยสอน SQLite นี้ (เลื่อนลงไปที่ "คำสั่งวันที่และเวลาอื่น ๆ ") คุณสามารถขอเวลาจากนั้นใช้การชดเชย (เป็นชั่วโมง) ในเวลาเดียวกัน ดังนั้นหากคุณทราบการชดเชยเขตเวลาของผู้ใช้คุณก็ทำได้ดี

ไม่เกี่ยวข้องกับกฎการออมแสงแม้ว่า ...


15

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

"timestamp" TEXT DEFAULT (strftime('%Y-%m-%dT%H:%M','now', 'localtime'))

แน่นอนส่วน% Y-% m-% dT% H:% M เป็นทางเลือก; มันเป็นเพียงวิธีที่ฉันชอบเก็บเวลาไว้ [นอกจากนี้หากการแสดงผลของฉันถูกต้องจะไม่มีประเภทข้อมูล "DATETIME" ใน sqlite ดังนั้นจึงไม่สำคัญว่าจะใช้ TEXT หรือ DATETIME เป็นประเภทข้อมูลในการประกาศคอลัมน์]


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

9

เมื่อมีคอลัมน์ที่กำหนดด้วย " NOT NULL DEFAULT CURRENT_TIMESTAMP" ระเบียนที่แทรกจะได้รับการกำหนดเวลา UTC / GMT เสมอ

นี่คือสิ่งที่ฉันทำเพื่อหลีกเลี่ยงการรวมเวลาในคำสั่ง INSERT / UPDATE ของฉัน:

--Create a table having a CURRENT_TIMESTAMP:
CREATE TABLE FOOBAR (
    RECORD_NO INTEGER NOT NULL,
    TO_STORE INTEGER,
    UPC CHAR(30),
    QTY DECIMAL(15,4),
    EID CHAR(16),
    RECORD_TIME NOT NULL DEFAULT CURRENT_TIMESTAMP)

--Create before update and after insert triggers:
CREATE TRIGGER UPDATE_FOOBAR BEFORE UPDATE ON FOOBAR
    BEGIN
       UPDATE FOOBAR SET record_time = datetime('now', 'localtime')
       WHERE rowid = new.rowid;
    END

CREATE TRIGGER INSERT_FOOBAR AFTER INSERT ON FOOBAR
    BEGIN
       UPDATE FOOBAR SET record_time = datetime('now', 'localtime')
       WHERE rowid = new.rowid;
    END

ทดสอบดูว่าใช้ได้ไหม ...

--INSERT a couple records into the table:
INSERT INTO foobar (RECORD_NO, TO_STORE, UPC, PRICE, EID)
    VALUES (0, 1, 'xyz1', 31, '777')

INSERT INTO foobar (RECORD_NO, TO_STORE, UPC, PRICE, EID)
    VALUES (1, 1, 'xyz2', 32, '777')

--UPDATE one of the records:
UPDATE foobar SET price = 29 WHERE upc = 'xyz2'

--Check the results:
SELECT * FROM foobar

หวังว่าจะช่วยได้


1
สิ่งนี้แตกต่างจากคำตอบที่ให้ไว้โดยผู้ใช้ hoju, CREATE TABLE อะไรก็ตาม (.... timestamp DATE DEFAULT (datetime ('now', 'localtime'),.) ;?
NoChance



4

คุณยังสามารถแปลงคอลัมน์เวลาเป็นการประทับเวลาได้โดยใช้ strftime ():

SELECT strftime('%s', timestamp) as timestamp FROM ... ;

ให้คุณ:

1454521888

คอลัมน์ตาราง 'การประทับเวลา' สามารถเป็นช่องข้อความได้โดยใช้current_timestampเป็นค่าเริ่มต้น

ไม่มี strftime:

SELECT timestamp FROM ... ;

ให้คุณ:

2016-02-03 17:51:28 น




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