このセクションでは、MySQL で保持されるタイムゾーン設定、名前付き時間サポートに必要なシステムテーブルのロード方法、タイムゾーンの変更を最新の状態に保つ方法、およびうるう秒のサポートを有効にする方法について説明します。
MySQL 8.0.19 以降、タイムゾーンオフセットは挿入された日時値に対してもサポートされます。詳細は、セクション11.2.2「DATE、DATETIME、および TIMESTAMP 型」 を参照してください。
レプリケーション設定のタイムゾーン設定の詳細は、セクション17.5.1.14「レプリケーションとシステム関数」 および セクション17.5.1.33「レプリケーションとタイムゾーン」 を参照してください。
MySQL Server では、複数のタイムゾーン設定が保持されます:
-
システムタイムゾーン。 サーバーは起動時に、ホストマシンのタイムゾーンを自動的に判別し、それを使用して
system_time_zone
システム変数を設定しようとします。 その後、この値は変更しません。起動時に MySQL Server のシステムタイムゾーンを明示的に指定するには、mysqld を起動する前に
TZ
環境変数を設定します。 mysqld_safe を使用してサーバーを起動する場合は、その--timezone
オプションを使用してシステムのタイムゾーンを設定することもできます。TZ
および--timezone
に許可される値は、システムによって異なります。 許容可能な値を確認するには、オペレーティングシステムのドキュメントを参照してください。 -
サーバーの現在のタイムゾーン。
time_zone
グローバルシステム変数は、サーバーが現在動作しているタイムゾーンを示します。time_zone
の初期値は'SYSTEM'
で、サーバーのタイムゾーンがシステムのタイムゾーンと同じであることを示します。注記SYSTEM
に設定されている場合、タイムゾーン計算を必要とするすべての MySQL 関数コールは、システムライブラリコールを実行して現在のシステムタイムゾーンを決定します。 このコールはグローバル mutex によって保護される可能性があるため、競合が発生します。初期グローバルサーバーのタイムゾーン値は、起動時にコマンド行で
--default-time-zone
オプションを使用して明示的に指定するか、オプションファイルで次の行を使用できます:default-time-zone='timezone'
SYSTEM_VARIABLES_ADMIN
権限 (または非推奨のSUPER
権限) がある場合は、次のステートメントを使用して、実行時にグローバルサーバーのタイムゾーン値を設定できます:SET GLOBAL time_zone = timezone;
-
セッションごとのタイムゾーン。 接続する各クライアントには、セッションの
time_zone
変数で指定された独自のセッションタイムゾーン設定があります。 最初、セッション変数は、time_zone
グローバル変数から値を取得しますが、クライアントは次のステートメントを使用して、それぞれのタイムゾーンを変更できます。SET time_zone = timezone;
セッションのタイムゾーン設定は、ゾーン依存の時間値の表示および格納に影響します。 これには、NOW()
や CURTIME()
などの関数で表示される値や、TIMESTAMP
カラムに保存し、そこから読み出す値も含まれます。 TIMESTAMP
カラムの値は、格納のためにセッションタイムゾーンから UTC に、取得のために UTC からセッションタイムゾーンに変換されます。
セッションのタイムゾーン設定は、UTC_TIMESTAMP()
などの関数や DATE
、TIME
、DATETIME
カラムの値によって表示される値には影響しません。 また、これらのデータ型の値も UTC で格納されません。タイムゾーンは、TIMESTAMP
値から変換するときにのみ適用されます。 DATE
、TIME
、または DATETIME
値に対してロケール固有の演算を実行する場合、これらの値を UTC に変換し、演算を実行してから、元に変換し直します。
現在のグローバルおよびセッションのタイムゾーン値は、次のように取得できます:
SELECT @@GLOBAL.time_zone, @@SESSION.time_zone;
timezone
値はいくつかの形式で指定できますが、大文字と小文字は区別されません:
値
'SYSTEM'
として、サーバーのタイムゾーンがシステムのタイムゾーンと同じであることを示します。-
'+10:00'
、'-6:00'
、'+05:30'
などの+
または-
の接頭辞が付いた、[
形式の UTC からのオフセットを示す文字列として。 必要に応じて、先頭のゼロを 10 未満の時間値に使用できます。このような場合、MySQL では、値を格納および取得するときに先頭のゼロが付加されます。 MySQL は、H
]H
:MM
'-00:00'
または'-0:00'
を'+00:00'
に変換します。MySQL 8.0.19 より前は、この値は
'-12:59'
から'+13:00'
までの範囲である必要がありました。MySQL 8.0.19 以降、許可される範囲は'-14:00'
から'+14:00'
までです。 -
'Europe/Helsinki'
,'US/Eastern'
,'MET'
や'UTC'
などの名前付きタイムゾーンとして。
タイムゾーン情報を格納するために、mysql
システムスキーマにはいくつかのテーブルが存在します (セクション5.3「mysql システムスキーマ」 を参照)。 MySQL のインストール手順では、タイムゾーンテーブルは作成されますが、ロードされません。 これを手動で行うには、次の手順を使用します。
情報は変更することがあるので、タイムゾーン情報のロードは必ずしも 1 回だけの操作とはかぎりません。 このような変更が起きた場合、古いルールを使用したアプリケーションは旧式になり、MySQL Server で使用されている情報を最新の状態に維持するために、タイムゾーンテーブルをリロードする必要が生じることがあります。 タイムゾーンの変更による現在の時間の維持を参照してください。
システムに独自の zoneinfo データベース (タイムゾーンを記述する一連のファイル) がある場合は、mysql_tzinfo_to_sql プログラムを使用してタイムゾーンテーブルをロードします。 このようなシステムの例には、Linux、macOS、FreeBSD および Solaris があります。 これらのファイルの 1 つの適切な場所は /usr/share/zoneinfo
ディレクトリです。 システムに zoneinfo データベースがない場合は、このセクションの後半で説明するように、ダウンロード可能なパッケージを使用できます。
コマンド行からタイムゾーンテーブルをロードするには、zoneinfo ディレクトリのパス名を mysql_tzinfo_to_sql に渡し、その出力を mysql プログラムに送信します。 例:
mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root -p mysql
ここに示す mysql コマンドは、mysql
システムスキーマのテーブルを変更する権限を持つ root
などのアカウントを使用してサーバーに接続することを前提としています。 必要に応じて接続パラメータを調整します。
mysql_tzinfo_to_sql は、システムのタイムゾーンファイルを読み取り、そのファイルから SQL ステートメントを生成します。mysql はこれらのステートメントを処理して、タイムゾーンテーブルをロードします。
mysql_tzinfo_to_sql を使用して、単一のタイムゾーンファイルをロードしたり、うるう秒の情報を生成することもできます:
-
タイムゾーン名
tz_name
に対応した単一のタイムゾーンファイルtz_file
をロードするには、次のように mysql_tzinfo_to_sql を呼び出します。mysql_tzinfo_to_sql tz_file tz_name | mysql -u root -p mysql
このアプローチでは、サーバーが認識する名前付きゾーンごとに、個別のコマンドを実行してタイムゾーンファイルをロードする必要があります。
-
タイムゾーンでうるう秒を考慮する必要がある場合は、次のようにうるう秒の情報を初期化します (
tz_file
はタイムゾーンファイルの名前です):mysql_tzinfo_to_sql --leap tz_file | mysql -u root -p mysql
mysql_tzinfo_to_sql の実行後、以前にキャッシュされたタイムゾーンデータが引き続き使用されないようにサーバーを再起動します。
システムに zoneinfo データベース (Windows など) がない場合は、MySQL Developer Zone でダウンロード可能な SQL ステートメントを含むパッケージを使用できます:
https://dev.mysql.com/downloads/timezones.html
システムに zoneinfo データベースがある場合は、ダウンロード可能なタイムゾーンパッケージを使用しないでください。 代わりに、mysql_tzinfo_to_sql ユーティリティーを使用してください。 そうしないと、MySQL とシステム上のほかのアプリケーション間で日時処理に違いが生じることがあります。
ダウンロードした SQL ステートメントタイムゾーンパッケージを使用するには、パッケージを解凍してから、解凍したファイルの内容をタイムゾーンテーブルにロードします:
mysql -u root -p mysql < file_name
次に、サーバーを再起動します。
MyISAM
テーブルを含むダウンロード可能なタイムゾーンパッケージは使用しないでください。 これは、古い MySQL バージョンを対象としています。 MySQL では、タイムゾーンテーブルに InnoDB
が使用されるようになりました。 これらを MyISAM
テーブルに置換しようとすると、問題が発生します。
タイムゾーンルールが変更されると、古いルールを使用するアプリケーションは期限切れになります。 現在の時間に維持するには、システムが現在のタイムゾーン情報を使用していることを確認する必要があります。 MySQL では、最新の状態に保つために複数の要因を考慮する必要があります:
オペレーティングシステムの時間は、そのタイムゾーンが
SYSTEM
に設定されている場合、MySQL Server が時間に使用する値に影響します。 オペレーティングシステムが最新のタイムゾーン情報を使用していることを確認します。 ほとんどのオペレーティングシステムでは、最新の更新またはサービスパックによってシステムは時間の変更に対応できます。 オペレーティングシステムのベンダーの web サイトで、時間の変更に対処する更新を確認します。システムの
/etc/localtime
タイムゾーンファイルを、mysqld の起動時に有効なものとは異なるルールを使用するバージョンに置き換える場合は、更新されたルールを使用するように mysqld を再起動します。 そうしないと、システムが時間を変更したときに mysqld が認識されないことがあります。-
MySQL で名前付きタイムゾーンを使用する場合は、
mysql
データベースのタイムゾーンテーブルが最新であることを確認してください:システムに独自の zoneinfo データベースがある場合は、zoneinfo データベースが更新されるたびに MySQL タイムゾーンテーブルをリロードします。
システムに独自の zoneinfo データベースがない場合、MySQL Developer Zone で更新がないか調べます。 新しい更新が使用可能になったら、それをダウンロードし、それを使用して現在のタイムゾーンテーブルのコンテンツを置き換えます。
両方の方法の手順については、タイムゾーンテーブルへの移入 を参照してください。mysqld では、検索するタイムゾーン情報がキャッシュされるため、タイムゾーンテーブルの更新後に mysqld を再起動して、古いタイムゾーンデータが引き続き提供されないようにします。
サーバーのタイムゾーン設定として使用するか、独自のタイムゾーンを設定するクライアントが使用するために、名前付きタイムゾーンが使用できるかどうか不確かな場合は、タイムゾーンテーブルが空かどうかを調べてください。 次のクエリーは、タイムゾーン名を含むテーブルに行があるかどうかを判断します。
mysql> SELECT COUNT(*) FROM mysql.time_zone_name;
+----------+
| COUNT(*) |
+----------+
| 0 |
+----------+
カウントがゼロの場合、テーブルが空であることを示します。 この場合、現在名前付きタイムゾーンを使用しているアプリケーションはなく、テーブルを更新する必要はありません (名前付きタイムゾーンのサポートを有効にする場合を除く)。 カウントがゼロより大きい場合、テーブルは空ではなく、その内容が名前付きタイムゾーンのサポートに使用できることを示します。 この場合、名前付きタイムゾーンを使用するアプリケーションが正しいクエリー結果を取得できるように、必ずタイムゾーンテーブルをリロードしてください。
サマータイムのルール変更に対して MySQL インストールが正しく更新されているかどうかを確認するには、次のようなテストを使用します。 この例では、3 月 11 日午前 2 時に米国で行われる 2007 年 DST の 1 時間の変更に適切な値を使用しています。
テストでは、次のクエリーが使用されます:
SELECT
CONVERT_TZ('2007-03-11 2:00:00','US/Eastern','US/Central') AS time1,
CONVERT_TZ('2007-03-11 3:00:00','US/Eastern','US/Central') AS time2;
2 つの時間値は、DST 変更が行われる時間を示し、名前付きタイムゾーンの使用には、タイムゾーンテーブルを使用する必要がにあります。 結果では、両方のクエリーで同じ結果が返されることが期待されます (「米国/中央」タイムゾーンの同等の値に変換された入力時間)。
タイムゾーンテーブルを更新する前に、次のような誤った結果が表示されます:
+---------------------+---------------------+
| time1 | time2 |
+---------------------+---------------------+
| 2007-03-11 01:00:00 | 2007-03-11 02:00:00 |
+---------------------+---------------------+
テーブルの更新後に、正しい結果が表示されます。
+---------------------+---------------------+
| time1 | time2 |
+---------------------+---------------------+
| 2007-03-11 01:00:00 | 2007-03-11 01:00:00 |
+---------------------+---------------------+
うるう秒値は、:59:59
で終わる時間部分を使用して返されます。 これは、NOW()
などの関数が、うるう秒の間、2、3 秒連続して同じ値を返すことがあることを意味します。 :59:60
または :59:61
で終わる時間部分を持つリテラル時間値が無効と見なされることに変わりはありません。
うるう秒の 1 秒前に TIMESTAMP
値を検索する必要がある場合、'
値との比較を使用すると異常な結果が得られる可能性があります。 この点について次の例で説明します。 セッションタイムゾーンが UTC に変更されるため、内部 YYYY-MM-DD hh:mm:ss
'TIMESTAMP
値 (UTC) と表示される値 (タイムゾーン修正が適用されている値) に違いはありません。
mysql> CREATE TABLE t1 (
a INT,
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (ts)
);
Query OK, 0 rows affected (0.01 sec)
mysql> -- change to UTC
mysql> SET time_zone = '+00:00';
Query OK, 0 rows affected (0.00 sec)
mysql> -- Simulate NOW() = '2008-12-31 23:59:59'
mysql> SET timestamp = 1230767999;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 (a) VALUES (1);
Query OK, 1 row affected (0.00 sec)
mysql> -- Simulate NOW() = '2008-12-31 23:59:60'
mysql> SET timestamp = 1230768000;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t1 (a) VALUES (2);
Query OK, 1 row affected (0.00 sec)
mysql> -- values differ internally but display the same
mysql> SELECT a, ts, UNIX_TIMESTAMP(ts) FROM t1;
+------+---------------------+--------------------+
| a | ts | UNIX_TIMESTAMP(ts) |
+------+---------------------+--------------------+
| 1 | 2008-12-31 23:59:59 | 1230767999 |
| 2 | 2008-12-31 23:59:59 | 1230768000 |
+------+---------------------+--------------------+
2 rows in set (0.00 sec)
mysql> -- only the non-leap value matches
mysql> SELECT * FROM t1 WHERE ts = '2008-12-31 23:59:59';
+------+---------------------+
| a | ts |
+------+---------------------+
| 1 | 2008-12-31 23:59:59 |
+------+---------------------+
1 row in set (0.00 sec)
mysql> -- the leap value with seconds=60 is invalid
mysql> SELECT * FROM t1 WHERE ts = '2008-12-31 23:59:60';
Empty set, 2 warnings (0.00 sec)
これを回避するには、うるう秒の修正が適用されているカラムに実際に格納されている UTC 値に基づく比較を使用できます:
mysql> -- selecting using UNIX_TIMESTAMP value return leap value
mysql> SELECT * FROM t1 WHERE UNIX_TIMESTAMP(ts) = 1230768000;
+------+---------------------+
| a | ts |
+------+---------------------+
| 2 | 2008-12-31 23:59:59 |
+------+---------------------+
1 row in set (0.00 sec)