Version 13.22
最強の敵・日時
「ソートの次は、恐らく一番やっかいな存在、日時について見てみます」
『日時って、2004年1月1日とかのことだよね。そんなにやっかいな
の?』
「火美ちゃんみたいに思ってる人は多いんだけど、実際、すごくやっかいな
んです。まずそのことをしっかり認識しておくことが重要かな」
『具体的にはどんなことがやっかいなの?』
・進数の違う7つの「桁」で構成されている。
・進数が「桁」によって異なる。
・様々な型がある。
・様々な表記方法がある。
・文字列から日時を取り出すのが面倒。
・「曜日」がある。
・「月」がプログラムの値と意味的な値とで異なる。
・地域によって異なる。
・比較がややこしい。
・特定の桁だけを比較したい場合がある。
・「あり得ない日時」を作るのが難しい。
・取得するタイミングが微妙。
・テストが面倒。
・一般的なアルゴリズムが存在しない。
『多!!』
「ざっと上げてもこのくらいあるんです。日時を扱うってことはこのくらい
大変だってこと」
『まずそれを肝に銘じとけってことね』
「そういうこと。では、ひとつずつ見ていきます」
・進数の違う7つの「桁」で構成されている。
「この7つの桁は【年】【月】【日】【時】【分】【秒】【ミリ秒】です」
『ミリ秒?』
「秒より下の単位。使わないことが多いけど、一応」
『でもさ、これって【桁】なの?』
「そうだよ。たとえば」
39
「右から1桁目の 9 が1増えたら、2桁目の 3 が 1 増えて 4 になるで
しょ」
『繰り上がり……確かに、分が 59 から 60 になったら、上の桁の時が 1
増えるね』
「しかも」
・進数が「桁」によって異なる。
「これがやっかいなんだよね」
年:なし
月:12
日:28〜31
時:24
分:60
秒:60
ミリ秒:1000
『うわ……普段使ってるけど、こうまとめてみると複雑だねー』
「それ、重要かも」
『?』
「前回、アルゴリズムは〈法則化ができないと作れない〉って説明したけ
ど」
『そか、頭の中で使えてても、文章で法則が書けないとプログラムにできな
いもんね。そこが難しいわけだ』
「そういうこと。さらに、【日】は進数が月によって変わるから」
『うわ、これ最悪!』
「そういうこともあるから、基本的には〈各桁での数値の管理〉はしませ
ん」
・様々な型がある。
「 Version 7.20 ( No.140 ) で紹介したように、基本的には time_t と tm
を使います」
『ふたつクラスがあるわけね』
「他のライブラリも含めるといっぱいあるけど、基本的にはこのふたつ。使
い分けはこう」
time_t:のべ秒。
tm :各【桁】の情報。
『普通に使うんなら tm の方が便利そうだけど』
「 time_t から tm へは localtime() で、その逆は mktime() で変換でき
るから、その場その場で使いやすい方を使うことになるかな」
『どっちにも変換できるんだ』
「まぁ場合によりけりだけどね。ランタイムによってはどちらかの引数しか
受け取ってくれない場合がほとんどだから」
『使うランタイムで変えなきゃいけない……』
「それに関連して」
・様々な表記方法がある。
「〈YYYY年MM月DD日〉って表示するのか〈YYYY/MM/DD〉って表示するのか、
時間は12時間表記か24時間表記か、等々、ものすごくいろんな書式があ
ります」
『日本と外国とも違うしね』
「ちなみに日時の文字列化には strftime() を使います」
『リファレンス読むと、かなり色々変えられるんだね』
「このランタイムは tm 構造体を受け取るから」
『こっちは tm のみってことね』
「だから、このランタイム使わずに年月日時分秒を取り出して使うっていう
のもありかな」
『面倒そう……』
「それ以上に面倒なのがあります」
・文字列から日時を取り出すのが面倒。
「何せ表記方法がすごくいっぱいあるから」
『……ってゆーか、無理じゃない?』
「書き方を決めてもらわないと無理だね。だから、ホームページとかで入力
する場合は」
『年月日それぞれ別に入力するよね』
「そうしないと大変だからね。日時を受け取る場合には、必ず分けて受け取
るようにした方がいいかな」
『できなかったら?』
「1文字ずつ見ていくしかないね……では次」
・「曜日」がある。
「その月の第1月曜、とかの取得は面倒かな。 tm::tm_wday に曜日が入っ
ているから、それで計算しないと出せないから」
『こーゆーのって計算しなきゃいけないんだ』
「この計算自体が一種のアルゴリズムかな。では次」
・「月」がプログラムの値と意味的な値とで異なる。
「実は tm::tm_mon に入ってる月は、1月が 0 、12月が 11 なんです」
『げ、ひとつずれてる!』
「 C 言語は 0 から始めるのが習慣になってるからこうなっちゃってるんで
す。プログラム内だけで処理するならいいんだけど、入力したデータと比較
したりすると」
『思いっきりやばいね……』
「なので、月の比較やセットは十分に気を付けてください」
『月を見たら思い出せ?』
「では次」
『う”』
・地域によって異なる。
『あ、標準時ってヤツね』
「そう。基本的に time_t には標準時が入っています。この値から tm 構造
体に変換するとき、 gmtime() は標準時に、 localtime() は日本の時間に
変換します」
『自動的に日本に変換してくれるんだ!』
「ただ、このあたりは【ロケール】っていう設定が絡んできてちょっと複雑
なんです。日本語環境の Windows で VC++ で作ってる分には気にしなくて
いいけど」
『そか、一応自動だけど、ちょっと訳ありって感じね』
「まぁそう。では次」
・比較がややこしい。
「ある日付の範囲、たとえば2004年1月10日〜2004年1月15日
という範囲と、他の日付の範囲を比較する場合に」
○完全一致
○完全に含む
○一部含むのでもOK
○逆に含まれていればOK
○全く重なっていなければOK
「などなど、様々な比較方法があるんです」
『確かに2本の線がどう重なるか、って感じよね』
「こういった範囲指定をプログラムで書くときには、ついつい間違えちゃう
んだよね。範囲の方向、含むか含まないか、それに対象と被対象、つまりふ
たつの日付を取り違えたりとか」
『う”、ありそう』
「しかも」
・特定の桁だけを比較したい場合がある。
「上の例だと年月日でしか比較してないでしょ」
『そか、時分秒を取り除いて比較してるんだ』
「頭の中だと年月日だけかそうじゃないかって簡単な問題なんだけど、プロ
グラムだととても重要だから」
『日付が重なった時に一致しなくなっちゃうもんね……』
「さらに」
・「あり得ない日時」を作るのが難しい。
「たとえば25時とか、0日とか、比較する時や処理する時にはこういった
〈あり得ない日時〉が使えると便利なんだけど、これを time_t に変換する
のは難しいから」
『変換しなきゃいいじゃん』
「比較するときには普通 time_t を使うから。そうしないと年月日時分秒」
『全部比較しなきゃいけないね……』
「だけどそういうイレギュラーな日時は time_t に変換できないから、それ
こそひとつずつ比較しなきゃいけなかったり」
『うーん……』
「比較の面倒さはこんなところかな。あと」
・取得するタイミングが微妙。
「日時をプログラムの実行開始時に取るのか、それとも逐次取るのか。厳密
なシステムだと、中で日付が変わっちゃうと」
『違う日付で処理される……』
「プログラムの実行時間が長い場合には特に重要かも。それと」
・テストが面倒。
「日付は自由にコントロールできないから」
『特別な日付のテストが大変よね、わざわざ時計変えたりしなきゃいけない
んだから』
「ま、色々方法があるからなんとかなるんだけど、面倒なことには変わりな
いかな。最後に」
・一般的なアルゴリズムが存在しない。
『……へ?』
「つまり、ここまで説明した問題を解決してくれる便利なライブラリはな
い、ってこと」
『えええ!? こんなに問題いっぱいあるのに?』
「フリーので探せばあるかもしれないけど、標準的なのはないんだよね。そ
れに、日時のアルゴリズムは、ちょっと条件が違うと使えないもの多いか
ら」
『そなの?』
「比較のバリエーションとかね。対象の日時が増えたりするともう」
『作るしかない……』
「これだけやっかいってことは肝に銘じておいて」
/*
Preview Next Story!
*/
『簡単そうなものが実は大変なんだね』
「意外でしょ」
『うん』
「こういう細かい知識が、プログラム作る上では重要かな」
『というわけで次回』
< Version 13.23 日時の取得 >
「につづく!」
『こういうことって本に載ってないしね』
「そういうところもこの講座の売りかな」
『だからだらだら続いてるんだけどね』
「う”」