MySQL ควรตั้งค่าเขตเวลาเป็น UTC หรือไม่


149

ติดตามคำถามของ /server/191331/should-servers-have-their-timezone-set-to-gmt-utc

ควรตั้งค่าเขตเวลา MySQL เป็น UTC หรือควรตั้งค่าเขตเวลาเดียวกันกับที่ตั้งเซิร์ฟเวอร์หรือ PHP (ถ้าไม่ใช่ UTC)

ข้อดีและข้อเสียคืออะไร?


stackoverflow.com/a/1650406/175071แบ่งปันเหตุผลที่ดีในการใช้ UTC
Timo Huovinen

UTC ไม่ใช่เขตเวลา UTC เป็นมาตรฐาน GMT เป็นเขตเวลา zachholman.com/talk/utc-is-enough-for-everyone-right
agoldev

อีกเหตุผลที่ดีในการใช้ UTC dba.stackexchange.com/questions/161416/ …
Timo Huovinen

คำตอบ:


533

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

ในทางตรงกันข้ามหากคุณมีการควบคุมเขตเวลาของเซิร์ฟเวอร์ที่คุณทำงานด้วยคุณสามารถตั้งค่าทุกอย่างให้เป็น UTC ภายในและไม่ต้องกังวลเกี่ยวกับเขตเวลาและ DST

นี่คือบันทึกบางส่วนที่ฉันเก็บรวบรวมวิธีการทำงานกับเขตเวลาเป็นรูปแบบของ cheatsheet สำหรับฉันและผู้อื่นซึ่งอาจมีผลต่อเขตเวลาที่บุคคลจะเลือกสำหรับเซิร์ฟเวอร์ของเขาและวิธีที่เขา / เธอจะจัดเก็บวันที่และเวลา

สูตรคำนวณเวลา MySQL

หมายเหตุ:

  1. การเปลี่ยนเขตเวลาจะไม่เปลี่ยนวันที่และเวลาที่เก็บไว้แต่จะเลือกช่วงเวลาที่แตกต่างจากคอลัมน์เวลา
  2. คำเตือน! UTC มีเวลากระโดดวินาทีลักษณะเหล่านี้เป็น '2012-06-30 23:59:60' และสามารถเพิ่มแบบสุ่มโดยมีการแจ้งล่วงหน้า 6 เดือนเนื่องจากการหมุนรอบโลกช้าลง
  3. GMT สร้างความสับสนให้กับวินาทีซึ่งเป็นสาเหตุที่ UTC ถูกคิดค้น

  4. คำเตือน! เขตเวลาในภูมิภาคที่แตกต่างกันอาจทำให้เกิดค่าวันที่และเวลาเดียวกันเนื่องจากประหยัดเวลาตามฤดูกาล

  5. คอลัมน์ประทับเวลาสนับสนุนเฉพาะวันที่ 1970/01/01 00:00:01 เพื่อ 2038/01/19 03:14:07 UTC เนื่องจากข้อ จำกัด
  6. ภายในคอลัมน์เวลาประทับ MySQLจะถูกจัดเก็บเป็นUTCแต่เมื่อเลือกวันที่ MySQL จะแปลงเป็นเขตเวลาปัจจุบันโดยอัตโนมัติ

    เมื่อจัดเก็บวันที่ในเวลาประทับ MySQL จะถือว่าวันที่อยู่ในเขตเวลาปัจจุบันและแปลงเป็น UTC เพื่อจัดเก็บ

  7. MySQL สามารถจัดเก็บวันที่บางส่วนในคอลัมน์วันที่และเวลาได้เช่นนี้ "2013-00-00 04:00:00"
  8. MySQL จัดเก็บ "0000-00-00 00:00:00" ถ้าคุณตั้งค่าคอลัมน์วันที่และเวลาเป็น NULL ยกเว้นว่าคุณตั้งค่าคอลัมน์ให้อนุญาตเป็นโมฆะเมื่อคุณสร้างมันขึ้นมา
  9. อ่านนี่

ในการเลือกคอลัมน์เวลาประทับในรูปแบบ UTC

ไม่ว่าเขตเวลา MySQL ปัจจุบันเซสชันใด:

SELECT 
CONVERT_TZ(`timestamp_field`, @@session.time_zone, '+00:00') AS `utc_datetime` 
FROM `table_name`

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

SELECT `timestamp_field` FROM `table_name`

ในการเลือกวันที่และเวลาปัจจุบันใน UTC:

