C++ Builder XE4, 10.2 Tokyo > 実装ミス > タイマー処理関連 > 指定時刻(hh, mm)に処理をする > 処理時刻のずれがある > 修正版v0.2

```txt:動作環境
C++ Builder XE4
RAD Studio 10.2 Tokyo Update 2
```

[C++ Builder XE4 > TTimer > 指定時刻(hh, mm)に処理をする > TTimer->Intervalの設定 > テストしにくい](https://qiita.com/7of9/items/b473ad1d0e1a24f40d27)

上記のXE4の実装を10.2 Tokyoで試した。

### code

```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>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TEdit *E_hh;
	TEdit *E_mm;
	TButton *Button1;
	TMemo *Memo1;
	TLabel *Label1;
	TTimer *TimerAlarm;
	void __fastcall Button1Click(TObject *Sender);
	void __fastcall TimerAlarmTimer(TObject *Sender);
private:	// ユーザー宣言
	TDateTime __fastcall TForm1::getNextAlarmDateTime(TDateTime now, int hh, int mm);
	void __fastcall TForm1::startAlarmTimer(void);
public:		// ユーザー宣言
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
```

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

#include <vcl.h>
#pragma hdrstop

#include <DateUtils.hpp>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
    TimerAlarm->Enabled = false;
}

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;
}

void __fastcall TForm1::startAlarmTimer(void)
{
    // 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. 次の日時取得
    TDateTime nextdt = getNextAlarmDateTime(Now(), next_hh, next_mm);

    String msg = "now: " + Now().DateTimeString() + L" next: " + nextdt.DateTimeString();
    Memo1->Lines->Add(msg);

    // 3. タイマーセット
    __int64 intvl = SecondsBetween(Now(), nextdt);
    TimerAlarm->Enabled = false;
    TimerAlarm->Interval = intvl * 1000; // msec
    TimerAlarm->Enabled = true;
}

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    startAlarmTimer();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TimerAlarmTimer(TObject *Sender)
{
    TimerAlarm->Enabled = false;

    String msg = L"Expired @ " + Now().DateTimeString();
    Memo1->Lines->Add(msg);

	startAlarmTimer();
}
//---------------------------------------------------------------------------

```

### 実行

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/64532d2e-1646-7e62-916f-9db6ce0f5fb1.png)

- 期待動作
    - 13:19:00にExpiredになる
- 実動作
    - 13:18:59にExpiredになる
    - 結果、次のタイマーが再度13:19:00になる
    - ミリ秒まで確認したところ、ミリ秒は954あたりになっている

~~XE4ではきちんと動作していた処理が、10.2 Tokyoでは失敗する。~~
    
~~10.2 Tokyoへ移植時に似たようなタイマー処理をしている部分は全滅しそう。~~
~~**看過できない不具合だ。**~~

XE4で同じ処理を実行したところ、同じ結果になった。
何かを見落としているのかもしれない。
[c++ builder > TDateTime > SecondsBetween() > 秒未満の切捨てに注意をする](https://qiita.com/7of9/items/b885091b329a2a617538)
[c++ builder XE4, 10.2 Tokyo > TDateTime > SecondsBetween() > msecの違い](https://qiita.com/7of9/items/23b93b09279696baf648)
が関係しているのかもしれない。

### code v0.2

- MilliSecondsBetween()を使って「秒の差 + ミリ秒の差」まで考慮
    - 関連: [C++ Builder XE4, 10.2 Tokyo > DateUtils > MilliSecondsBetween > 秒の違いをミリ秒の単位で返す](https://qiita.com/7of9/items/d697a1dfcb84b95bd69e)

```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>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE で管理されるコンポーネント
	TEdit *E_hh;
	TEdit *E_mm;
	TButton *Button1;
	TMemo *Memo1;
	TLabel *Label1;
	TTimer *TimerAlarm;
	void __fastcall Button1Click(TObject *Sender);
	void __fastcall TimerAlarmTimer(TObject *Sender);
private:	// ユーザー宣言
	TDateTime __fastcall TForm1::getNextAlarmDateTime(TDateTime now, int hh, int mm);
	void __fastcall TForm1::startAlarmTimer(void);
public:		// ユーザー宣言
	__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
```

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

#include <vcl.h>
#pragma hdrstop

#include <DateUtils.hpp>
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
    TimerAlarm->Enabled = false;
}

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;
}

void __fastcall TForm1::startAlarmTimer(void)
{
    // 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. 次の日時取得
    TDateTime nextdt = getNextAlarmDateTime(Now(), next_hh, next_mm);

    String msg = "now: " + Now().DateTimeString() + L" next: " + nextdt.DateTimeString();
    Memo1->Lines->Add(msg);

    // 3. タイマーセット
		//__int64 intvl_sec = SecondsBetween(Now(), nextdt);
	__int64 intvl_msec = MilliSecondsBetween(Now(), nextdt);
	TimerAlarm->Enabled = false;
    TimerAlarm->Interval = intvl_msec; // msec
    TimerAlarm->Enabled = true;
}

//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    startAlarmTimer();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::TimerAlarmTimer(TObject *Sender)
{
    TimerAlarm->Enabled = false;

	String msg = L"Expired @ " + Now().FormatString(L"yyyy/mm/dd hh:nn:ss.zzz");
    Memo1->Lines->Add(msg);

	startAlarmTimer();
}
//---------------------------------------------------------------------------
```

![qiita.png](https://qiita-image-store.s3.amazonaws.com/0/32870/0585052a-ca88-6e93-c6af-69c1687d40ca.png)