異なるタイムゾーンで時間を操作する機能

時間を操作することに関するいくつかの質問と決定が蓄積されているという事実のため、私は小さなレビューをすることにしました。


データベース内のさまざまなタイプのデータを操作する


MYSQL

mysqlには時間を表すためのいくつかの標準データ型があります; TIMESTAMPDATETIMEを見ていきます。
ドキュメントには、変換ポリシーは一部のタイプのデータに適用されますが、一部のデータには適用されないと書かれています。
実際には、すべてがはるかに興味深いものです。 いくつかの例を見てみましょう。
テーブルを作成します。
 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 ) 。
結論:


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ヨーロッパ/モスクワヨーロッパ/ロンドン
時間出力111512
isdst出力001
ゾーン出力UTCMskBst

  time_t t = 1355137685; // 2012-12-10 11:08:05 (UTC) 

システム内のゾーンUTCヨーロッパ/モスクワヨーロッパ/ロンドン
時間出力111511
isdst出力000
ゾーン出力UTCMskGMT

 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ヨーロッパ/モスクワヨーロッパ/ロンドン
時間出力111111
isdst出力000
ゾーン出力GMTGMTGMT

  time_t t = 1355137685; // 2012-12-10 11:08:05 (UTC) 

システム内のゾーンUTCヨーロッパ/モスクワヨーロッパ/ロンドン
時間出力111111
isdst出力000
ゾーン出力GMTGMTGMT

 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ヨーロッパ/モスクワヨーロッパ/ロンドン
ピンt1339326485(2012年6月10日日曜日11:08:05 GMT)1339312085(2012年6月10日日曜日07:08:05 GMT)1339326485(2012年6月10日日曜日11:08:05 GMT)
時間出力111112
isdst出力001
gmtoff出力014400(4 * 60 * 60)3600(1 * 60 * 60)
ゾーン出力UTCMskBst

ロンドンのtm_hourフィールドとtm_isdstフィールドが変更されていることに注意してください。これは、詳細な時間構造のフィールドを正規化するプロセスの一部です。
今のために
 bdt.tm_mon = 11; // 11th mon - Dec 

システム内のゾーンUTCヨーロッパ/モスクワヨーロッパ/ロンドン
ピンt1355137685(2012年12月10日月曜日11:08:05 GMT)1355123285(2012年12月10日、07:08:05 GMT)1355137685(2012年12月10日月曜日11:08:05 GMT)
時間出力111111
isdst出力000
gmtoff出力014400(4 * 60 * 60)0
ゾーン出力UTCMskGMT

 Function: time_t timegm(struct tm *brokentime) 

UTCで動作します。
システム内のゾーンUTCヨーロッパ/モスクワヨーロッパ/ロンドン
ピンt1339326485(2012年6月10日日曜日11:08:05 GMT)1339326485(2012年6月10日日曜日11:08:05 GMT)1339326485(2012年6月10日日曜日11:08:05 GMT)
時間出力111111
isdst出力000
gmtoff出力000
ゾーン出力GMTGMTGMT
今のために
 bdt.tm_mon = 11; // 11th mon - Dec 

システム内のゾーンUTCヨーロッパ/モスクワヨーロッパ/ロンドン
ピンt1355137685(2012年12月10日月曜日11:08:05 GMT)1355137685(2012年12月10日月曜日11:08:05 GMT)1355137685(2012年12月10日月曜日11:08:05 GMT)
時間出力111111
isdst出力000
gmtoff出力000
ゾーン出力GMTGMTGMT

結論:
ユーザーコンピューター上のプログラムでユーザーに時刻を表示する場合は、 timelocal/localtime関数を使用します。サーバーで作業している場合は、 timegm/gmtime使用しtimegm/gmtime 。 また、同僚のいずれかまたはサードパーティのライブラリで*ローカル*関数を使用する場合に備えて、サーバーにUTCゾーンを設定します。 ユーザーのコンピューター上でも、UTCで長期間保存して作業するため、ユーザーがゾーンを変更しても、すべての日付は正しいままです。

ご注意


Linuxでタイムゾーンを設定する

debベースの分布と、タイムゾーンを設定するためのいくつかの鉄の方法のみを検討してください。

LEAP SECOND

通常、これは別のトピックなので、うるう秒に関するこの記事の例はありません。 特定の関数がどのように機能するか、さまざまなデータベースがどのように動作するかを自分で確認できます。mysqlの例を次に示します。

その他

Source: https://habr.com/ru/post/J162341/


All Articles