ファイヤープロジェクト
日付と時刻
2003-07-20T15:13+09:00   matsu
プログラムによっては日付や時刻を取得する必要がある.コンピュータで日付や時刻を操作する場合は,「エポックの起点」からの経過秒(もしくはミリ秒)をもとに行うことが多いようだ(もちろんlocaleも考慮する).「エポックの起点」はシステムによるが,UNIXではGMT 1970年1月1日0時らしい.epochを辞書で引いてみたら,「ある特徴のある時代」らしい.ネットで調べると,「基準時刻」と訳してあったりする.こちらの方がわかりやすいだろうか.
time関数によってエポックの起点からの経過秒を取得できる.
#include <time.h>
#include <stdio.h>
#include <unistd.h>

/* エポックの起点からの経過秒を表示 */
int main()
{
  int i;
  time_t current_time;

  for(i=0;i<10;i++){
    /* time関数は引数に返り値と同じ値を格納する.
       ここではヌルポインタを引数にしている*/
    current_time=time((time_t *)0);
    fprintf(stdout,"Current time is %ld\n",current_time);
    sleep(1);
  }
  exit(0);
}
ある処理の直前と直後にtime関数を呼び出してその差を取れば,その処理に要した秒数を取得できる.しかし,time関数の返り値はtime_t型.移植性を考慮するなら差をとるのではなくてdifftime関数を使用した方がいいらしい(time_t型が実際にはint値でない場合を考慮して,ということだろうか).
#include <time.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
  int i;
  time_t pre_time;
  time_t post_time;

  /* ループ前の時刻 */
  pre_time=time((time_t *)0);

  for(i=0;i<10;i++){
    fprintf(stdout,"%ds\n",i);
    sleep(1);
  }

  /* ループ後の時刻 */
  post_time=time((time_t *)0);

  /* ループに要した時間を出力 */
  /* difftime(time_t a,time_t b)はa-bをdouble型で返す. */
  fprintf(stdout,"%gs passed \n",difftime(post_time,pre_time));

  exit(0);

}
時刻を秒で表されてもどうだろう,という時がある.そういう時にはgmtime関数を使用してtm構造体をつくる.これはJavaでいうと,time関数がDateオブジェクト.getTime()で,tm構造体はCalendarオブジェクトという感じだろうか.実際,tmはtm->tim_secとかして使用するし,Calendarもget(Calendar.SECOND)として使用する.
tm構造体は少なくても以下のメンバを含む.
inttm_sec秒(0〜61)閏秒を考慮
inttm_min分(0〜59)
inttm_hour時間(0〜23)
inttm_mday日(1〜31)
inttm_mon月(0〜11).実際の月とは1ずれている
inttm_year年(1900年が起点)
inttm_wday曜日(0〜6).0は月曜
inttm_yday1年の中での通算年数(0〜365)
inttm_isdst夏時間
ここではtm_...と書いている.私のシステムではこうだったのだ(/usr/include/time.h参照).が,Linuxプログラミング(ISBN-7973-0819-2)ではtim_....と書いてあった.なぜだろうか.
#include <time.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
  struct tm *s_time;
  time_t the_time;

  (void) time(&the_time);
  s_time=gmtime(&the_time);

  /* tm構造体のs_timeから年月日時分秒をとりだす. */
  /* tm_yearは1900年からの経過年なので1900を足す */
  printf("%4d年%2d月%2d日 %2d時%2d分%2d秒\n",
	 1900+s_time->tm_year,s_time->tm_mon,s_time->tm_mday,
	 s_time->tm_hour,s_time->tm_min,s_time->tm_sec);

  exit(0);
}
このプログラムを私のシステムで実行すると,9時間ずれていた.プログラムの出力はGMTの値だからだ.これをロケールにあわせた出力にするにはgmtime関数のかわりにlocaltime関数を使用する.というか関数名が体を表している.
#include <time.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
  struct tm *s_time;
  time_t the_time;

  (void) time(&the_time);
  /* gmtimeではなくlocaltimeを使う */
  s_time=localtime(&the_time);

  /* tm構造体のs_timeから年月日時分秒をとりだす. */
  /* tm_yearは1900年からの経過年なので1900を足す */
  printf("%4d年%2d月%2d日 %2d時%2d分%2d秒\n",
	 1900+s_time->tm_year,s_time->tm_mon,s_time->tm_mday,
	 s_time->tm_hour,s_time->tm_min,s_time->tm_sec);

  exit(0);
}
逆にtm構造体にデータをいれてそこからtime_t型の値を求めるにはmktime関数を使用する.また,上の例では自分でフォーマットを指定して時刻を表示したが,時刻を既定のフォーマットに整形して表示する関数にasctime関数,ctime関数がある.
#include <time.h>
#include <stdio.h>
#include <unistd.h>

int main()
{
  time_t the_time;
  time_t clone_time;
  struct tm *s_time;

  (void) time(&the_time);
  fprintf(stdout,"%ld\n",the_time);

  /* mktimeはローカルタイムで記述されているtm構造体を引数にとるので注意. */
  s_time=localtime(&the_time);
  clone_time=mktime(s_time);
  fprintf(stdout,"%ld\n",clone_time);

  /* asctimeやctimeが返す文字列を表示 */
  /* ctime関数はasctime(localtime(t))と等価 */
  fprintf(stdout,"%s",asctime(s_time));
  fprintf(stdout,"%s",ctime(&clone_time));

}
tm構造体の要素を取り出したり,逆に要素に値を代入したりするのはちょっと面倒くさい.そこで,便利な関数としてstrftimeとstrptimeがある.strftimeは引数のtm構造体から指定されたフォーマットに必要な要素をとりだして文字列に格納する.sprintfのtime版のような関数だ.
#include <time.h>
#include <stdio.h>

int main()
{
  time_t target_time;
  struct tm* target_time_tm;
  char* time_string;
  int string_length=20;

  time_string=(char *)malloc(string_length*sizeof(char));

  /* time_tをとってそれからローカルなtm構造体を取得. */
  (void)time(&target_time);
  target_time_tm=localtime(&target_time);
  
  /* tm構造体からフォーマットで指定した文字列を生成 */
  (void)strftime(time_string,string_length,"%Y %m/%d %H:%M:%S",target_time_tm);

  /* 生成した文字列を出力 */
  fprintf(stdout,"%s \n",time_string);

  exit(0);

}
実行すると以下のように出力される.
2002 11/03 02:00:34
strptimeは逆に引数の文字列が指定されたフォーマットの順に記述されていると仮定してtm構造体の要素を設定していく.
#include <time.h>
#include <stdio.h>

int main()
{
  /* 日付を表す文字列 */
  char* source_string="2002 11 3 01 50 30";
  struct tm* result_tm;

  result_tm=(struct tm*)malloc(sizeof(struct tm));

  /* source_stringからtm構造体を設定 */
  strptime(source_string,"%Y %m %d %H %M %S",result_tm);

  /* tm構造体の値を表示.(これには先程のstrftimeの方が便利) */
  fprintf(stdout,"%d %d/%d %d:%d:%d\n",
  1900+result_tm->tm_year,result_tm->tm_mon+1,result_tm->tm_mday,
  result_tm->tm_hour,result_tm->tm_min,result_tm->tm_sec);

  exit(0);
}
なお,strftimeとstrptimeのフォーマットに使用できる変換指定子はmanに書いてある.
matsu(C)
Since 2002
Mail to matsu