difftime関数が0を返す

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
担麺々

difftime関数が0を返す

#1

投稿記事 by 担麺々 » 10年前

年・月・日を入力させ、答えがそれより前か後かを表示して、ランダムな年・月・日を当てさせるゲームを作っています。
ソースコードは下記のとおりなのですが、プログラムを実行すると、どんな年・月・日を入力しても「正解です。」と表示されてしまいます。
difftime関数の返却値を代入した、変数diftmの値が0になっていることが問題だと思われますが、なぜ変数diftmの値が0になるのでしょうか。ご教示いただければ幸いです。

コード:

#include <stdio.h>
#include <time.h>

int is_leap(int year);
int monthdays(int year, int month);

int main(void)
{
	time_t crtt;
	struct tm *crtlt;
	struct tm anslt = {0};
	time_t anst;
	struct tm inlt = {0};
	int diftm = 1;

	srand(time(&crtt));
	crtlt = localtime(&crtt);
	anst = mktime(&anslt);

	anslt.tm_year = 1 + rand() % crtlt->tm_year;
	anslt.tm_mon = rand() %  12;
	anslt.tm_mday = 1 + rand() % monthdays(anslt.tm_year, anslt.tm_mon);

	printf("日付当てゲームを開始します。\n年月日を当ててください。\nなお、答えは1900年から現在の年までです。\n");

	do {
		printf("年:");			scanf("%d", &inlt.tm_year);
		printf("月:");			scanf("%d", &inlt.tm_mon);
		printf("日:");			scanf("%d", &inlt.tm_mday);

		if (inlt.tm_mon < 1 || inlt.tm_mon > 12)
			printf("入力が不正です。再入力してください。\n");
		else if (inlt.tm_year < 1900 || inlt.tm_year > crtlt->tm_year + 1900)
			printf("答えは1900年から現在の年までです。再入力してください。\n");
		else if (inlt.tm_mday < 1 || inlt.tm_mday > monthdays(inlt.tm_year, inlt.tm_mon))
			printf("入力が不正です。再入力してください。\n");
		else {
			time_t intm;

			inlt.tm_year -= 1900;
			inlt.tm_mon--;

			printf("%d\n", diftm); /*デバッグ用*/

			intm = mktime(&inlt);
			diftm = (int)difftime(anst, intm);
			printf("%d\n", diftm); /*デバッグ用*/

			if (diftm > 0)
				printf("それよりも後です。\n");
			else if (diftm < 0)
				printf("それよりも前です。\n");
		}
		printf("%d\n", diftm); /*デバッグ用*/
	} while (diftm);

	printf("正解です。\n");

	return 0;
}

int is_leap(int year)
{
	return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
}

int monthdays(int year, int month)
{
	int mday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

	if (month-- != 2)
		return mday[month];
	return (mday[month] + is_leap(year));
}


アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: difftime関数が0を返す

#2

投稿記事 by みけCAT » 10年前

試しにperrorを追加したコード

コード:

#include <stdio.h>
#include <time.h>
#include <stdlib.h> /* srand()とrand()を使うので、あった方がいい */

int is_leap(int year);
int monthdays(int year, int month);

int main(void)
{
	time_t crtt;
	struct tm *crtlt;
	struct tm anslt = {0};
	time_t anst;
	struct tm inlt = {0};
	int diftm = 1;

	srand(time(&crtt));
	crtlt = localtime(&crtt);
	perror("1-localtime"); /* 追加 */
	anst = mktime(&anslt);
	perror("2-mktime"); /* 追加 */

	anslt.tm_year = 1 + rand() % crtlt->tm_year;
	anslt.tm_mon = rand() %  12;
	anslt.tm_mday = 1 + rand() % monthdays(anslt.tm_year, anslt.tm_mon);

	printf("日付当てゲームを開始します。\n年月日を当ててください。\nなお、答えは1900年から現在の年までです。\n");

	do {
		printf("年:");			scanf("%d", &inlt.tm_year);
		printf("月:");			scanf("%d", &inlt.tm_mon);
		printf("日:");			scanf("%d", &inlt.tm_mday);

		if (inlt.tm_mon < 1 || inlt.tm_mon > 12)
			printf("入力が不正です。再入力してください。\n");
		else if (inlt.tm_year < 1900 || inlt.tm_year > crtlt->tm_year + 1900)
			printf("答えは1900年から現在の年までです。再入力してください。\n");
		else if (inlt.tm_mday < 1 || inlt.tm_mday > monthdays(inlt.tm_year, inlt.tm_mon))
			printf("入力が不正です。再入力してください。\n");
		else {
			time_t intm;

			inlt.tm_year -= 1900;
			inlt.tm_mon--;

			printf("%d\n", diftm); /*デバッグ用*/

			intm = mktime(&inlt);
			perror("3-mktime"); /* 追加 */
			diftm = (int)difftime(anst, intm);
			perror("4-diftm"); /* 追加 */
			printf("%d\n", diftm); /*デバッグ用*/

			if (diftm > 0)
				printf("それよりも後です。\n");
			else if (diftm < 0)
				printf("それよりも前です。\n");
		}
		printf("%d\n", diftm); /*デバッグ用*/
	} while (diftm);

	printf("正解です。\n");

	return 0;
}

