ค่าการประทับเวลาถูกจัดเก็บแตกต่างกันใน PostgreSQL เมื่อชนิดข้อมูลWITH TIME ZONE
เทียบกับWITHOUT TIME ZONE
หรือไม่ สามารถแสดงความแตกต่างกับกรณีทดสอบอย่างง่ายได้หรือไม่?
ค่าการประทับเวลาถูกจัดเก็บแตกต่างกันใน PostgreSQL เมื่อชนิดข้อมูลWITH TIME ZONE
เทียบกับWITHOUT TIME ZONE
หรือไม่ สามารถแสดงความแตกต่างกับกรณีทดสอบอย่างง่ายได้หรือไม่?
คำตอบ:
ความแตกต่างได้รับความคุ้มครองในเอกสาร PostgreSQL สำหรับวันที่ / เวลาประเภท ใช่การรักษาTIME
หรือTIMESTAMP
แตกต่างระหว่างหนึ่งหรือWITH TIME ZONE
WITHOUT TIME ZONE
ไม่ส่งผลกระทบต่อวิธีการจัดเก็บค่า มันมีผลต่อวิธีการตีความ
ผลกระทบของเขตเวลาในประเภทข้อมูลเหล่านี้ครอบคลุมเฉพาะในเอกสาร ความแตกต่างเกิดขึ้นจากสิ่งที่ระบบสามารถรู้ถึงคุณค่าอย่างสมเหตุสมผล:
ด้วยเขตเวลาเป็นส่วนหนึ่งของค่าค่าสามารถแสดงผลเป็นเวลาท้องถิ่นในลูกค้า
หากไม่มีเขตเวลาเป็นส่วนหนึ่งของค่าเขตเวลาเริ่มต้นที่ชัดเจนคือ UTC ดังนั้นจึงมีการแสดงผลสำหรับเขตเวลานั้น
พฤติกรรมนั้นแตกต่างกันไปตามปัจจัยอย่างน้อยสามประการ:
WITH TIME ZONE
หรือWITHOUT TIME ZONE
) ของค่านี่คือตัวอย่างที่ครอบคลุมการรวมกันของปัจจัยเหล่านี้:
foo=> SET TIMEZONE TO 'Japan';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+09
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 06:00:00+09
(1 row)
foo=> SET TIMEZONE TO 'Australia/Melbourne';
SET
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 00:00:00+11
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP;
timestamp
---------------------
2011-01-01 00:00:00
(1 row)
foo=> SELECT '2011-01-01 00:00:00+03'::TIMESTAMP WITH TIME ZONE;
timestamptz
------------------------
2011-01-01 08:00:00+11
(1 row)
timestamp with time zone
และtimestamp without time zone
ใน Postgres ไม่ได้เก็บข้อมูลโซนเวลาจริงๆ คุณสามารถยืนยันสิ่งนี้ได้อย่างรวดเร็วที่หน้าเอกสารประเภทข้อมูล: ทั้งสองประเภทใช้จำนวนออคเต็ตเท่ากันและมีช่วงของการบันทึกค่าดังนั้นจึงไม่มีที่ว่างสำหรับการจัดเก็บข้อมูลเขตเวลา ข้อความของหน้ายืนยันสิ่งนี้ ข้อผิดพลาดบางอย่าง: "ไม่มี tz" หมายถึง "ละเว้น offset เมื่อแทรกข้อมูล" และ "with tz" หมายถึง "ใช้ offset เพื่อปรับค่าเป็น UTC"
ฉันพยายามอธิบายให้เข้าใจมากกว่าเอกสาร PostgreSQL ที่อ้างถึง
ทั้ง TIMESTAMP
รุ่นเก็บเขตเวลา (หรือออฟเซ็ต) แม้ชื่อจะแนะนำก็ตาม ความแตกต่างคือในการตีความข้อมูลที่จัดเก็บ (และในแอปพลิเคชันที่ต้องการ) ไม่ใช่ในรูปแบบที่จัดเก็บเอง:
TIMESTAMP WITHOUT TIME ZONE
ร้านค้าวันที่เวลาท้องถิ่น (aka. วันที่ปฏิทินผนังและเวลานาฬิกาแขวน) เขตเวลาของมันไม่ได้ระบุเท่าที่ PostgreSQL สามารถบอกได้ (แม้ว่าแอปพลิเคชันของคุณอาจรู้ว่ามันคืออะไร) ดังนั้น PostgreSQL จะไม่มีการแปลงที่เกี่ยวข้องกับเขตเวลาในอินพุตหรือเอาต์พุต หากค่าถูกป้อนลงในฐานข้อมูลเป็น'2011-07-01 06:30:30'
แล้วไม่มี mater ในเขตเวลาใดที่คุณแสดงในภายหลังมันจะยังคงบอกว่าปี 2011, เดือน 07, วันที่ 07, วันที่ 01, 06 ชั่วโมง, 30 นาทีและ 30 วินาที (ในบางรูปแบบ) PostgreSQL '2011-07-01 06:30:30+00'
และเขตเวลาใด ๆ ที่คุณระบุในอินพุตจะถูกละเว้นดังนั้นจึง'2011-07-01 06:30:30+05'
เหมือนกันกับ Just'2011-07-01 06:30:30'
เป็นเช่นเดียวกับเพียงสำหรับนักพัฒนา Java: java.time.LocalDateTime
มันคล้ายกับ
TIMESTAMP WITH TIME ZONE
เก็บจุดบนเส้นเวลา UTC ลักษณะที่ปรากฏ (จำนวนชั่วโมงนาที ฯลฯ ) ขึ้นอยู่กับเขตเวลาของคุณ แต่จะอ้างอิงทันที "กายภาพ" ทันที (เช่นช่วงเวลาของเหตุการณ์จริงทางกายภาพ) อินพุตถูกแปลงภายในเป็น UTC และเป็นวิธีการจัดเก็บ สำหรับสิ่งนั้นการชดเชยของอินพุตจะต้องเป็นที่รู้จักดังนั้นเมื่ออินพุตไม่มีออฟเซ็ตหรือโซนเวลาที่ชัดเจน (เช่นแน่นอน)'2011-07-01 06:30:30'
) จะถือว่าอยู่ในเขตเวลาปัจจุบันของเซสชัน PostgreSQL มิฉะนั้นจะใช้ออฟเซ็ตหรือโซนเวลาที่ระบุอย่างชัดเจน (เหมือนใน'2011-07-01 06:30:30+05'
) ผลลัพธ์จะถูกแปลงเป็นเขตเวลาปัจจุบันของเซสชัน PostgreSQL สำหรับนักพัฒนา Java: มันคล้ายกับjava.time.Instant
(โดยมีความละเอียดต่ำกว่า) แต่ด้วย JDBC และ JPA 2.2 คุณควรแมปกับjava.time.OffsetDateTime
( java.util.Date
หรือjava.sql.Timestamp
บางคนบอกว่าทั้งสองTIMESTAMP
รูปแบบเก็บวันที่เวลา UTC ชนิดของมัน แต่มันทำให้สับสนในความคิดของฉัน TIMESTAMP WITHOUT TIME ZONE
ถูกจัดเก็บไว้เช่น a TIMESTAMP WITH TIME ZONE
ซึ่งแสดงผลด้วยเขตเวลา UTC เกิดขึ้นเพื่อให้ปีเดือนวันชั่วโมงนาทีวินาทีและ microseconds เดียวกันกับที่พวกเขาอยู่ในวันเวลาท้องถิ่น แต่มันไม่ได้หมายถึงการเป็นตัวแทนของจุดบนเส้นเวลาที่การตีความ UTC บอกว่ามันเป็นเพียงวิธีเข้ารหัสฟิลด์วันที่และเวลาท้องถิ่น (เป็นกลุ่มของจุดบนเส้นเวลาเนื่องจากเขตเวลาจริงไม่ใช่ UTC เราไม่ทราบว่ามันคืออะไร)
TIMESTAMP WITH TIME ZONE
Instant
ทั้งสองเป็นตัวแทนของจุดบนเส้นเวลาใน UTC Instant
เป็นที่ต้องการในความคิดของฉันมากกว่าOffsetDateTime
ที่มันเป็นตัวเองมากขึ้นเอกสาร: การTIMESTAMP WITH TIME ZONE
ถูกดึงเสมอจากฐานข้อมูลเป็น UTC และInstant
อยู่เสมอในเวลา UTC เพื่อให้การแข่งขันเป็นธรรมชาติในขณะที่OffsetDateTime
สามารถดำเนินการชดเชยอื่น ๆ
OffsetDateTime
เป็นประเภท Java ที่แมป ฉันไม่แน่ใจว่าInstance
ยังคงสนับสนุนอย่างไม่เป็นทางการอยู่หรือไม่
'2011-07-01 06:30:30+00'
และ'2011-07-01 06:30:30+05'
ถูกละเว้น แต่ฉันสามารถทำได้insert into test_table (date) values ('2018-03-24T00:00:00-05:00'::timestamptz);
และมันจะแปลงมันเป็น utc อย่างถูกต้อง โดยที่ date เป็น timestamp โดยไม่มี timezone ฉันพยายามเข้าใจว่าค่าหลักของการประทับเวลาด้วยเขตเวลาคืออะไรและมีปัญหา
::timestamptz
คุณยุ่งเรื่องกับ เมื่อคุณแปลงสตริงเป็นTIMESTAMP WITH TIME ZONE
และเมื่อนั้นจะถูกแปลงเป็นเพิ่มเติมWITHOUT TIME ZONE
จะเก็บวัน "ปฏิทินผนัง" และเวลานาฬิกาแขวนของทันทีที่เห็นจากโซนเวลาเซสชันของคุณ (ซึ่งอาจเป็น UTC) มันจะยังคงเป็นเพียงการประทับเวลาในท้องถิ่นที่มีการชดเชยที่ไม่ได้ระบุ (ไม่มีโซน)
นี่คือตัวอย่างที่ควรช่วย หากคุณมีการประทับเวลาด้วยเขตเวลาคุณสามารถแปลงการประทับเวลานั้นเป็นเขตเวลาอื่น หากคุณไม่มีเขตเวลาพื้นฐานมันจะไม่ถูกแปลงอย่างถูกต้อง
SELECT now(),
now()::timestamp,
now() AT TIME ZONE 'CST',
now()::timestamp AT TIME ZONE 'CST'
เอาท์พุท:
-[ RECORD 1 ]---------------------------
now | 2018-09-15 17:01:36.399357+03
now | 2018-09-15 17:01:36.399357
timezone | 2018-09-15 08:01:36.399357
timezone | 2018-09-16 02:01:36.399357+03
timestamp
และtimestamptz
หมายถึง timestamptz
หมายถึงจุดสัมบูรณ์ในเวลา (UTC) โดยที่timestamp
แสดงถึงสิ่งที่นาฬิกาแสดงในเขตเวลาที่แน่นอน ดังนั้นเมื่อแปลงtimestamptz
เป็นเขตเวลาคุณจะถามว่านาฬิกาแสดงอะไรในนิวยอร์ก ณ เวลาที่แน่นอนนี้ ในขณะที่เมื่อ "แปลง" timestamp
คุณกำลังถามว่าอะไรคือจุดที่แน่นอนในเวลาที่นาฬิกาในนิวยอร์กแสดง x
AT TIME ZONE
ปลูกสร้างนี้เป็นเกมพัฒนาสมองของตัวเองแม้ว่าคุณจะเข้าใจประเภทWITH
vs. WITHOUT TIME ZONE
ดังนั้นจึงเป็นทางเลือกที่อยากรู้อยากเห็นสำหรับการอธิบายพวกเขา (: ( AT TIME ZONE
แปลงการWITH TIME ZONE
ประทับเวลาเป็นการWITHOUT TIME ZONE
ประทับเวลาและในทางกลับกัน ... ไม่ชัดเจน)
now()::timestamp AT TIME ZONE 'CST'
ไม่เข้าท่าเว้นแต่คุณจะรู้ว่านาฬิกา CST สำหรับโซน 'CST' จะแสดงเวลาที่นาฬิกาท้องถิ่นของคุณกำลังแสดงอยู่หรือไม่