SELECT UTC_TIMESTAMP();
SELECT UTC_TIMESTAMP;
SELECT CONVERT_TZ(NOW(), @@session.time_zone, '+00:00');

ตัวอย่างผลลัพธ์: 2015-03-24 17:02:41

เพื่อเลือกวันที่และเวลาปัจจุบันในเขตเวลาของเซสชัน

SELECT NOW();
SELECT CURRENT_TIMESTAMP;
SELECT CURRENT_TIMESTAMP();

เพื่อเลือกเขตเวลาที่ตั้งไว้เมื่อเซิร์ฟเวอร์เปิดใช้งาน

SELECT @@system_time_zone;

ผลตอบแทน "MSK" หรือ "+04: 00" สำหรับเวลามอสโกมี (หรือเคย) ข้อผิดพลาด MySQL ที่ถ้าตั้งค่าให้เป็นตัวเลขชดเชยมันจะไม่ปรับเวลาออมแสงตามฤดูกาล

เพื่อให้ได้เขตเวลาปัจจุบัน

SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);

มันจะกลับมา 02:00:00 ถ้าเขตเวลาของคุณคือ +2: 00

ในการรับค่าการประทับเวลา UNIX ปัจจุบัน (เป็นวินาที):

SELECT UNIX_TIMESTAMP(NOW());
SELECT UNIX_TIMESTAMP();

เพื่อรับคอลัมน์เวลาประทับเป็น UNIX Timestamp

SELECT UNIX_TIMESTAMP(`timestamp`) FROM `table_name`

ในการรับคอลัมน์ UTC datetime เป็นเวลาประทับ UNIX

SELECT UNIX_TIMESTAMP(CONVERT_TZ(`utc_datetime`, '+00:00', @@session.time_zone)) FROM `table_name`

รับ datetime ของเขตเวลาปัจจุบันจากจำนวนเต็ม UNIX timestamp ที่เป็นค่าบวก

SELECT FROM_UNIXTIME(`unix_timestamp_int`) FROM `table_name`

รับ UTC datetime จากเวลาประทับ UNIX

SELECT CONVERT_TZ(FROM_UNIXTIME(`unix_timestamp_int`), @@session.time_zone, '+00:00') 
FROM `table_name`

รับวันที่และเวลาปัจจุบันของเขตเวลาจากจำนวนเต็ม UNIX การประทับเวลาติดลบ

SELECT DATE_ADD('1970-01-01 00:00:00',INTERVAL -957632400 SECOND) 

มี 3 สถานที่ที่อาจตั้งค่าเขตเวลาใน MySQL:

หมายเหตุ: เขตเวลาสามารถตั้งค่าได้ 2 รูปแบบ:

  1. การชดเชยจาก UTC: '+00: 00', '+10: 00' หรือ '-6: 00'
  2. เป็นเขตเวลาที่มีชื่อ: 'Europe / Helsinki', 'US / Eastern' หรือ 'MET'

เขตเวลาที่มีชื่อสามารถใช้งานได้ต่อเมื่อตารางข้อมูลโซนเวลาในฐานข้อมูล mysql ถูกสร้างและเติมข้อมูลแล้ว

ในไฟล์ "my.cnf"

default_time_zone='+00:00'

หรือ

timezone='UTC'

ตัวแปร @@ global.time_zone

เพื่อดูว่าพวกเขาตั้งค่าเป็นอะไร

SELECT @@global.time_zone;

หากต้องการตั้งค่าให้ใช้อย่างใดอย่างหนึ่ง:

SET GLOBAL time_zone = '+8:00';
SET GLOBAL time_zone = 'Europe/Helsinki';
SET @@global.time_zone='+00:00';

ตัวแปร @@ session.time_zone

SELECT @@session.time_zone;

ในการตั้งค่าให้ใช้อย่างใดอย่างหนึ่ง:

SET time_zone = 'Europe/Helsinki';
SET time_zone = "+00:00";
SET @@session.time_zone = "+00:00";

ทั้ง "@@ global.time_zone variable" และ "@@ session.time_zone variable" อาจกลับมา "SYSTEM" ซึ่งหมายความว่าพวกเขาใช้ timezone set ใน "my.cnf"

เพื่อให้ชื่อเขตเวลาใช้งานได้ (ถึงแม้จะเป็นเขตเวลาเริ่มต้น) คุณจะต้องตั้งค่าตารางข้อมูลเขตเวลาของคุณด้วย: http://dev.mysql.com/doc/refman/5.1/en/time-zone-support HTML

