時間・時刻を計算する関数 - 情報処理IIレジュメ(2022-9)

仕様

時刻または時間の時・分・秒を整数配列で保持することとします(後期にはこれを構造体でも実装します)。 例えば、 2 時 34 分 56 秒という時刻であれば、以下のように長さ 3 の整数型の要素を持つ配列を用意するものとします。 ここで、HH、MM、SS などを定数で定義しているのは後で日付などが追加されることなどを考慮したためです。 0, 1, 2 のような意味づけした数値(Magic Number) を使うのではなく、定数にすることにより、データ構造が変化した場合にも柔軟に対応することが可能となります。

#define N 3
#define HH 0
#define MM 1
#define SS 2
(中略)
int jikoku[N];
jikoku[HH] = 2;
jikoku[MM] = 34;
jikoku[SS] = 56;

時刻・時間配列

作成する関数

関数のポンチ絵を記述するので、プロトタイプ宣言を記述してください。

  1. h:m:s の 0:00:00 からの秒数を得る

    getSeconds

  2. 0:00:00 からの秒数を時刻から得る

    getSecondsFromTime

  3. 0:00:00 からの秒数から時刻を得る

    makeTimeFromSeconds

  4. 時刻・時間のセット (結果の分・秒は範囲内に設定されていること。ただし、入力の m, s は 0 か ら 59 までの範囲に入っていない場合もあることを考えること。例 62 分→ 1 時間 2 分に変換できるように)

    makeTime

  5. 時刻の前後関係を返す (t1p が t2p より未来なら 1、過去なら −1、等しければ 0)

    timeCmp

  6. 時刻に時間を加算した時刻を得る (結果の分・秒は範囲内に設定されていること)

    addTime

  7. 時間を n 倍した時間を得る (分・秒は範囲内に設定されていること)

    mulTime

  8. 時間を n 等分した時間を得る (端数秒は切り捨て。結果の分・秒は範囲内に設定されていること)

    divTime

  9. 時間(t1p)を時間(t2p)で割り、何等分できるかを得る(端数は切り捨て)

    divTimeByTime

  10. 二つの時刻の間の時間を得る(正の値が必要なので未来から過去を引くこと。結果の分・秒は範囲内に設定されていること)

    subTime

ファイルの準備

時刻・時間の関数を作成する前に、それをテストするためのプログラム(testMyTime.c)を書きます。 まずは,もっとも簡単な時刻から秒数を得るプログラムのテストを作成します。 先週の fact フォルダを用いて関係するファイルを用意してください。以下手順を示します。

  • fact.zip をダウンロードし、展開します。
  • fact という名前を myTime に変更する。
  • このフォルダで Visual Studio Code を開く。
  • ファイル名を以下のように変更する。
    • fact.c → myTime.c
    • fact.h → myTime.h
    • factMain.c → myTimeMain.c
    • testFact.c → testMyTime.c
  • Makefile を開き、上と同じ修正を行う
    • fact.h → myTime.h
    • fact.o → myTime.o
    • factMain → myTimeMain
    • testFact → testMyTime
  • .gitignore に書かれている最後の行を修正します。
    • factMain → myTimeMain
    • testFact → testMyTime
  • ファイルの準備ができたので、chokidar でファイルを監視します。
chokidar "*.c" "*.h" Makefile -c "make test"

テストの作成

testMyTime.c を以下のように記述します。 まず最初に getSeconds のテストを記述します。 ##########の部分は自分で考えてください。

#include <stdio.h>

#include "myTime.h"
#include "testCommon.h"

void testGetSeconds() {
    int ans;
    testStart("getSeconds");
    ans = getSeconds(2, 34, 56);
    assertEqualsInt(ans, ##########);
    ans = getSeconds(11, 59, 59);
    assertEqualsInt(ans, ##########);
}

int main() {
    testGetSeconds();
    testErrorCheck();
    return 0;
}
  • 実装の雛形まで作成し,コンパイルできることを確認します。 以下のことに気をつけてください。
    • 実装は当初中身は適当な return 文のみを書き、コンパイルが通ることだけを確認します
    • テストは複数個記述します。 一つだけでは偶然一致してしまうかもしれないからです。
  • テストに失敗することを確認します
  • 失敗を確認した後で、テストが通過するように実装を記述します
  • 一つの工程が終わったら source をコミットします

テストの追加 (getSecondsFromTime)

  • getSeconds が通ったら、次の関数 (getSecondsFromTime) をテストします。
// testGetSeconds() の下に記述する
void testGetSecondsFromTime() {
    int jikoku1[N] = {2, 34, 56}, jikoku2[N] = {11, 59, 59};
    int ans;
    testStart("getSecondsFromTime");
    ans = getSecondsFromTime(jikoku1);
    assertEqualsInt(ans, ##########);
    ans = getSecondsFromTime(jikoku2);
    assertEqualsInt(ans, ##########);
}

int main() {
    testGetSeconds();
    testGetSecondsFromTime(); // ここを増やす
    testErrorCheck();
    return 0;
}
  • 実装の雛形まで作成し,コンパイルできることを確認します
  • テストに失敗することを確認します
  • テストが通過するように実装を記述します

更なるテストの追加 (makeTimeFromSeconds)

  • getSecondsFromTime が通ったら、次の関数(makeTimeFromSeconds)をテストします。
// testGetSecondsFromTime() の下に記述する
void testMakeTimeFromSeconds() {
    int jikoku[N];
    testStart("makeTimeFromSeconds");
    makeTimeFromSeconds(jikoku, 2 * 3600 + 34 * 60 + 56);
    assertEqualsInt(jikoku[HH], ###);
    assertEqualsInt(jikoku[MM], ###);
    assertEqualsInt(jikoku[SS], ###);
    makeTimeFromSeconds(jikoku, 12 * 3600 - 1);
    assertEqualsInt(jikoku[HH], ###);
    assertEqualsInt(jikoku[MM], ###);
    assertEqualsInt(jikoku[SS], ###);
}

int main() {
    testGetSeconds();
    testGetSecondsFromTime();
    testMakeTimeFromSeconds(); // ここを増やす
    testErrorCheck();
    return 0;
}
  • 実装の雛形まで作成し,コンパイルできることを確認します
  • テストに失敗することを確認します
  • テストが通過するように実装を記述します

以下、学生の進度に合わせて各自テストおよび実装を記述します。


hkob.hatenablog.com