Version 13.24
日時の作成
「今回は好きな日時を作る方法について説明します」
『好きな日時? 夕暮れ時の淡い雰囲気が好きとか?』
「いやそうじゃなくて、 time() 関数だけだと現在日時しか取得できないで
しょ。でもプログラムではどんな日時も tm 構造体に入れられないと」
『そか、それができないと現在日時しか扱えないもんね』
「そういうこと。だから、まずは任意の日時を作成できるようにします。基
本的に、任意の日時の作成方法は、次の3通り」
1. time() → localtime() → 日時セット → mktime()
2. tm 構造体作成 → 日時セット → mktime()
3. 直接のべ秒を作成
『なんか最後のはすごく力業っぽいね……』
「うん、上から順に簡単なものから並べているから。まずは」
1. time() → localtime() → 日時セット → mktime()
「から。これは、前回のプログラムとほとんど同じ」
void MakeDate()
{
// まずは「のべ秒」を取得。
time_t lTime_t;
time( &lTime_t );
TRACE( "%u秒\n", lTime_t );
// 1083123673秒
// 次にローカル時間に変換します。
tm *pstTm;
pstTm = localtime( &lTime_t );
setlocale( LC_ALL, "japanese" );
char pchTime[256];
strftime
( pchTime
, 255
, "%Y年%#m月%#d日%a曜日%H時%M分%S秒"
, pstTm
);
TRACE( "%s\n", pchTime );
// 2004年5月11日火曜日12時42分46秒
// 年を 2010 年にします。
pstTm->tm_year = 110;
strftime
( pchTime
, 255
, "%Y年%#m月%#d日%a曜日%H時%M分%S秒"
, pstTm
);
TRACE( "%s\n", pchTime );
// 2010年5月11日火曜日12時42分46秒
// 秒に変換します。
time_t lTime_t2;
lTime_t2 = mktime( pstTm );
TRACE( "%u秒\n", lTime_t2 );
// 1083123934秒
}
「前回との大きな違いはここ」
// 年を 2010 年にします。
pstTm->tm_year = 110;
「こうやって年を変えれば」
// 2004年5月11日火曜日12時42分46秒
// 2010年5月11日火曜日12時42分46秒
『ちゃんと年だけ変わるわけねー、便利!』
「手っ取り早く変えるならこれが一番簡単かな。ただ、注意点がみっつあり
ます」
・localtime() が返す tm 構造体はグローバル変数。
・計算には使わない方がいい。
・tm 構造体のすべてのメンバ変数は使わない。
「まずひとつめから。前回説明したように、 localtime() が返す tm 構造
体は、グローバル変数です」
『そっか、こうやって年を変えると、他の tm 構造体も変わっちゃうかもし
れないんだ』
「そういうこと。必要なら複製を作った方がいいかもね。次の注意点は、こ
の方法は〈計算〉には使わない方がいい、ってこと」
『計算?』
「たとえば〈1時間後〉とか」
『え? だって tm_hour に 1 足せばいいだけじゃ……あ、繰り上がり』
「そういうこと。 tm_hour が 59 だったら、足した後 tm_wday も変えな
きゃいけない、それが 23 だったら」
『うわーきりないね』
「詳しくは次回説明するけど、基本的にはのべ秒で計算します。全部秒にし
て計算すれば、繰り上がりは考えなくていいからね」
『確かに……』
「最後の注意点は、 tm 構造体のすべてのメンバ変数を使うわけじゃない、
ってこと」
『そういえば、 tm 構造体ってメンバ変数いっぱいあるよね』
「でしょ。この中で、曜日の tm_wday や、1年間でののべ日の tm_yday は
使いません」
『セットするときにはこのふたつは無視していいってことね』
「そういうこと。以上を踏まえて、今度はいちから tm 構造体を作ってみま
す」
2. tm 構造体作成 → 日時セット → mktime()
「現在日付とは関係ない日時を作成したり、元々ある tm 構造体から作り直
すときにはこの方法。と言っても難しいところはなくて、年月日時分秒を
セットすればいいだけです」
void MakeDateInDirect()
{
// tm 構造体作成。
tm stTm;
// 0 で初期化。
memset( &stTm, 0, sizeof( stTm ) );
// 各データをセット。
stTm.tm_year = 104; // 2004 年
stTm.tm_mon = 4; // 5 月
stTm.tm_mday = 1; // 1 日
stTm.tm_hour = 13; // 13 時
stTm.tm_min = 35; // 35 分
stTm.tm_sec = 2; // 2 秒
setlocale( LC_ALL, "japanese" );
char pchTime[256];
strftime
( pchTime
, 255
, "%Y年%#m月%#d日%a曜日%H時%M分%S秒"
, &stTm
);
TRACE( "%s\n", pchTime );
// 2004年5月1日日曜日13時35分02秒
// 秒に変換します。
time_t lTime_t;
lTime_t = mktime( &stTm );
TRACE( "%u秒\n", lTime_t );
// 1083386102秒
// 確認のため、もう一度 tm 構造体に。
tm *pstTm;
pstTm = localtime( &lTime_t );
setlocale( LC_ALL, "japanese" );
strftime
( pchTime
, 255
, "%Y年%#m月%#d日%a曜日%H時%M分%S秒"
, pstTm
);
TRACE( "%s\n", pchTime );
// 2004年5月1日土曜日13時35分02秒
}
『6個のメンバ変数に入れればいいわけね』
「注意点は3つ。まず、必要なのはこの6つのメンバ変数で、他は関係あり
ません」
『さっき言ってた、他のメンバ変数の話ね』
「でも、他のメンバ変数を放っておくと変な値が入ったままになるから、
memset() で初期化します」
『 Version 12.11 ( No.234 ) で使ったのだね』
「 memset() を使うことで全部の変数に 0 を入れて初期化できます。これ
をしないと危険だから注意」
『最後の注意点は?』
「年と月」
『あ、年は 1900 引いた数で、月は 0 〜 11 、ってヤツね』
「そう。特に月はとても危険です。この例みたいに数字で扱うならまだし
も」
stTm.tm_mon = iMonth;
「こういうふうに変数で渡す場合、 iMonth は 0 〜 11 なのか 1 〜 12 な
のか、ものすごく気をつかう必要があります」
『う”、確かに……変数名見ただけじゃわかんないし』
「一番安全なのは、 tm::tm_mon 以外ではすべて 1 〜 12 で扱うこと」
『 tm::tm_mon でしか 0 〜 11 にしないんだ』
「そう。だって、 0 〜 11 っていう規則は、 tm 構造体だけのルールなん
だもの」
『あ、そっか、他で 0 〜 11 を使う理由がないんだ……』
「画面から入力されるにしても、ファイルから入力されるにしても、当然月
は 1 〜 12 。だから、プログラム上ではほとんど 1 〜 12 しか使わないよ
うにして」
『 tm::tm_mon に入れるときだけ…… - 1 すればいいの?』
「そういうこと。専用の関数を用意するといいかもね。ここはとてもバグの
できやすい場所だから気を付け過ぎるくらい気を付けて」
『はーい』
「さて、日時の作成方法の最後」
3. 直接のべ秒を作成
「だけど、普通はこんな方法使わないから必要ありません」
『あらら』
「どうしてもしてみたい場合には mktime() のソースコードを参考にしてみ
てください」
『って、それじゃ mktime() 使った方が早いってことじゃん』
「ま、そういうこと」
/*
Preview Next Story!
*/
『細かいルールや憶えることが多くて大変!』
「こういうノウハウは、本当は関数にしちゃうのが一番なんだけど」
『月の変換関数みたく?』
「そういうこと。全部は憶えきれないしね」
『だよねー』
「でもとりあえずは全部憶えてもらいます」
『げ』
「というわけで次回」
< Version 13.25 日時の計算 >
『につづく!』
「引き続きノウハウ集!」
『全部憶えきれるかな』
「火美ちゃん、自分の設定忘れてない?」