Haruskah MySQL mengatur zona waktunya ke UTC?

149

Tindak lanjuti pertanyaan /server/191331/should-servers-have-their-timezone-set-to-gmt-utc

Haruskah zona waktu MySQL diatur ke UTC atau haruskah zona waktu itu sama dengan server atau PHP ditetapkan? (Jika bukan UTC)

Apa pro dan kontra?

Timo Huovinen
sumber
stackoverflow.com/a/1650406/175071 membagikan alasan yang baik untuk menggunakan UTC
Timo Huovinen
UTC bukan zona waktu. UTC adalah standar, GMT adalah zona waktu. zachholman.com/talk/utc-is-enough-for-everyone-right
agoldev
Alasan bagus lainnya untuk menggunakan UTC dba.stackexchange.com/questions/161416/…
Timo Huovinen

Jawaban:

533

Tampaknya tidak masalah zona waktu apa yang ada di server selama Anda memiliki waktu yang tepat untuk zona waktu saat ini, ketahui zona waktu kolom waktu yang Anda simpan, dan mengetahui masalah dengan waktu musim panas.

Di sisi lain, jika Anda memiliki kontrol zona waktu dari server tempat Anda bekerja, maka Anda dapat mengatur semuanya ke UTC secara internal dan tidak pernah khawatir tentang zona waktu dan DST.

Berikut adalah beberapa catatan yang saya kumpulkan tentang cara bekerja dengan zona waktu sebagai bentuk lembar contekan untuk saya dan orang lain yang mungkin memengaruhi zona waktu apa yang akan dipilih orang untuk servernya dan bagaimana ia akan menyimpan tanggal dan waktu.

Cheatsheet Zona Waktu MySQL

Catatan:

  1. Mengubah zona waktu tidak akan mengubah datetime yang disimpan atau stempel waktu , tetapi itu akan memilih datetime yang berbeda dari kolom stempel waktu
  2. Peringatan! UTC memiliki lompatan detik, ini terlihat seperti '2012-06-30 23:59:60' dan dapat ditambahkan secara acak, dengan pemberitahuan 6 bulan sebelumnya, karena perlambatan rotasi bumi
  3. GMT membingungkan detik, itulah sebabnya UTC ditemukan.

  4. Peringatan! zona waktu regional yang berbeda mungkin menghasilkan nilai datetime yang sama karena waktu musim panas

  5. Kolom cap waktu hanya mendukung tanggal 1970-01-01 00:00:01 hingga 2038-01-19 03:14:07 UTC, karena batasan .
  6. Secara internal, kolom stempel waktu MySQL disimpan sebagai UTC tetapi ketika memilih tanggal, MySQL secara otomatis akan mengubahnya ke zona waktu sesi saat ini.

    Saat menyimpan tanggal dalam timestamp, MySQL akan menganggap bahwa tanggal tersebut berada di zona waktu sesi saat ini dan mengubahnya menjadi UTC untuk penyimpanan.

  7. MySQL dapat menyimpan sebagian tanggal dalam kolom datetime, ini terlihat seperti "2013-00-00 04:00:00"
  8. MySQL menyimpan "0000-00-00 00:00:00" jika Anda menetapkan kolom datetime sebagai NULL, kecuali jika Anda secara khusus mengatur kolom untuk mengizinkan nol ketika Anda membuatnya.
  9. Baca ini

Untuk memilih kolom cap waktu dalam format UTC

tidak peduli apa zona waktu sesi MySQL saat ini di:

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

Anda juga dapat mengatur zona waktu sesi global atau saat ini menjadi UTC dan kemudian memilih stempel waktu seperti:

SELECT `timestamp_field` FROM `table_name`

Untuk memilih datetime saat ini di UTC:

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

Contoh hasil: 2015-03-24 17:02:41

Untuk memilih datetime saat ini di zona waktu sesi

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

Untuk memilih zona waktu yang ditetapkan saat server diluncurkan

SELECT @@system_time_zone;

