none
時刻取得について RRS feed

  • 質問

  • VS2008, MFCベースでWindows Appを作っています。

     

    時刻を取得するプログラムでCTime::GetCurrentTime()を用いて実装していますが、プログラムが実行中にWindowsの時計からタイムゾーンを変更しても追従してくれず困っています。

    ランタイムにタイムゾーンが変更された際に追従するにはどのように実装するのが正しいでしょうか?

     

    また、Windows7から(?)追加になった追加の時計の情報も取りたいのですが、取得方法が分からず困っています。第二、第三の時計の取得APIはありますでしょうか?

    お手数掛けますが宜しく御願いいたします。

    2011年11月22日 5:30

回答

  • CTime::GetCurrentTime() は、UTC(世界協定時刻)を返す関数です。ですので、現在のタイムゾーンがどうなっているか?の影響は受けません。

    タイムゾーンが日本の場合に、単純に取得して表示すると9時間ずれた値を返してきます。

    現在設定されたタイムゾーン分変更するという場合は、GetSystemTimeAdjustment API などを利用して設定を反映させるか、GetLocalTime API など、時間情報を現在のタイムゾーンが反映された値で返すAPIを利用するなどして取り出します。

    ちなみに、Vista以降の複数個時計への対応は、GetDynamicTimeZoneInformation API や、DYNAMIC_TIME_ZONE_INFORMATION 構造体のリファレンスを一読してみてください。

     


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク EmoraEmora 2011年11月23日 7:07
    2011年11月22日 8:10
  • CTime クラスの説明に「CTime の値は、協定世界時刻 (UTC: Coordinated Universal Time) を基準にしています。」とあり、タイムゾーンに依存しない形で値を保持しています。CTime::Format()CTime::FormatGmt()の2種類が存在するのはそのためで、前者はローカル時間で書式化してくれるはずです。

    とっちゃんさんがGetDynamicTimeZoneInformation()などを挙げられていますが、こちらは違うはずです。質問されている機能は、OSではなくエクスプローラーもしくはタスクバーの情報なので、Windows APIでは取得できないかな、と思います。

    • 回答としてマーク EmoraEmora 2011年11月23日 7:07
    2011年11月22日 12:04
  • CTime::GetLocalTm() は、CRT の localtime 関数(VS2010 2038年問題を回避するために _localtime64_s を利用)を利用して情報を設定しています(VS2010のソースで確認)。

    CRTのソースは参照していないのでわかりませんが、プログラム起動後のコンパネの修正が反映されないということは、タイムゾーンの情報をキャッシュしているのだと思います。

    一般的にタイムゾーンはプログラムの実行中にポンポン切り替わったりするものではありません。そのため、一度設定したらキャッシュしている可能性はあります。

    CRTのタイムゾーンの設定に関連するものに _tzset() という関数があります。リファレンスを読むと、_ftime と localtime の時刻修正のために使用するとありますので、呼び出す前にこの関数を実行してみると意図した動作になると思います。

    試してみてください。


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク EmoraEmora 2011年11月24日 7:38
    2011年11月24日 5:45
  • すみません、自己解決致しました。

     

    GetLocalTm()を呼び出す前に _tzset() をすれば良いのですね。この儀式を行うとタイムゾーンを変更した直後に反映してくれました。

     

    お手数掛けました。

    • 回答としてマーク EmoraEmora 2011年11月24日 7:38
    2011年11月24日 5:40

すべての返信

  • CTime::GetCurrentTime() は、UTC(世界協定時刻)を返す関数です。ですので、現在のタイムゾーンがどうなっているか?の影響は受けません。

    タイムゾーンが日本の場合に、単純に取得して表示すると9時間ずれた値を返してきます。

    現在設定されたタイムゾーン分変更するという場合は、GetSystemTimeAdjustment API などを利用して設定を反映させるか、GetLocalTime API など、時間情報を現在のタイムゾーンが反映された値で返すAPIを利用するなどして取り出します。

    ちなみに、Vista以降の複数個時計への対応は、GetDynamicTimeZoneInformation API や、DYNAMIC_TIME_ZONE_INFORMATION 構造体のリファレンスを一読してみてください。

     


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク EmoraEmora 2011年11月23日 7:07
    2011年11月22日 8:10
  • CTime クラスの説明に「CTime の値は、協定世界時刻 (UTC: Coordinated Universal Time) を基準にしています。」とあり、タイムゾーンに依存しない形で値を保持しています。CTime::Format()CTime::FormatGmt()の2種類が存在するのはそのためで、前者はローカル時間で書式化してくれるはずです。

    とっちゃんさんがGetDynamicTimeZoneInformation()などを挙げられていますが、こちらは違うはずです。質問されている機能は、OSではなくエクスプローラーもしくはタスクバーの情報なので、Windows APIでは取得できないかな、と思います。

    • 回答としてマーク EmoraEmora 2011年11月23日 7:07
    2011年11月22日 12:04
  • とっちゃんさん、 佐祐理さん、有難うございます。

    タスクバーの情報なんですねー、残念です。

    2011年11月23日 7:05
  • お手数掛けております。相変わらずハマっております。

     

    GetCurrentTime()はUTCを返すのは分かったのですが、Format()、或いはGetHour()は「この関数は、GetLocalTm 関数を呼び出します。GetLocalTm 関数は、内部で静的に割り当てられているバッファを使用します。このバッファのデータは、ほかの CTime メンバ関数を呼び出すと上書きされます。」という事で、LocalTimeになって帰ってきます。

    しかし、相変わらずプログラム起動後にWindowsのTimeZoneを変更しても追従してくれません。

    TimeZone変更後にプログラム再起動するときちんと新しいTimeZoneで時刻取得が出来るのですけども。

     

    ためしに環境変数を見てみると( str.Format( _T( "%d" ), _get_timezone ); )、これもやはりプログラム起動後にTimeZoneを変更しても変化は無くTimeZone変更後にプログラム再起動で差分が変化します。

     

    TZ環境変数を明示的に更新、或いは指定しないとうまく動作しないのでしょうか。

    2011年11月24日 5:19
  • すみません、自己解決致しました。

     

    GetLocalTm()を呼び出す前に _tzset() をすれば良いのですね。この儀式を行うとタイムゾーンを変更した直後に反映してくれました。

     

    お手数掛けました。

    • 回答としてマーク EmoraEmora 2011年11月24日 7:38
    2011年11月24日 5:40
  • CTime::GetLocalTm() は、CRT の localtime 関数(VS2010 2038年問題を回避するために _localtime64_s を利用)を利用して情報を設定しています(VS2010のソースで確認)。

    CRTのソースは参照していないのでわかりませんが、プログラム起動後のコンパネの修正が反映されないということは、タイムゾーンの情報をキャッシュしているのだと思います。

    一般的にタイムゾーンはプログラムの実行中にポンポン切り替わったりするものではありません。そのため、一度設定したらキャッシュしている可能性はあります。

    CRTのタイムゾーンの設定に関連するものに _tzset() という関数があります。リファレンスを読むと、_ftime と localtime の時刻修正のために使用するとありますので、呼び出す前にこの関数を実行してみると意図した動作になると思います。

    試してみてください。


    わんくま同盟,Microsoft MVP for Visual C++(Oct 2005-) http://blogs.wankuma.com/tocchann/
    • 回答としてマーク EmoraEmora 2011年11月24日 7:38
    2011年11月24日 5:45