Version 13.23
日時の取得
「今回は、おさらいも含めて、日時の取得について見ていきます」
『一度 Version 7.20 ( No.140 ) でやったんだもんね』
「あのときはとりあえずの説明だったからね。アルゴリズムに使う場合には
とりあえずじゃまずいから」
『はーい』
「でもとりあえずはとりあえず」
void OutputDate()
{
// まずは「のべ秒」を取得。
time_t lTime_t;
time( &lTime_t );
TRACE( "%u秒\n", lTime_t );
// 1083123673秒
// 次にローカル時間に変換します。
tm *pstTm;
pstTm = localtime( &lTime_t );
TRACE
( "%d年%d月%d日%d曜日%d時%d分%d秒\n"
, pstTm->tm_year
, pstTm->tm_mon
, pstTm->tm_mday
, pstTm->tm_wday
, pstTm->tm_hour
, pstTm->tm_min
, pstTm->tm_sec
);
// 104年4月6日4曜日12時52分36秒
// 再変換します。
time_t lTime_t2;
lTime_t2 = mktime( pstTm );
TRACE( "%u秒\n", lTime_t2 );
// 1083123934秒
}
『ランタイムで時間取ってくるのね』
「一番手軽で標準的だからね。まず」
time_t lTime_t;
time( &lTime_t );
「この time() 関数で、世界標準時の1970年1月1日0時0分0秒から
現在時刻までの【のべ秒】を取得します」
『のべ秒って微妙に不思議……』
「基本的には、日時はこののべ秒で扱った方がいいから。計算や比較が簡単
だし、最初に取得するのはこれだしね」
『なにわともあれ基本中の基本ってとこね』
「でも、これは世界標準時だし、秒だし、というわけで全然使えません。そ
こで、年月日時分秒に変換します」
tm *pstTm;
pstTm = localtime( &lTime_t );
『またランタイムね』
「 localtime() はその地域の日時、わかりやすく言うと日本の日時を取得
するランタイムです」
『ってことは、外国の日時も取得できるの?』
「一応ね。 _tzset() っていうラインタイムや SetTimeZoneInformation()
っていう API を使わなきゃいけないんで少し面倒だけど。あと、標準時が
取得した場合には gmtime() を使えばいいから」
『でもたぶん使わないね……』
「それと重要なこと。この関数、他のとちょっと違うところない?」
『違うところ……』
「戻り値とか」
『戻り値、構造体のポインタ……? ポインタが戻り値って変くない? 普
通ポインタ返したら free() で解放したりとかしなきゃいけないとか、もう
使えなくなってるとか』
「そう、実は localtime() は tm 構造体の変数をひとつ持っていて、その
ポインタを常に返してるんです」
『?』
「ようするに、何度呼んでも同じポインタが返ってくるってこと」
tm *pstTm;
pstTm = localtime( &lTime_t );
TRACE( "%X\n", pstTm );
// 422250
tm *pstTm2;
pstTm2 = localtime( &lTime_t );
TRACE( "%X\n", pstTm2 );
// 422250
『あ、アドレスが同じだ』
「つまり同じ変数のポインタを返してるってことです。これはとても重要。
この例で、1度目の localtime() と2度目の localtime() の間に time()
を呼んで lTime_t を変えていたら?」
『んー、 pstTm と pstTm2 の中身って変わるよね……?? でも、このふ
たつはポインタは同じだから、同じ変数が変えられる……???』
「そう、同じ変数が変えられるから、見た目的には pstTm の中身が勝手に
変えられちゃうことになるんです」
『げ!』
「だから、もし前に取得した localtime() の戻り値をずっと使いたい場合
には、別に tm 構造体の変数を作って、そこにコピーする必要があります」
『めんどい……』
「確かにね。引数で tm 構造体のポインタ受けるようにすればいいだけなん
だけどね。ランタイムは設計が古いから」
『うー』
「さて、 localtime() にはまだまだ問題があります。まずは中の数値」
TRACE
( "%d年%d月%d日%d曜日%d時%d分%d秒\n"
, pstTm->tm_year
, pstTm->tm_mon
, pstTm->tm_mday
, pstTm->tm_wday
, pstTm->tm_hour
, pstTm->tm_min
, pstTm->tm_sec
);
// 104年4月6日4曜日12時52分36秒
「これは 2004 年 5 月 6 日 12 時 52 分 36 秒に実行した結果です」
『まず年が違うね』
「この年は、西暦4桁の年から 1900 を引いた数」
『なんでわざわざ?』
「たぶん昔は型が小さかったんじゃないかな、 unsigned char とか」
『そか、それだと 2004 は入らないね』
「昔はコンピューターの性能が悪かったから、そういうところで切りつめら
れてたからね。月も同じような理由も含まれてるかな」
『月? あ、この前言ってたね、1ヶ月ずれてるんだよね』
「そう、1月は 0 、12月は 11 」
『そのまま使うとずれちゃう……』
「この月で〈1月のみの処理〉とかしたり、あとファイルや画面、エディッ
トボックスとかの入出力に使うと問題になるから」
『どうすればいいの?』
「変換用の関数や比較用の関数を使ったり」
const int JANUARY = 0;
const int FEBRUARY = 1;
//...
const int DECEMBER = 11;
「みたいに定数値を用意しておいてそれで比較したり」
『これはわかりやすいね』
「あと同じく曜日もこういうふうに定義しておいた方がいいかも」
『うわ、なんか数字が出てる……』
「文字列変換には strftime() っていうランタイムがあるからそれを使うの
もいいかも」
setlocale( LC_ALL, "japanese" );
char pchTime[256];
strftime
( pchTime
, 255
, "%Y年%#m月%#d日%a曜日%H時%M分%S秒"
, pstTm
);
TRACE( "%s\n", pchTime );
// 2004年5月6日木曜日12時52分36秒
『おおっ! これはかなりちゃんとしてるね』
「年のちゃんとした西暦表示、月の +1 、曜日の漢字表示をしてくれるから」
『 %Y が年、 %#m が月…… printf() に似てるね』
「仕組みは同じだから、使いやすいと思うよ。ちなみに # は、これを付け
ると余分な 0 を取ってくれます」
『余分なゼロ?』
「たとえば、この例だと "%m月" って書いたら "05月" ってなります」
『あ、 # を付けなければ自動的に 0 入れてるくれるんだ』
「そういうこと。プログラム上でどちらがいいか判断して変えてください」
『はーい。あ、あと、 setlocale() ってなに?』
「これは、 strftime() の表記方法を変えるランタイム。これがないと」
『あ、木曜日が Thu になった』
「こうやって指定すれば日本語バージョンで表示されるから」
『曜日とか英語だとかなりめんどいものね……』
「さて、こうやって文字列化したものをまた tm 構造体に戻す場合、簡単な
方法はありません」
『やっぱ、ひとつずつ見ていって、 sscanf() とかで int に変換しなきゃ
いけないの?』
「それしかないんだけど、そのためには表記方法が統一されてないと」
『?』
「つまり、 "2004年5月6日" か "2004/5/6/" か "2004-5-6" とか」
『うわ、そっか、書き方っていっぱいあるんだ』
「全ての表記方法に対応することは不可能だから、どれかひとつだけ受け取
るような感じかな」
『受け取る?』
「そう、それも重要かな。たとえばダイアログで日時を受け取る場合には、
こういう自由に書けるような形じゃなくて、年、月、日、それぞれエディッ
トボックスを分けて入力してもらうのが楽」
『インターネットとかでも普通そうだよね』
「〈日時の文〉を元に日時を抜き出す、っていう処理はできる限り避けた方
がいいよ、ものすごくちゃんと詰めないとうまくいかないから」
『だよね……区切りもそうだし、 0 が入ってるかとか、スペースとか……』
「さて、今のは文字列から tm 構造体への変換の話だったけど、 tm 構造体
から〈のべ秒〉への変換は簡単にできます」
// 再変換します。
time_t lTime_t2;
lTime_t2 = mktime( pstTm );
TRACE( "%u秒\n", lTime_t2 );
// 1083123934秒
『お、ホントだ』
「これはとても重要。 tm 構造体は文字列への変換や年月日時分秒単位の処
理には向いているんだけど、比較や計算には向いてないから」
『? 月の比較なら pstTm->tm_mon を比較、じゃいけないの?』
「そりゃ本当に月だけ比較するならいいけど……普通は日時全体で比較する
から、月の比較の前に年の比較をしないと」
『あ、年を比較して、そのあと月を……そか、ちゃんと比較するならそう
やって順番に全部の桁比較しなきゃいけないんだ。面倒……』
「だから秒に変換して比較するわけ」
『そっか! 秒に変換しちゃえばそれだけで比較できちゃうんだから』
「というわけで次回はその方法について説明します」
/*
Preview Next Story!
*/
『なんか、細かいノウハウが多いね』
「日時関係はちょっと間違えると全然結果が違うからね」
『そういうのって怖いね』
「だからそう言ってるでしょ」
『はい……』
「というわけで次回」
< Version 13.24 日時の作成 >
『につづく!』
「次回もそういう注意点満載です」
『でもちょっと胃が痛くなる……』
「プログラミングっていうのはそういうもの」