UNIXライクなシステムの異なるタイムゾーンで時間を操作する方法

アプリケーションがローカル時間だけでなく、他のタイムゾーンでの表現にも依存している場合、異なるタイムゾーンで時間を表現するのが困難になる可能性があります。 遭遇していませんか? したがって、アプリケーションをUnixの世界に移植しませんでした。



実際、Windowsの場合、プログラマーにはタイムゾーンを操作するための便利な専用WinAPI関数のセットが提供されます。 例は、 TIME_ZONE_INFORMATION 構造、それに加えてGetTimeZoneInformation関数です。



しかし、UTC + 0に対するオフセット、「サマータイム」に切り替えるためのルールを知る必要がある場合は、うるう年をうるう秒および他の特定の情報を考慮し、Unixのようなオペレーティングシステムでこれをすべて行う必要があります。 この記事は、C / C ++でこのすべてのジャンクを扱う練習に専念しています。



このトピックは、さまざまな観点から多くの記事で繰り返し取り上げられてきましたが、特定の言語、システム、およびテクノロジーの実用的な例はめったにありません。 例はスタックオーバーフロー(多くの質問があります)で見つけることができ、Habréでこのトピックは理論的な観点から非常に深く触れられました。 さらに、ローカル時間のトピックに関するミニスタディもあり、一見するとコンピュータ時間の問題は決して些細なものではないことがわかります。 独自の調査を実施した後、受け取ったかなりの量の情報を簡単にわかりやすく共有したいと思います。さまざまなタイムゾーンの時間を変換する方法でそれらを発行しました。



クラシック:C標準ライブラリ


はい、 time.h



ヘッダーで定義されているその非常に小さな関数を使用して、時間変換を実行することもできます。 一定の制限があります。



実際、すべてのローカルに依存する関数( gmtime()



localtime()



)は、 TZ



環境変数を使用してロケールパラメーターを決定します。 これは、時間を目的のタイムゾーンに変換するには、最初にこの変数を(必要なゾーンの名前で)設定し、変換関数を呼び出してから、環境からTZ



を再度削除する必要があることを意味します。 マルチタスクとマルチスレッド用でない限り、すべてがうまくいくでしょう。 当然、この方法では競合が発生し、予測が困難なエラーが発生する可能性があります。



このアプローチを使用したサンプルコード:



 putenv("TZ=Asia/Calcutta"); ///      tzset(); ///    time_t timeToConvert = time(0); ///     struct tm *pCalcuttasTime; ///      pCalcuttasTime = localtime(&timeToConvert); ///  putenv("TZ="); ///   TZ /// ...     ...
      
      







tz database 、またはバイトのジャグリング


独自の目的にOlsonデータベースを使用するのが最も望ましいオプションです。 利点は明らかです:データベースは、地球のあらゆるコーナーで考えられるすべての移行ルールを最も完全に反映し(前世紀の初めからこれらのルールに変更が加えられた場合)、多くのシステムに配布され( /usr/share/zoneinfo



参照)、ベースが統一されていますシステムで更新されました。 しかし、彼女と一緒に仕事をしようとしたため、私はこのオプションを放棄することにしました。



データベースはバイナリ形式で配布されます(これにはzic



コンパイラーが使用されます)。 形式の説明は、 tzfile.h



ヘッダーファイルにあります(公式のFTPデータベースを使用して検索してください)。 データベースを操作するためのツールが見つかりませんでした(たぶん見た目が悪いのでしょうか?)。 しかし、必要なタイムゾーンのファイルを読み取ろうとすると、データ解釈の問題に遭遇しました。これらすべての微妙さと用語では、このすべての掘りの目的を忘れて頭を消すことができます。 そして、そのような些細なことを無視するために、最も適切で便利なツールを使用することが決定されました。



Boost.Date_Time


そのような状況でよく起こるように、 ブーストは救助に来ます。 公式ドキュメントの簡単な翻訳を含むDate_Timeライブラリセットの幅広い可能性に関する記事がすでにありました。 ところで、不必要な依存関係をプロジェクトに追加したくない人にとっての朗報は、ライブラリがヘッダーのみであることです(特定の形式の文字列からタイムスタンプオブジェクトを作成するような特定の場所を除きます)。



問題を解決するには、2つの方法があります。目的のタイムゾーンのルールをプログラム内のハードコードで書き留める(そしてそれを嫌う)か、すべてのルールを特別なCSV形式のファイルに保存する。 そのようなファイルは、その後自動的に更新できます(そして、非常に重要な最新の移行ルールを維持します)。 このファイルは、ブースト配布( date_time_zonespec.csv



)で配布されますが、 他の場所から取得することもできます。 さらに、ファイルの使用には、とりわけ、すべての地域のルールが含まれています。



短所もありません。 遷移規則が異なっていた20世紀の初め頃にタイムスタンプを変換する必要がある場合はどうなりますか? そのような場合も考慮する必要があり、残念ながら、ここでのブーストはあまり役に立たないかもしれません。



たとえば、一連のDate_Timeライブラリの機能を使用して時間を変換するコードを提供します。



 #include "boost/date_time/local_time/local_time.hpp" #include "boost/date_time/posix_time/posix_time.hpp" using namespace boost::local_time; using namespace boost::posix_time; ptime convertUTC0toCustomTimeZone(const ptime &utcTime, const std::string &tzName) { ///     tz_database tzDB; tzDB.load_from_file("./date_time_zonespec.csv"); ///    time_zone_ptr timeZone = tzDB.time_zone_from_region(tzName); /// tzName   "Asia/Calcutta" ///     local_date_time localTime(utcTime, timeZone); /// ...    return localTime.local_time(); }
      
      







まとめ


ブーストオプションを使用することを好みました。 もちろん、tzデータベースを使用すると、タイムゾーン情報を最新の状態に保つ手間や、移行ルールが変更されたタイムスタンプを変換する手間が省けます。 しかし、ほとんどのアプリケーションでは、このようなトリックは役に立たず、ブーストは日付の操作を非常に便利にします。



All Articles