int is_leap(int year)
{
	/* 警告されたのでカッコを追加 */
	/* return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; */
	return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}

int monthdays(int year, int month)
{
	int mday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

	if (month-- != 2)
		return mday[month];
	return (mday[month] + is_leap(year));
}
を実行すると、

コード:

YUKI.N>gcc -g3 -Wall -Wextra -o difftime_returns_0_mod1.exe difftime_returns_0_m
od1.c

YUKI.N>difftime_returns_0_mod1.exe
1-localtime: No error
2-mktime: Invalid argument
日付当てゲームを開始します。
年月日を当ててください。
なお、答えは1900年から現在の年までです。
年:1999
月:1
日:1
1
3-mktime: Invalid argument
4-diftm: Invalid argument
0
0
正解です。

YUKI.N>
と表示されました。したがって、45行目のmktimeがうまくいっていないことがわかります。
原因は調査中です。

【訂正】うまくいっていないのは18行目のmktimeの方でした。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: difftime関数が0を返す

#3

投稿記事 by みけCAT » 10年前

18行目のmktimeで、20~22行目でansltにデータを設定する前にansltを使って時刻データを生成しようとしているのが問題だと思います。
さらに、少なくとも自分の環境(GCC 4.8.1)では、mktimeでだいたい1971年より前の日付は作れないようです。
このことは以下のコードで確認しました。

コード:

#include <stdio.h>
#include <time.h>
#include <errno.h>

int main(void) {
	struct tm t={0};
	time_t mkedtime;
	int i;
	t.tm_mon = 0;
	t.tm_mday = 1;
	for(i=0;i<100;i++) {
		t.tm_year = i;
		errno = 0;
		mkedtime = mktime(&t);
		printf("%d/%d/%d -> %.0f\n",t.tm_year,t.tm_mon,t.tm_mday,(double)mkedtime);
		perror("mktime");
	}
	return 0;
}
出力の一部

コード:

67/0/1 -> -1
mktime: Invalid argument
68/0/1 -> -1
mktime: Invalid argument
69/0/1 -> -1
mktime: Invalid argument
70/0/1 -> -1
mktime: Invalid argument
71/0/1 -> 31503600
mktime: No error
72/0/1 -> 63039600
mktime: No error
73/0/1 -> 94662000
mktime: No error
74/0/1 -> 126198000
mktime: No error
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

担麺々

Re: difftime関数が0を返す

#4

投稿記事 by 担麺々 » 10年前

>みけCAT さん
ありがとうございます。変数ansltにデータを設定した後にmetime関数で時刻データを生成するよう修正しました。また、私もGCC 4.8.1を使用しているので、答えが1971年以降になるよう修正しました。

コード:

/* main関数以外は同じ*/