หมายเหตุ: คุณไม่สามารถทำได้เนื่องจากจะส่งคืนค่า NULL:

SELECT 
CONVERT_TZ(`timestamp_field`, TIMEDIFF(NOW(), UTC_TIMESTAMP), '+00:00') AS `utc_datetime` 
FROM `table_name`

ตั้งค่าตารางเขตเวลา mysql

ในCONVERT_TZการทำงานคุณต้องกรอกตารางเขตเวลา

SELECT * FROM mysql.`time_zone` ;
SELECT * FROM mysql.`time_zone_leap_second` ;
SELECT * FROM mysql.`time_zone_name` ;
SELECT * FROM mysql.`time_zone_transition` ;
SELECT * FROM mysql.`time_zone_transition_type` ;

ถ้ามันว่างเปล่าให้เติมมันด้วยการรันคำสั่งนี้

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql

หากคำสั่งนี้ให้ข้อผิดพลาด " ข้อมูลยาวเกินไปสำหรับคอลัมน์ 'ตัวย่อ' ที่แถว 1 " อาจเกิดจากอักขระ NULL ที่ต่อท้ายตัวย่อเขตเวลา

การแก้ไขการเรียกใช้นี้

mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
(if the above gives error "data too long for column 'abbreviation' at row 1")
mysql_tzinfo_to_sql /usr/share/zoneinfo > /tmp/zut.sql

echo "SET SESSION SQL_MODE = '';" > /tmp/mysql_tzinfo_to.sql
cat /tmp/zut.sql >> /tmp/mysql_tzinfo_to.sql

mysql --defaults-file=/etc/mysql/my.cnf --user=verifiedscratch -p mysql < /tmp/mysql_tzinfo_to.sql

