電子書籍の厳選無料作品が豊富!

VisualStudioを使ってC言語でコンソールアプリケーションのカレンダーを作っています。

やりたいことは以下の通りです。

プログラムを起動すると、現在の月を中央に三ヶ月分のカレンダーを表示。
Lキーでカレンダー全体を一ヶ月前に変更
Rキーで一ヶ月後に変更
Dキーで三ヶ月前に変更
Uキーで三ヶ月後に変更
Aキーで一年前に変更
Bキーで一年後に変更

起動時にカレンダー表示するのはできているんですが、Rでカレンダーを進めていくと12月の次が13月、次が0月、その次が1月となってしまい、日付も上手いこと表示されません。
L,D,U,でも同じようになります。
また、A,Bで年を変えると日付が正確に表示されないです。
これらを正しく表示させる方法をご教授いただけますでしょうか。



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

#define KEY_ESC(0x1b)// エスケープ
#define KEY_L(0x6c)// L
#define KEY_D(0x64)// D
#define KEY_R(0x72)// R
#define KEY_U(0x75)// U
#define KEY_A(0x61)// A
#define KEY_B(0x62)// B

time_t timer;// 現在の時刻
struct tm local;// 地方時
int curr_year;// 現在の年
intcurr_mon;// 現在の月
int input;// どのキーが入力されたか

int calendar[12][6][7];// 月、最大の週、1週間
int year;// 年
int month;// 月
int day;// 日
int nweek;// その月が何周あるか
int nday;// その月が何日まであるか
int day_of_week;// その月が何曜日から始まるか

void CalendarDisplay(void);
void CalendarSet(void);
void DayOfWeek(int, int, int);
void NumOfDay(int);
void KeyOperat(int);
int LaepYear(int, int);
int Zeller(int, int, int);

int main(void)
{
CalendarDisplay();
while (1)
{
printf("ESCキーで終了\n\n");
input = _getch();

if(input == KEY_ESC) {
break;
}
else if(input == KEY_L || input == KEY_D ||
input == KEY_R || input == KEY_U ||
input == KEY_A || input == KEY_B) {
KeyOperat(input);
}
}
return 0;
}

// カレンダーの表示
void CalendarDisplay(void)
{
// 現在時刻を取得
timer = time(NULL);
// 地方時に変換
localtime_s(&local, &timer);
// 現在の年
curr_year = local.tm_year + 1900;
// 現在の月
curr_mon = local.tm_mon + 1;

CalendarSet();
}

// 当月とその±1の月を設定
void CalendarSet(void)
{
for (month = curr_mon-1; month <= curr_mon+1; month++)
{
// 12月を超えたら次の年に
if(curr_mon > 12) {
curr_mon = 1;
month = 1;
curr_year += 1;
}
// 1月を超えたら前の年に
else if(curr_mon < 1) {
curr_mon = 12;
month = 12;
curr_year -= 1;
}
// その月が何日まであるか求めて代入
nday = LaepYear(curr_year, month);
// その月が何曜日から始まるか
day_of_week = Zeller(curr_year, month, 1);
nweek = 0;

for (day = 1; day <= nday; day++)
{
calendar[month-1][nweek][day_of_week] = day;
//printf("day-> %d ", day);
if (++day_of_week == 7) {
day_of_week = 0;
nweek++;
}
}
}
DayOfWeek(month, curr_mon, curr_year);
}

// 月と曜日の表示
void DayOfWeek(int m, int cur_m, int cur_y)
{
for (m = cur_m-1; m <= cur_m-1; m++)
{
printf("%d%6d月%16d%6d月%16d%6d月\n" "日 月 火 水 木 金 土  "
"日 月 火 水 木 金 土  " "日 月 火 水 木 金 土\n",
cur_y, m, cur_y, m + 1, cur_y, m + 2);
NumOfDay(&calendar[m-1]);
}
}

// 日数の表示
void NumOfDay(int calendar[][6][7])
{
int max_w = 0, nmon = 0, w = 0;

// その月の最大週の間
for (max_w = 0; max_w < 6; max_w++)
{
// 3ヶ月分表示
for (nmon = 0; nmon < 3; nmon++)
{
for (w = 0; w < 7; w++)
{
if (calendar[nmon][max_w][w]) {
printf("%2d ", calendar[nmon][max_w][w]);
}
else {
printf(" ");
}
}
printf(" ");
}
printf("\n");
}
}

// キー操作でカレンダーの表示を変える
void KeyOperat(int input)
{
do {
switch(input)
{
case KEY_L:
curr_mon -= 1;
break;
case KEY_R:
curr_mon += 1;
break;
case KEY_D:
curr_mon -= 3;
break;
case KEY_U:
curr_mon += 3;
break;
case KEY_A:
curr_year -= 1;
break;
case KEY_B:
curr_year += 1;
break;
default :
input = !input;
break;
}
} while(!input);

CalendarSet();
}