int main(void)
{
	time_t crtt;
	struct tm *crtlt;
	struct tm anslt = {0};
	time_t anst;
	struct tm inlt = {0};
	int diftm = 1;

	srand(time(&crtt));
	crtlt = localtime(&crtt);

	anslt.tm_year = 71 + rand() % (crtlt->tm_year - 71);
	anslt.tm_mon = rand() %  12;
	anslt.tm_mday = 1 + rand() % monthdays(anslt.tm_year, anslt.tm_mon);
	anst = mktime(&anslt);

	printf("日付当てゲームを開始します。\n年月日を当ててください。\nなお、答えは1971年から現在の年までです。\n");

	do {
		printf("年:");			scanf("%d", &inlt.tm_year);
		printf("月:");			scanf("%d", &inlt.tm_mon);
		printf("日:");			scanf("%d", &inlt.tm_mday);

		if (inlt.tm_mon < 1 || inlt.tm_mon > 12)
			printf("入力が不正です。再入力してください。\n");
		else if (inlt.tm_year < 1971 || inlt.tm_year > crtlt->tm_year + 1900)
			printf("答えは1971年から現在の年までです。再入力してください。\n");
		else if (inlt.tm_mday < 1 || inlt.tm_mday > monthdays(inlt.tm_year, inlt.tm_mon))
			printf("入力が不正です。再入力してください。\n");
		else {
			time_t intm;

			inlt.tm_year -= 1900;
			inlt.tm_mon--;

			intm = mktime(&inlt);
			diftm = (int)difftime(anst, intm);
			/* ---デバッグ用--- */
			printf("%d\n%d\n%d\n\n", diftm, (int)anst, (int)intm);
			printf("%d\n%d\n%d\n", anslt.tm_year, anslt.tm_mon, anslt.tm_mday);
			printf("%d\n%d\n%d\n\n", inlt.tm_year, inlt.tm_mon, inlt.tm_mday);
			/* --------------*/

			if (diftm > 0)
				printf("それよりも後です。\n");
			else if (diftm < 0)
				printf("それよりも前です。\n");
		}
	} while (diftm);

	printf("正解です。\n");

	return 0;
}
それが上記のコードなのですが、実行すると、どのような年・月・日を入力しても、diftm == anst となります。
変数intmの値が0だとそのようになりますが、実行結果から、変数intmの値やtm構造体のメンバの値は正常であるように思いました。
どの点が間違っているのでしょうか。度々すみません。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: difftime関数が0を返す

#5

投稿記事 by みけCAT » 10年前

gccの不都合のようです。
無理にdifftimeを用いずに、ansltとinltの各要素、またはanstとintmを直接比較してみてはいかがでしょうか?
c - difftime returns incorrect value in MinGW - Stack Overflow
Man page of DIFFTIME
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

担麺々

Re: difftime関数が0を返す

#6

投稿記事 by 担麺々 » 10年前

>みけCAT さん
ありがとうございます。ご助言いただいたように、difftime関数を用いずに、変数anstとintmを直接比較する方法をとったところ、正常に動作しました。ありがとうございました。

コード:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int is_leap(int year);
int monthdays(int year, int month);

int main(void)
{
	time_t crtt;
	struct tm *crtlt;
	struct tm anslt = {0};
	time_t anst;
	struct tm inlt = {0};
	time_t intm;

	srand(time(&crtt));
	crtlt = localtime(&crtt);

	anslt.tm_year = 71 + rand() % (crtlt->tm_year - 71);
	anslt.tm_mon = rand() %  12;
	anslt.tm_mday = 1 + rand() % monthdays(anslt.tm_year, anslt.tm_mon);
	anst = mktime(&anslt);

	printf("日付当てゲームを開始します。\n年月日を当ててください。\nなお、答えは1971年から現在の年までです。\n");

	do {
		printf("年:");			scanf("%d", &inlt.tm_year);
		printf("月:");			scanf("%d", &inlt.tm_mon);
		printf("日:");			scanf("%d", &inlt.tm_mday);

		if (inlt.tm_mon < 1 || inlt.tm_mon > 12)
			printf("入力が不正です。再入力してください。\n");
		else if (inlt.tm_year < 1971 || inlt.tm_year > crtlt->tm_year + 1900)
			printf("答えは1971年から現在の年までです。再入力してください。\n");
		else if (inlt.tm_mday < 1 || inlt.tm_mday > monthdays(inlt.tm_year, inlt.tm_mon))
			printf("入力が不正です。再入力してください。\n");
		else {
			inlt.tm_year -= 1900;
			inlt.tm_mon--;

			intm = mktime(&inlt);

			if (anst > intm)
				printf("それよりも後です。\n");
			else if (anst < intm)
				printf("それよりも前です。\n");
		}
	} while (anst != intm);

	printf("正解です。\n");

	return 0;
}

int is_leap(int year)
{
	return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}

int monthdays(int year, int month)
{
	int mday[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

	if (month-- != 2)
		return mday[month];
	return (mday[month] + is_leap(year));
}


閉鎖

“C言語何でも質問掲示板” へ戻る