C++ Builder > TTimer > 指定時刻(hh, mm)に処理をする (毎日処理) > TTimer(1秒)+期限更新 | Dependency Injectionを使ったテスト

```txt:動作環境
C++ Builder XE4
```

前回: https://qiita.com/7of9/items/b473ad1d0e1a24f40d27

### 処理概要

- 指定時刻(hh, mm)に処理を行う
- 毎日処理をする

- v0.1
    - 実装したが、1日一回の処理を待つわけにはいかない
- v0.2
    - Dependency Injectionを使って「現在日時」の(時、分、秒）を変更するボタン3つを追加
        - => ボタンをN回押して調整するのが面倒
- v0.3
    - TDateTimePickerを使った
    - 「現在日時」をTDateTimePickerのDate,Timeで置き換え

### コンポーネント概要

- Timer1sec : 1秒ごとのタイマー
    - 「現在日時」の表示更新
    - 「次の処理日時」に達したか確認する
- m_nextDateTime: 次の処理日時
- m_nowDateTime: 現在日時
    - getNow()で使われる
- TimerAddSecond: 1秒ごとのタイマー
    - m_nowDateTimeを毎秒更新する


### code v0.1..v0.3

```Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include <Vcl.ExtCtrls.hpp>
#include <Vcl.ComCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TTimer *Timer1sec;
	TEdit *E_hh;
	TEdit *E_mm;
	TLabel *Label1;
	TButton *B_start;
	TMemo *Memo1;
	TTimer *TimerAddSecond;
	TButton *dbgB_add5Sec;
	TLabel *L_nowDateTime;
	TButton *dbgB_addHour;
	TButton *dbgB_add5Min;
	TDateTimePicker *DTP_time;
	TDateTimePicker *DTP_date;
	TButton *dbgB_setDateTime;
	void __fastcall Timer1secTimer(TObject *Sender);
	void __fastcall B_startClick(TObject *Sender);
	void __fastcall TimerAddSecondTimer(TObject *Sender);
	void __fastcall dbgB_add5SecClick(TObject *Sender);
	void __fastcall dbgB_addHourClick(TObject *Sender);
	void __fastcall dbgB_add5MinClick(TObject *Sender);
	void __fastcall dbgB_setDateTimeClick(TObject *Sender);
private:	// ユーザー宣言
	TDateTime m_nowDateTime; // 現在時刻 (Dependency Injection用)
	TDateTime m_nextDateTime; // 次の処理時間
	TDateTime __fastcall getNextAlarmDateTime(TDateTime now, int hh, int mm);
	TDateTime __fastcall getNow(void);
public:		// ユーザー宣言
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
```

```Unit1.dfm
object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 300
  ClientWidth = 635
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 8
    Top = 35
    Width = 179
    Height = 13
    Caption = '                               h                        m'
  end
  object L_nowDateTime: TLabel
    Left = 8
    Top = 8
    Width = 92
    Height = 16
    Caption = 'L_nowDateTime'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -13
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
  end
  object E_hh: TEdit
    Left = 40
    Top = 32
    Width = 57
    Height = 21
    TabOrder = 0
    Text = '10'
  end
  object E_mm: TEdit
    Left = 120
    Top = 32
    Width = 57
    Height = 21
    TabOrder = 1
    Text = '30'
  end
  object B_start: TButton
    Left = 216
    Top = 30
    Width = 75
    Height = 25
    Caption = 'B_start'
    TabOrder = 2
    OnClick = B_startClick
  end
  object Memo1: TMemo
    Left = 8
    Top = 104
    Width = 617
    Height = 185
    Lines.Strings = (
      'Memo1')
    ScrollBars = ssVertical
    TabOrder = 3
  end
  object dbgB_add5Sec: TButton
    Left = 320
    Top = 8
    Width = 75
    Height = 25
    Caption = 'dbgB_add5Sec'
    TabOrder = 4
    OnClick = dbgB_add5SecClick
  end
  object dbgB_addHour: TButton
    Left = 320
    Top = 73
    Width = 75
    Height = 25
    Caption = 'dbgB_addHour'
    TabOrder = 5
    OnClick = dbgB_addHourClick
  end
  object dbgB_add5Min: TButton
    Left = 320
    Top = 42
    Width = 75
    Height = 25
    Caption = 'dbgB_add5Min'
    TabOrder = 6
    OnClick = dbgB_add5MinClick
  end
  object DTP_time: TDateTimePicker
    Left = 440
    Top = 35
    Width = 81
    Height = 21
    Date = 43026.485760914350000000
    Time = 43026.485760914350000000
    Kind = dtkTime
    TabOrder = 7
  end
  object DTP_date: TDateTimePicker
    Left = 440
    Top = 8
    Width = 97
    Height = 21
    Date = 43026.488268506950000000
    Time = 43026.488268506950000000
    TabOrder = 8
  end
  object dbgB_setDateTime: TButton
    Left = 440
    Top = 62
    Width = 75
    Height = 25
    Caption = 'dbgB_setDateTime'
    TabOrder = 9
    OnClick = dbgB_setDateTimeClick
  end
  object Timer1sec: TTimer
    OnTimer = Timer1secTimer
    Left = 88
    Top = 64
  end
  object TimerAddSecond: TTimer
    OnTimer = TimerAddSecondTimer
    Left = 136
    Top = 64
  end
end

```

```Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <DateUtils.hpp>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------

/*
v0.3 Oct. 18, 2017 Improve debug feature
	- add [dbgB_setDateTime]
	- add [DTP_time]
	- add [DTP_date]
v0.2 Oct. 18, 2017 Dependency Injection for Now()
	- add debug
		+ add [dbgB_addHour]
		+ add [dbgB_add5Min]
		+ add [dbgB_add5Sec]
	- add getNow()
	- add [m_nowDateTime]
v0.1 Oct. 18, 2017
	- add [kInit_dateTime]
	- add getNextAlarmDateTime()
	- add [m_nextDateTime]
*/

static const TDateTime kInit_dateTime = (TDateTime)0;

__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
	m_nextDateTime = kInit_dateTime; // @ctor
	m_nowDateTime = Now(); // @ ctor
}
//---------------------------------------------------------------------------

TDateTime __fastcall TForm1::getNextAlarmDateTime(TDateTime now, int hh, int mm)
{
	TDateTime res = RecodeHour(now, hh);
	res = RecodeMinute(res, mm);
	res = RecodeSecond(res, 0);
	if (res <= now) {
		res = IncDay(res, 1);
    }
	return res;
}

TDateTime __fastcall TForm1::getNow(void)
{
	return m_nowDateTime;
	//return Now();
}

void __fastcall TForm1::Timer1secTimer(TObject *Sender)
{
	L_nowDateTime->Caption = getNow().FormatString(L"yyyy/mm/dd hh:nn:ss");

	if (m_nextDateTime == kInit_dateTime) {
		return;
	}
	if (getNow() > m_nextDateTime) {
		m_nextDateTime = IncDay(m_nextDateTime, 1);

		String msg = L"Expired @ " + getNow().DateTimeString();
		Memo1->Lines->Add(msg);
	}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::B_startClick(TObject *Sender)
{
	// TODO: 0m > 関数化

	// 1. フォームからの読取り
	int next_hh, next_mm;
	try {
		next_hh = StrToIntDef(E_hh->Text, -1);
		next_mm = StrToIntDef(E_mm->Text, -1);
	} catch (Exception &exc) {
		ShowMessage(exc.Message);
		return;
	}
	if (next_hh < 0 || next_mm < 0) {
		Memo1->Lines->Add(L"Error: set numerical values for [hh] and [mm]");
		return; // error
	}

	// 2. 次の日時取得
	m_nextDateTime = getNextAlarmDateTime(Now(), next_hh, next_mm);

	String msg = "now: " + getNow().DateTimeString() + L" next: " + m_nextDateTime.DateTimeString();
	Memo1->Lines->Add(msg);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TimerAddSecondTimer(TObject *Sender)
{
	m_nowDateTime = IncSecond(m_nowDateTime, 1);
}
//---------------------------------------------------------------------------
// デバッグ用
void __fastcall TForm1::dbgB_add5SecClick(TObject *Sender)
{
	m_nowDateTime = IncSecond(m_nowDateTime, 5);
}
void __fastcall TForm1::dbgB_add5MinClick(TObject *Sender)
{
	m_nowDateTime = IncMinute(m_nowDateTime, 5);
}
void __fastcall TForm1::dbgB_addHourClick(TObject *Sender)
{
	m_nowDateTime = IncHour(m_nowDateTime, 1);
}
void __fastcall TForm1::dbgB_setDateTimeClick(TObject *Sender)
{
	m_nowDateTime = DateOf(DTP_date->Date) + TimeOf(DTP_time->Time);
}
//---------------------------------------------------------------------------
```

### 結果

Expired が発生した後にTDateTimePickerの日時を次の発生日時の前に変更してdbgB_setDateTimeを押す、ということを繰り返した。
以下となり、実装としては正しそう。

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/2313132a-72a5-e44e-5609-87a5570223de.png)

### 追記
(2017/10/23)

StrToIntDef()にてtry, catchをしているが、try, catchは不要のように思う。