// うるう年の判定
int LaepYear(int y, int m)
{
const int num_day[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (m == 2 && y % 4 == 0 &&
(y % 100 != 0 || y % 400 == 0)) {
return 29;
}
return num_day[m - 1];
}

// ツェラーの公式から曜日を求める
int Zeller(int y, int m, int d)
{
int dow;
  // 1月と2月の場合、それぞれ前年の13月、14月とする
if(m == 1 || m == 2) {
y--;
m = m + 12;
}
dow = (y + y/4 - y/100 + y/400 + (13*m+8)/5 + d) % 7;

return dow;
}

A 回答 (3件)

とりあえず、CalenderSet()の月判定が間違っていますね。



if( curr_mon > 12 ) は、if( month > 12 ) ではないですか?
1月未満か、の判定も同じく。

また、ループの終了条件はループする毎に評価されますから、このfor( month = curr_mon-1; month <= curr_mon+1; month++ )の終了条件を作る材料である、curr_monをループ内で変更してしまうと、終了条件も変わりますよ?

そして、ループカウンタでもあるmonth=1や=12とやってしまうと、今度は1や12からカウントを始めますので、意図しない月数表示されたり、前後分合わせて3ヶ月のうち1~2月分しか表示しない、等の動作を行う気もします。

for()ループのカウンタ(ここではmonth)と終了条件に参照する変数(ここではcurr_mon)は、変更しないで、テンポラリを作成したほうが良いと思います。

void CalenderSet( void )
{
int dispmon; //monthの代わり
int dispyear;

for( month = curr_mon-1; month <= curr_mon+1; month++ ){
dispmon = month; //表示したい月をdispmonにセット
dispyear = curr_year;

if( dispmon > 12 ){
dispmon -= 12; //今回は+-1月なので、dispmon = 1でもOK。2月以上前後に対応したかったら12を引く
dispyear = curr_year + 1;
}
if( dispmon < 1 ){
dispmon += 12; //上と同じ
dispyear = curr_year - 1;
}

// その月が何日まであるか求めて代入
nday = LaepYear( dispyear, dispmon ); //変更前→curr_year, month);
// その月が何曜日から始まるか
day_of_week = Zeller(dispyear, dispmon, 1 ); //変更前→ curr_year, month, 1);
nweek = 0;

以降省略。必要に応じて、変更してください。

変な動作をする原因は、このCalenderSet()で、中央の年月を記憶する変数(curr_year, curr_mon)を変更してしまっているということではないでしょうか?
curr_monやcurr_yearは、中央に表示したい年月であり、キー操作によって”のみ”移動させたいのでしょうから、キー操作以外で変更してはいけません。

表示するために前後の年月が必要なのであれば、それを記憶するためのローカル変数(dispyear, dispmon)を作って、それにコピーした後で、前後の年月にするための変更をローカル変数に行うべきです。

これ以外の部分は見てないのですが、とりあえずここを変更してみて、デバックを進めてみてはどうでしょう?
    • good
    • 0
この回答へのお礼

遅くなって申し訳ありません。
丁寧で分かりやすいご回答ありがとうございました。
テンポラリを作成してソースを改造してみたところ、思い通りの動きにすることができました!
普通に考えれば分かることでしたが、自分だけで考えてた時はテンポラリを作成するという事を全く思いつかなかったので非常に助かりました。

お礼日時:2012/06/01 15:44

グローバル変数使わずに1月分のカレンダー表示から始めた方がいいと思う。

    • good
    • 0
この回答へのお礼

現在の月から始めたかったんですけど、1月分からの方が考え方は簡単ですよね。
それと、たまに日数がきちんと表示されないバグがグローバル変数をやめることで直りました!
ありがとうございました。

お礼日時:2012/06/01 15:48

症状を正確に書いてほしいんだけどなぁ....



ひょっとして表示される 3か月の中央が 12月だと「その次の月」が「13月」になってたりしませんか?

変数の使い分けが変だと思う.

この回答への補足

すみません、字数制限の関係で詳しく書けませんでした。
具体的には一ヶ月ずつ進めていくと、おっしゃる通り以下のような感じになります。

2012  11月        2012  12月         2012  13月
日 月 火 水 木 金 土  日 月 火 水 木 金 土    月 火 水 木 金 土
          1 2 3                1
 4 5 6 7 8 9 10   2 3  4 5 6  7 8
11 12 13 14 15 16 17  9 10 11 12 13 14 15
18 19 20 21 22 23 24  16 17 18 19 20 21 22
25 26 27 28 29 30    23 24 25 26 27 28 29
                30 31
ESCキーで終了

2013 0月 2013 1月 2013 2月
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土
114 4 57  13 24 4       1 2 3 4 5             1 2
112 4 144        6 7 8 9 10 11 12  3  4 5 6  7 8  9
               13 14 15 16 17 18 19   10 11 12 13 14 15 16
               20 21 22 23 24 25 26   17 18 19 20 21 22 23
               27 28 29 30 31       24 25 26 27 28

ESCキーで終了
-----------------------------------------------------------

一年後にすると
2010   4月       2010   5月       2010   6月
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土
 1 2 3 4 1 2  3    1 2  3 4 5 6 1        1  2 3 4 5
4 5 6 7 8 9 10    2 3  4 5 6 7 8    6 7 8  9 10 11 12
11 12 13 14 15 16 17  9 10 11 12 13 14 15  13 14 15 16 17 18 19
18 19 20 21 22 23 24  16 17 18 19 20 21 22  20 21 22 23 24 25 26
25 26 27 28 29 30 30  23 24 25 26 27 28 29  27 28 29 30 30 29 30
                30 31
ESCキーで終了

と、表示されてしまいます。

>変数の使い分けが変だと思う.
具体的にどこの変数の使い方がおかしいのでしょうか?
ご教授くだされば嬉しいです。

補足日時:2012/05/24 14:54
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!


おすすめ情報