Mengembalikan "MSK" atau "+04: 00" untuk waktu Moskow misalnya, ada (atau ada) bug MySQL di mana jika diatur ke offset numerik, itu tidak akan menyesuaikan waktu penghematan siang hari

Untuk mendapatkan zona waktu saat ini

SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP);

Ini akan kembali 02:00:00 jika zona waktu Anda adalah +2: 00.

Untuk mendapatkan cap waktu UNIX saat ini (dalam detik):

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

Untuk mendapatkan kolom cap waktu sebagai cap waktu UNIX

SELECT UNIX_TIMESTAMP(`timestamp`) FROM `table_name`

Untuk mendapatkan kolom waktu UTC sebagai cap waktu UNIX

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

Dapatkan datzone zona waktu saat ini dari bilangan bulat waktu UNIX positif

SELECT FROM_UNIXTIME(`unix_timestamp_int`) FROM `table_name`

Dapatkan datetime UTC dari cap waktu UNIX

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

Dapatkan datzone zona waktu saat ini dari bilangan bulat cap waktu UNIX negatif

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

Ada 3 tempat di mana zona waktu mungkin diatur di MySQL:

Catatan: Zona waktu dapat diatur dalam 2 format:

  1. offset dari UTC: '+00: 00', '+10: 00' atau '-6: 00'
  2. sebagai zona waktu bernama: 'Eropa / Helsinki', 'AS / Timur', atau 'MET'

Zona waktu bernama hanya dapat digunakan jika tabel informasi zona waktu di database mysql telah dibuat dan diisi.

dalam file "my.cnf"

default_time_zone='+00:00'

atau

timezone='UTC'

@@ variabel global.time_zone

Untuk melihat nilai apa yang ditetapkan

SELECT @@global.time_zone;

Untuk menetapkan nilai untuk itu gunakan salah satu:

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

@@ session.time_zone variabel

SELECT @@session.time_zone;

Untuk mengaturnya gunakan salah satu:

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

"@@ global.time_zone variabel" dan "@@ session.time_zone variabel" mungkin mengembalikan "SYSTEM" yang berarti bahwa mereka menggunakan zona waktu yang diatur dalam "my.cnf".

Agar nama zona waktu berfungsi (bahkan untuk zona waktu standar) Anda harus mengatur tabel informasi zona waktu Anda harus diisi: http://dev.mysql.com/doc/refman/5.1/en/time-zone-support. html

Catatan: Anda tidak dapat melakukan ini karena akan mengembalikan NULL:

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

Siapkan tabel zona waktu mysql