(ตรวจสอบให้แน่ใจว่ากฎของเซิร์ฟเวอร์ของคุณเป็นรุ่นล่าสุดแล้วzdump -v Europe/Moscow | grep 2011 https://chrisjean.com/updating-daylight-saving-time-on-linux/ )

ดูประวัติการเปลี่ยนแปลง DST (ปรับเวลาตามฤดูกาล) แบบเต็มสำหรับทุกเขตเวลา

SELECT 
tzn.Name AS tz_name,
tztt.Abbreviation AS tz_abbr,
tztt.Is_DST AS is_dst,
tztt.`Offset` AS `offset`,
DATE_ADD('1970-01-01 00:00:00',INTERVAL tzt.Transition_time SECOND)  AS transition_date
FROM mysql.`time_zone_transition` tzt
INNER JOIN mysql.`time_zone_transition_type` tztt USING(Time_zone_id, Transition_type_id)
INNER JOIN mysql.`time_zone_name` tzn USING(Time_zone_id)
-- WHERE tzn.Name LIKE 'Europe/Moscow' -- Moscow has weird DST changes
ORDER BY tzt.Transition_time ASC

CONVERT_TZ ยังใช้การเปลี่ยนแปลง DST ใด ๆ ที่จำเป็นตามกฎในตารางด้านบนและวันที่ที่คุณใช้

หมายเหตุ:
ตามเอกสารค่าที่คุณตั้งค่าสำหรับ time_zone จะไม่เปลี่ยนแปลงหากคุณตั้งค่าเป็น "+01: 00" ตัวอย่างเช่น time_zone จะถูกตั้งค่าเป็นออฟเซ็ตจาก UTC ซึ่งไม่เป็นไปตาม DST ดังนั้น มันจะยังคงเหมือนเดิมตลอดทั้งปี

เฉพาะเขตเวลาที่มีชื่อเท่านั้นที่จะเปลี่ยนเวลาในช่วงเวลากลางวันตามฤดูกาล

ตัวย่อเช่นCETจะเป็นฤดูหนาวและCESTเป็นฤดูร้อนในขณะที่ +01: 00 จะเป็นUTCเวลา + 1 ชั่วโมงและทั้งสองจะไม่เปลี่ยนแปลงด้วย DST

systemเขตเวลาจะเป็นเขตของเครื่องโฮสต์ที่มีการติดตั้ง MySQL (ยกเว้น MySQL ล้มเหลวในการตรวจสอบได้)

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการทำงานกับ DST ได้ที่นี่

คำถามที่เกี่ยวข้อง:

แหล่งที่มา:


ดังนั้นหากฉันตั้งประเภทคอลัมน์เป็น timestamp และ time_zone ของฉันคือ +12: 00 และฉันต้องการอัปเดตคอลัมน์โดยใช้วันที่ / เวลาตาม utc มีวิธีใดที่จะรวมเขตเวลาในคำสั่งอัปเดตหรือฉันควรใช้ convert_tz เช่น. การปรับปรุงtableชุดmodified= '2016/07/07 08:10 00: 00'
bumperbox

2
@bumperbox mysql จะถือว่าวันที่ที่คุณให้คอลัมน์การประทับเวลาอยู่ในเขตเวลาเดียวกับเซิร์ฟเวอร์ mysql ดังนั้นคุณต้องแปลงวันที่จาก +12: 00 เขตเวลาเป็นเขตเวลาเซิร์ฟเวอร์ mysql ของคุณสำหรับการอัปเดต นี่คือเหตุผลที่ฉันใช้ UTC บนเซิร์ฟเวอร์ mysql และแปลงวันที่เป็น UTC ก่อนที่จะจัดเก็บ
Timo Huovinen

5
หนึ่งในคำตอบที่ดีที่สุดและให้ข้อมูลมากที่สุดที่ฉันได้พบในช่วงหลายปีที่ใช้ SO ขอบคุณ.
Mitya

คำเตือน!!! การใช้หรือการแปลงจากเวลาท้องถิ่นในเขตเวลา DST จะผิดหนึ่งชั่วโมงเป็นเวลาหนึ่งชั่วโมงต่อปีในตอนท้ายของการประหยัดเวลากลางวัน สิ่งนี้มีผลต่อการUNIX_TIMESTAMP(NOW());ใช้งานของคุณโดยCONVERT_TZ()ที่หนึ่งในพารามิเตอร์คือ `@@ session.time_zone สำหรับการแปลงชุดข้อมูล UTC เป็นเวลาประทับ UNIX อย่างน่าเชื่อถือคุณต้องตั้งค่าช่วงเวลา time_zone ก่อน
Doin

1
@Flimm ถูกต้องทั้งหมดฉันลืมที่จะแก้ไขบางเวลาที่ผ่านมา
Timo Huovinen

3

นี่คือตัวอย่างการทำงาน:

jdbc:mysql://localhost:3306/database?useUnicode=yes&characterEncoding=UTF-8&serverTimezone=Europe/Moscow

2

PHP และ MySQL มีการกำหนดค่าเขตเวลาเริ่มต้นของตนเอง คุณควรซิงโครไนซ์เวลาระหว่างฐานข้อมูลของคุณกับเว็บแอปพลิเคชันมิฉะนั้นคุณสามารถเรียกใช้บางประเด็นได้

อ่านบทช่วยสอนนี้: วิธีซิงโครไนซ์ PHP และเขตเวลา MySQL ของคุณ


มันเป็นโค้ดสองบรรทัด: date_default_timezone_set("America/Los_Angeles");และmysql_query("SET time_zone='" . date('P', time()) . "'");ทำงานได้อย่างสวยงามมาก!
Noumenon

3
@Noumenon ระวังด้วย! เช้านี้ฉันเกาหัวเพราะนั่นเป็นสิ่งที่ฉันกำลังทำอยู่และตอนนี้เวลาของฉันก็หมดไปหนึ่งชั่วโมงแล้ว สิ่งที่ฉันสงสัยคือการใช้เขตเวลาที่กำหนดนั้นมีความแม่นยำมากกว่าเมื่อมีการใช้ DST หากคุณใช้ America / New_York, MySQL รู้เกี่ยวกับ DST และจะจัดเก็บวันที่อย่างเหมาะสม หากคุณเพิ่งตั้งค่าเป็น -04: 00 เหมือนที่คุณมีที่นี่มันจะไม่นำการคำนวณ DST มาพิจารณา
nathanb

1
จะตรวจสอบถ้า MySQL รู้เกี่ยวกับเวลาอย่างถูกต้องกฎเวลาได้รับการปรับปรุงอย่างสม่ำเสมอและตาราง MySQL ที่เกี่ยวข้องยังต้องปรับปรุง (ดูด้านบนที่ด้านล่างของคำตอบของฉัน)
Timo Huovinen

1

ข้อดีและข้อเสียนั้นเหมือนกันมากขึ้นอยู่กับว่าคุณต้องการสิ่งนี้หรือไม่

ระวังถ้าเขตเวลา MySQL แตกต่างจากเวลาระบบของคุณ (เช่น PHP) การเปรียบเทียบเวลาหรือการพิมพ์ไปยังผู้ใช้จะเกี่ยวข้องกับการแก้ไขบางอย่าง

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