C++ Builder XE4 > TTimer > 100ミリ秒程度の誤差で1秒インターバルを行う (蓄積しない誤差)

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

### 蓄積する誤差

- TTimerを1,000ミリ秒で動かす
    - 1,014ミリ秒程度のインターバルになる
        - 実測

14ミリ秒の誤差は86400秒/日で1秒/回コールすると
86400 * 14 / 1000 = 1209.6秒のずれになる。

蓄積しないための実装を検討した。

### 実装案

- 別のタイマー（アラームタイマー）を用意
- TTimerは短いインターバル(例:100ミリ秒)で実施する
- TTimer内でアラームタイマーの時刻と現在時刻を比較する

### 実装例

```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 で管理されるコンポーネント
	TTimer *Timer1;
	void __fastcall Timer1Timer(TObject *Sender);
	void __fastcall FormShow(TObject *Sender);
private:	// ユーザー宣言
	TDateTime m_nextAlarm;  // 次のアラーム時刻
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)
{
	Timer1->Enabled = false;
	Timer1->Interval = 100;  // 55ミリ秒以下にはしない (発火しない)
    Timer1->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
	if (Now() < m_nextAlarm) {
		return;
	}

	m_nextAlarm = IncSecond(m_nextAlarm, 1);
	OutputDebugString(Now().FormatString(L"yyyy/mm/dd hh:nn:ss.zzz").c_str());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormShow(TObject *Sender)
{
	m_nextAlarm = Now();
}
//---------------------------------------------------------------------------

```

### 動作例

```
デバッグ出力: 2018/12/06 12:05:24.772 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:05:25.758 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:05:26.735 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:05:27.721 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:05:28.704 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:05:29.687 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:05:30.779 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:05:31.763 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:05:32.743 プロセス Project1.exe (1348)
(中略)
デバッグ出力: 2018/12/06 12:08:35.728 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:08:36.709 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:08:37.689 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:08:38.785 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:08:39.768 プロセス Project1.exe (1348)
デバッグ出力: 2018/12/06 12:08:40.749 プロセス Project1.exe (1348)
```

100ミリ秒程度の誤差であるが、その誤差は蓄積されない。

### 関連

- [C++ Builder XE4 > TTimer > Link: TTimer not firing at correct interval > 55ミリ秒以下のインターバルは発火しない (1998年の情報 + 2018年の情報)](https://qiita.com/7of9/items/5f1f9e0c27461f376169)

- [c++ builder XE4, 10.2 Tokyo > TDateTime > ミリ秒 (msec) まで表示 > Now().FormatString(L"yyyy/mm/dd hh:nn:ss.zzz")](https://qiita.com/7of9/items/a59f79b3c38ef3e178ed)