データベース内のさまざまなタイプのデータを操作する
MYSQL
mysqlには時間を表すためのいくつかの標準データ型があります;
TIMESTAMP
と
DATETIME
を見ていきます。
ドキュメントには、変換ポリシーは一部のタイプのデータに適用されますが、一部のデータには適用されないと書かれています。
実際には、すべてがはるかに興味深いものです。 いくつかの例を見てみましょう。
テーブルを作成します。
create table xxxDate(ts TIMESTAMP NOT NULL, dt DATETIME NOT NULL);
モスクワの現在のゾーンを設定します(モスクワでは最近、夏時間はありません、UTC + 4):
set time_zone='Europe/Moscow';
それぞれ夏時間と冬時間の2つのレコードを作成します。
insert into xxxDate values('2012-06-10 15:08:05', '2012-06-10 15:08:05'); insert into xxxDate values('2012-12-10 15:08:05', '2012-12-10 15:08:05');
データベースからこれらの日付の選択が示すものを見てみましょう:
select * from xxxDate; +---------------------+---------------------+ | ts | dt | +---------------------+---------------------+ | 2012-06-10 15:08:05 | 2012-06-10 15:08:05 | | 2012-12-10 15:08:05 | 2012-12-10 15:08:05 | +---------------------+---------------------+ select UNIX_TIMESTAMP(ts), UNIX_TIMESTAMP(dt) from xxxDate; +--------------------+--------------------+ | UNIX_TIMESTAMP(ts) | UNIX_TIMESTAMP(dt) | +--------------------+--------------------+ | 1339326485 | 1339326485 | | 1355137685 | 1355137685 | +--------------------+--------------------+
両方の列の値が同じであることが
UNIX_TIMESTAMP
ます。これは、
UNIX_TIMESTAMP
関数が現在のゾーンの引数の値を考慮し、UTCに変換するためです。 明らかに、同じ値は、同じ値
Mon, 10 Dec 2012 11:08:05 UTC
等しく変換されます。
今、私たちはロンドンに移動しています!
set time_zone='Europe/London'; select * from xxxDate; +---------------------+---------------------+ | ts | dt | +---------------------+---------------------+ | 2012-06-10 12:08:05 | 2012-06-10 15:08:05 | | 2012-12-10 11:08:05 | 2012-12-10 15:08:05 | +---------------------+---------------------+
ドキュメンテーション
TIMESTAMP
よると、データベースに挿入される前にUTCに変換されるため、驚くべきことは何もありません。したがって、現在のゾーンを変更した後、データベースは現在のゾーンのこの時間の値を提供します。
DATETIME
型の値は変更され
DATETIME
いません。
ここで、モスクワのアルゴリズムの動作をより詳細に検討します。 tsの値は貼り付けの際にUTCに変換され、フェッチの際には15時間現在のゾーン(ロンドンの場合)に応じた値に変換され、UNIX_TIMESTAMPが選択された場合は、データベースに保存されたとおりに表示されました。
ロンドンで期待される結果:
select UNIX_TIMESTAMP(ts), UNIX_TIMESTAMP(dt) from xxxDate; +--------------------+--------------------+ | UNIX_TIMESTAMP(ts) | UNIX_TIMESTAMP(dt) | +--------------------+--------------------+ | 1339326485 | 1339337285 | // 14h (dt) | 1355137685 | 1355152085 | // 15h (dt) +--------------------+--------------------+
Ts値は変更されておらず、dt値は現在のゾーンの値と見なされるため、夏時間(最初のレコード)
1339337285 = Sun, 10 Jun 2012 14:08:05 GMT
および冬時間(下のレコード)
1355152085 = Mon, 10 Dec 2012 15:08:05 GMT
。
念のため、UTCの動作を確認してください。
set time_zone='UTC'; select * from xxxDate; +---------------------+---------------------+ | ts | dt | +---------------------+---------------------+ | 2012-06-10 11:08:05 | 2012-06-10 15:08:05 | | 2012-12-10 11:08:05 | 2012-12-10 15:08:05 | +---------------------+---------------------+ select UNIX_TIMESTAMP(ts), UNIX_TIMESTAMP(dt) from xxxDate; +--------------------+--------------------+ | UNIX_TIMESTAMP(ts) | UNIX_TIMESTAMP(dt) | +--------------------+--------------------+ | 1339326485 | 1339340885 | // 15h (dt) | 1355137685 | 1355152085 | // 15h (dt) +--------------------+--------------------+
すべてが前の説明に従っており、ts値は変更されておらず、dt値は現在のゾーンで考慮されるため、変更されません(
1339340885 = Sun, 10 Jun 2012 15:08:05 GMT; 1355152085 = Mon, 10 Dec 2012 15:08:05 GMT
) 。
結論:
-
DATETIME
で作業し、サーバー/接続タイムゾーンの変更に関する情報が失われた状態でサーバーを移動すると(データの挿入またはインポート中の不適切なタイムゾーン設定)、イベントの実際の時間に関する情報が失われます。 たとえば、モスクワ時間の15:00にレコードを作成し(バックアップからデータベースにデータをインポートしました)、UTCでサーバーをセットアップしましたが、それ以前はタイムゾーンがモスクワであることに気付きませんでした。 その結果、UTCの11時間ではなく、両方の注文が4時間後(15時間)に行われ、別の日になる可能性があります。 したがって、私の意見では、TIMESTAMP
作業する必要がありTIMESTAMP
。 - また、サーバーでデバッグする際に不必要な問題を回避するために、UTCゾーンを作成し、UTCでデータを操作し、クライアント側ではクライアントが必要とするゾーンで表示することをお勧めします。
- feedbee 記事の最後の良い例でもあります 。
- うるう秒の問題を回避するには、UTCでUNIXエポックを使用する価値があります(うるう秒のセクションを参照)。
SQLite3
sqlite3の状況を考慮してください。 sqliteのドキュメントによると、時間を節約するためのデータ型はありませんが、テキスト、浮動小数点数、整数として保存された時間を操作するための関数があります。 一般的に、これらのアイデアは基本的に違いはありません。 sqliteでは、localtimeおよびutc修飾子を使用しない場合、現在のタイムゾーンは使用されないと想定できます。 たとえば、システム設定に関係なく、
CURRENT_TIMESTAMP
はUTCに設定されます。
$ date Mon Dec 10 22:05:50 MSK 2012 $ sqlite3 sqlite> select CURRENT_TIMESTAMP; 2012-12-10 18:06:05 sqlite> select datetime(CURRENT_TIMESTAMP, 'localtime'); 2012-12-10 22:06:35
したがって、文字列の解析時にエラーを検出しないように、プログラム内のデータをutcに変換し、UNIXエポックを使用してください。
デバッグ用の機能:
select strftime('%s', CURRENT_TIMESTAMP); 1355162582 select datetime(1355152085, 'unixepoch'); 2012-12-10 15:08:05
ユーザーが時間を見る方法
日時タイプを使用し、変換しない場合、ユーザーは時間内に混乱します。 たとえば、2人のユーザーが異なるタイムゾーンに住んでいる場合、ゾーンを指定せずに同じタイムラインを見ると、異なる時間について考えることになります。 繰り返さないために、ここにリンク例があります。
時間を操作するためのC関数
まず、 glibcで時間を操作することの説明を理解しておくと非常に便利です。 異なるタイムゾーンでのいくつかの関数の結果に関するいくつかの例を検討します。 ドキュメントでも、struct tm(以降ブレークダウン時間と呼ばれます)は通常、ユーザーへの表示(わかりやすいため)にのみ使用されると書かれています。 プログラムで他のより適切なデータ型を使用する方が適切です。
いくつかの例を見てみましょう。
Function: struct tm * localtime_r(const time_t *time, struct tm *resultp)
単純な時間を、ユーザーゾーンに関連して表される内訳時間に変換します。
time_t t = 1339326485; // 2012-06-10 11:08:05 (UTC) struct tm bdt; localtime_r (&t, &bdt); cout << bdt.tm_hour << endl; cout << bdt.tm_isdst << endl; cout << bdt.tm_zone << endl;
システム内のゾーン | UTC | ヨーロッパ/モスクワ | ヨーロッパ/ロンドン |
---|---|---|---|
時間出力 | 11 | 15 | 12 |
isdst出力 | 0 | 0 | 1 |
ゾーン出力 | UTC | Msk | Bst |
time_t t = 1355137685; // 2012-12-10 11:08:05 (UTC)
システム内のゾーン | UTC | ヨーロッパ/モスクワ | ヨーロッパ/ロンドン |
---|---|---|---|
時間出力 | 11 | 15 | 11 |
isdst出力 | 0 | 0 | 0 |
ゾーン出力 | UTC | Msk | GMT |
Function: struct tm * gmtime_r(const time_t *time, struct tm *resultp)
ユーザーゾーンに関係なく、UTCゾーンの値を返します。
time_t t = 1339326485; // 2012-06-10 11:08:05 (UTC) struct tm bdt; gmtime_r (&t, &bdt); cout << bdt.tm_hour << endl; cout << bdt.tm_isdst << endl; cout << bdt.tm_zone << endl;
システム内のゾーン | UTC | ヨーロッパ/モスクワ | ヨーロッパ/ロンドン |
---|---|---|---|
時間出力 | 11 | 11 | 11 |
isdst出力 | 0 | 0 | 0 |
ゾーン出力 | GMT | GMT | GMT |
time_t t = 1355137685; // 2012-12-10 11:08:05 (UTC)
システム内のゾーン | UTC | ヨーロッパ/モスクワ | ヨーロッパ/ロンドン |
---|---|---|---|
時間出力 | 11 | 11 | 11 |
isdst出力 | 0 | 0 | 0 |
ゾーン出力 | GMT | GMT | GMT |
Function: time_t mktime(struct tm *brokentime)
( timelocalと同義ですが、まれです)
内訳時間を単純時間に変換します。
重要: 引数の現在のゾーンを設定します。
tm_zoneフィールドは引数とは見なされません。時刻は現在のタイムゾーンで設定され、時刻はUTCで返されると見なされます。
struct tm bdt; bdt.tm_sec = 5; // 05 sec bdt.tm_min = 8; // 08 min bdt.tm_hour = 11; // 11 h bdt.tm_mday = 10; // 10 bdt.tm_mon = 5; // 6th mon - Jun bdt.tm_year = 112;// 2012 - 1900 bdt.tm_wday = 0; // ignored bdt.tm_yday = 0; // ignored bdt.tm_isdst= 0; bdt.tm_gmtoff= 0; bdt.tm_zone = "UTC"; time_t t = mktime(&bdt); cout << t << endl; cout << bdt.tm_hour << endl; cout << bdt.tm_isdst << endl; cout << bdt.tm_gmtoff << endl; cout << bdt.tm_zone << endl;
システム内のゾーン | UTC | ヨーロッパ/モスクワ | ヨーロッパ/ロンドン |
---|---|---|---|
ピンt | 1339326485(2012年6月10日日曜日11:08:05 GMT) | 1339312085(2012年6月10日日曜日07:08:05 GMT) | 1339326485(2012年6月10日日曜日11:08:05 GMT) |
時間出力 | 11 | 11 | 12 |
isdst出力 | 0 | 0 | 1 |
gmtoff出力 | 0 | 14400(4 * 60 * 60) | 3600(1 * 60 * 60) |
ゾーン出力 | UTC | Msk | Bst |
ロンドンのtm_hourフィールドとtm_isdstフィールドが変更されていることに注意してください。これは、詳細な時間構造のフィールドを正規化するプロセスの一部です。
今のために
bdt.tm_mon = 11; // 11th mon - Dec
システム内のゾーン | UTC | ヨーロッパ/モスクワ | ヨーロッパ/ロンドン |
---|---|---|---|
ピンt | 1355137685(2012年12月10日月曜日11:08:05 GMT) | 1355123285(2012年12月10日、07:08:05 GMT) | 1355137685(2012年12月10日月曜日11:08:05 GMT) |
時間出力 | 11 | 11 | 11 |
isdst出力 | 0 | 0 | 0 |
gmtoff出力 | 0 | 14400(4 * 60 * 60) | 0 |
ゾーン出力 | UTC | Msk | GMT |
Function: time_t timegm(struct tm *brokentime)
UTCで動作します。
システム内のゾーン | UTC | ヨーロッパ/モスクワ | ヨーロッパ/ロンドン |
---|---|---|---|
ピンt | 1339326485(2012年6月10日日曜日11:08:05 GMT) | 1339326485(2012年6月10日日曜日11:08:05 GMT) | 1339326485(2012年6月10日日曜日11:08:05 GMT) |
時間出力 | 11 | 11 | 11 |
isdst出力 | 0 | 0 | 0 |
gmtoff出力 | 0 | 0 | 0 |
ゾーン出力 | GMT | GMT | GMT |
bdt.tm_mon = 11; // 11th mon - Dec
システム内のゾーン | UTC | ヨーロッパ/モスクワ | ヨーロッパ/ロンドン |
---|---|---|---|
ピンt | 1355137685(2012年12月10日月曜日11:08:05 GMT) | 1355137685(2012年12月10日月曜日11:08:05 GMT) | 1355137685(2012年12月10日月曜日11:08:05 GMT) |
時間出力 | 11 | 11 | 11 |
isdst出力 | 0 | 0 | 0 |
gmtoff出力 | 0 | 0 | 0 |
ゾーン出力 | GMT | GMT | GMT |
結論:
ユーザーコンピューター上のプログラムでユーザーに時刻を表示する場合は、
timelocal/localtime
関数を使用します。サーバーで作業している場合は、
timegm/gmtime
使用し
timegm/gmtime
。 また、同僚のいずれかまたはサードパーティのライブラリで*ローカル*関数を使用する場合に備えて、サーバーにUTCゾーンを設定します。 ユーザーのコンピューター上でも、UTCで長期間保存して作業するため、ユーザーがゾーンを変更しても、すべての日付は正しいままです。
ご注意
Linuxでタイムゾーンを設定する
debベースの分布と、タイムゾーンを設定するためのいくつかの鉄の方法のみを検討してください。
- 方法1(debベースの配布で動作します):
ターミナルでコマンドを実行し、指示に従います(「UTC」は「Etc」セクションにあります)。
sudo dpkg-reconfigure tzdata - 2番目の方法(おそらくどこでも動作します):
ターミナルでコマンドを実行します。
sudo ln -sf /usr/share/zoneinfo/UTC /etc/localtime
/etc/timezone
ため、/etc/timezone
(もしあれば)を編集してください - 3番目の方法:
TZ環境変数を目的のゾーンに設定します。次に例を示します。
export TZ=Europe/London
LEAP SECOND
通常、これは別のトピックなので、うるう秒に関するこの記事の例はありません。 特定の関数がどのように機能するか、さまざまなデータベースがどのように動作するかを自分で確認できます。mysqlの例を次に示します。
- UTCにはうるう秒が含まれます
- 非常に重要な注意 :
POSIXでは、time_tは1970年1月1日00:00:00からカウントされ、UTCはうるう秒を含まないことを要求していますが、実際にはそうである場合があります。 また、うるう秒のサポートに応じて、difftime関数の動作は異なります。 注意してください。 - ユリウス日にはうるう秒も含まれません。
- mysqlでは、うるう秒は表示されません。 60または61秒(s)の代わりに、常に59( link )があります。 しかし、同時に、すべてがサポートされており、UTCでUNIXエポックを処理している場合は正しく動作します。
- sqliteの一般的な推奨事項:日付を整数として保存します。これには、既にうるう秒が含まれています(mysqlの場合と同様)。 そうすれば、常に正確な時間を知ることができます。
その他
- time_zone(mysql)変数の値がSYSTEMの場合、システムゾーン(サーバーの起動時にシステムで構成された)が現在のゾーンとして選択されます。
- http://www.onlineconversion.com/unix_time.htm Unix時間を通常時間に変換するサイト
- GMT-時代遅れの概念と見なすことができるため、この記事では主にUTCを使用します。