Thinkers'Studio
JavaとC言語の自習ツール
構造体を関数の戻り値にする(指定日のn日後を調べる)

 今回は、指定日の n日後の日付を調べる関数を作ります。
 年月日は構造体として定義し、指定日を渡したり結果を戻すのに、構造体関数とやり取りします。 n に負の値を指定すると、過去の日付を調べることもできます。
  (例) 2020年東京オリンピック開会式(2020/07/24) の 1000日前は?
     2013年の立春(2013/02/04)の 209日後は?  二百十日は、台風襲来の特異日だそうです
     2014年の立春(2014/02/04)の 87日後は?  八十八夜は、新茶の摘み時です
  下で、作成したプログラムを使って調べることにしましょう。

 引数に渡す元の日付は書き換えないものとし、結果は戻り値で返します。
 【 用意する関数 】 dateYMD_t afterNday( dateYMD_t t, int n )
  機 能 : tに指定された日のn日後の日付を求め、dateYMD_t型の戻り値で返す
          nに負の値を指定すると、過去の日付が戻される

関数の処理手順 】
 ymd の d に n を加算してしまい、d が当月になるまで、m を進めてその分の日数を引きます。
  たとえば 2013/10/10 の 90日後なら、まず d に 90 を足す --> (2013/10/100)
   ・ ひと月進める --> d から10月の 31日を引き m は 11月へ(2013/11/69)
   ・ 11から12月へも同様 --> (2013/12/39)
   ・ さらに翌年の 1月へ --> (2014/01/09)   ・・・ d が当月の日数内になったので、処理は終わり
 N < 0 の場合は、過去の日付を求めるので上記と逆です。m を戻し日数は足し算していく処理になります。たとえば n が -90 なら 2013/10/-80 から始め、d が 1以上の有効な日付になるまで繰り返します。
 なお、y がうるう年かどうかで 2月の日数が異なるため、その判定も必要です。

【 指定日の n日後の日付を調べるプログラム例 】
 ここでは指定される日付は正しいものとします。
#include <stdio.h>
typedef struct {
    int y;
    int m;
    int d;
} dateYMD_t;

// --- yがうるう年か調べる
int isLeapYear( int y )
{
    if( y % 4 == 0 && y % 100 != 0 || y % 400 == 0 ) return 1;
    else return 0;
}
// --- 指定日tのn日後の日付を返す
dateYMD_t afterNday( dateYMD_t t, int n )
{
    int days[][12] =
    { { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
    , { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } };
    int isleap;

    if( n == 0 ) return t;
    isleap = isLeapYear( t.y ); //うるう年判定
    t.d += n;
    if( n > 0 ) { //将来日付 dが当月内になるまで日数を引いてはmを進める
        while( t.d > days[isleap][t.m-1] ) {
            t.d -= days[isleap][t.m-1];
            if( ++t.m > 12 ) {
                isleap = isLeapYear( ++t.y );
                t.m = 1;
            }
        }
    } else { //過去日付 dが正の日付になるまで日数を足してはmを戻す
        while( t.d <= 0 ) {
            t.d += days[isleap][t.m-1];
            if( --t.m < 1 ) {
                isleap = isLeapYear( --t.y );
                t.m = 12;
            }
        }
    }
    return t;
}
// ------------------------------------------------------
main()
{
    dateYMD_t dlst[] = { {2020,7,24}, {2013,2,4}, {2014,2,4} };
    dateYMD_t nd;
    int i, nlst[] = { -1000, 209, 87 };

    for( i = 0; i < 3; i++ ) {
        nd = afterNday( dlst[i], nlst[i] );
        printf( "%4d/%02d/%02d の %5d日後 ・・・ %4d/%02d/%02d\n"
        , dlst[i].y, dlst[i].m, dlst[i].d, nlst[i], nd.y, nd.m, nd.d );
    }
}

 afterNday関数内では新たな構造体変数を用意せず、仮引数を書き換えたものを結果として returnします。値渡しの引数のコピーを書き換えても実引数には変更が反映されません。
 呼び出し側では、戻り値を dateYMD_t型の変数に代入してから使います。構造体は = により一括で代入ができます。

(実行結果は!)
2020/07/24 の -1000日後 ・・・ 2017/10/28    2020東京五輪開会式の1000日前
2013/02/04 の   209日後 ・・・ 2013/09/01    2013年の二百十日
2014/02/04 の    87日後 ・・・ 2014/05/02    2014年の八十八夜

(今回のクイズは自習です)
 別の Tips で「有効な日付かをチェックする関数」「曜日を調べる関数」 を紹介しました。これらと組み合わせて、プログラムの機能を充実させてみましょう。 また、日付関連の機能を整理してひとつのファイルにまとめるのも勉強になりそうです。

[ ご案内 ] 構造体の要点を学習できるコース:要点講座 [構造体編]

前回の答え:(前回の問題を見る )  たとえば、次のようにします。
 #define DEBUG 
   :
 #ifdef DEBUG                DEBUGが定義されているときだけ 
     PR_DBL2( hm, w );       #ifdef DEBUG と #endif 間の内容が有効です。
 #endif