Agar CONVERT_TZdapat bekerja, Anda perlu tabel zona waktu untuk diisi

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` ;

Jika kosong, isilah dengan menjalankan perintah ini

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

jika perintah ini memberi Anda kesalahan " data terlalu panjang untuk 'singkatan' kolom pada baris 1 ", maka itu mungkin disebabkan oleh karakter NULL yang ditambahkan pada akhir singkatan zona waktu

perbaikannya adalah menjalankan ini

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

(pastikan server Anda aturan pertama up to date zdump -v Europe/Moscow | grep 2011 https://chrisjean.com/updating-daylight-saving-time-on-linux/ )

Lihat riwayat transisi DST (Daylight Saving Time) penuh untuk setiap zona waktu

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 juga menerapkan perubahan DST yang diperlukan berdasarkan aturan dalam tabel di atas dan tanggal yang Anda gunakan.

Catatan:
Menurut dokumen , nilai yang Anda tetapkan untuk time_zone tidak berubah, jika Anda menetapkannya sebagai "+01: 00" misalnya, maka time_zone akan ditetapkan sebagai offset dari UTC, yang tidak mengikuti DST, jadi itu akan tetap sama sepanjang tahun.

Hanya zona waktu yang disebutkan yang akan mengubah waktu selama waktu musim panas.

Singkatan seperti CETakan selalu menjadi waktu musim dingin dan CESTakan menjadi musim panas sedangkan +01: 00 akan selalu menjadi UTCwaktu + 1 jam dan keduanya tidak akan berubah dengan DST.

The systemzona waktu akan menjadi zona waktu dari mesin host di mana mysql diinstal (kecuali mysql gagal untuk menentukan itu)

Anda dapat membaca lebih lanjut tentang bekerja dengan DST di sini

Pertanyaan-pertanyaan Terkait:

Sumber:

Timo Huovinen
sumber
Jadi jika saya mengatur jenis kolom saya ke stempel waktu. Dan time_zone saya adalah +12: 00, dan saya ingin memperbarui kolom menggunakan tanggal / waktu berdasarkan utc, apakah ada cara untuk memasukkan zona waktu dalam pernyataan pembaruan, atau haruskah saya menggunakan convert_tz. Misalnya. pembaruan tableset modified= '2016-07-07 08:10 +00: 00'
bumperbox
2
@bumperbox mysql selalu mengasumsikan bahwa tanggal yang Anda berikan kolom stempel waktu berada di zona waktu yang sama dengan server mysql, jadi Anda perlu mengonversi tanggal Anda dari zona waktu +12: 00 ke zona waktu server mysql server Anda untuk pembaruan. Inilah sebabnya saya menggunakan UTC di server mysql dan mengonversi tanggal apa pun ke UTC sebelum menyimpannya.
Timo Huovinen
5
Salah satu jawaban terbaik dan paling informatif yang pernah saya temui selama bertahun-tahun dalam menggunakan SO. Terima kasih.
Mitya
PERINGATAN!!! Setiap penggunaan, atau konversi dari, waktu lokal di zona waktu DST akan salah satu jam selama satu jam setiap tahun pada akhir penghematan siang hari. Ini memengaruhi UNIX_TIMESTAMP(NOW());serta semua penggunaan Anda di CONVERT_TZ()mana salah satu parameternya adalah `@@ session.time_zone. Untuk mengkonversi datetimes UTC dengan andal ke cap waktu UNIX, Anda pada dasarnya harus mengatur time_zone sesi terlebih dahulu.
Lakukan
1
@ Flimm sepenuhnya benar, saya lupa untuk memperbaikinya beberapa waktu lalu.
Timo Huovinen
3

Ini adalah contoh kerja:

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

PHP dan MySQL memiliki konfigurasi zona waktu default sendiri. Anda harus menyinkronkan waktu antara basis data dan aplikasi web Anda, jika tidak, Anda dapat menjalankan beberapa masalah.

Baca tutorial ini: Cara Menyinkronkan Zona Waktu PHP dan MySQL Anda


sumber
Ini pada dasarnya dua baris kode: date_default_timezone_set("America/Los_Angeles");dan mysql_query("SET time_zone='" . date('P', time()) . "'");Bekerja sangat elegan!
Noumenon
3
@Noumenon Hati-hati dengan itu! Saya telah menggaruk-garuk kepala saya pagi ini karena itulah yang saya lakukan, dan beberapa waktu saya libur satu jam sekarang. Yang saya duga adalah bahwa menggunakan zona waktu bernama lebih akurat ketika DST terlibat. Jika Anda menggunakan America / New_York, MySQL tahu tentang DST dan akan menyimpan tanggal dengan tepat. Jika Anda hanya mengaturnya ke -04: 00 seperti yang Anda miliki di sini, itu tidak akan mempertimbangkan perhitungan DST.
nathanb
1
Periksa apakah mysql tahu tentang DST dengan benar, aturan DST diperbarui secara berkala dan tabel mysql terkait juga perlu diperbarui (lihat di atas di bagian bawah jawaban saya)
Timo Huovinen
1

Pro dan kontra cukup banyak identik. Itu tergantung pada apakah Anda menginginkan ini atau tidak.

Hati-hati, jika zona waktu MySQL berbeda dari waktu sistem Anda (misalnya PHP), membandingkan waktu atau pencetakan dengan pengguna akan melibatkan beberapa penyesuaian.

alandarev
sumber