2021年1月31日日曜日

15: C++において最適に日時を取り扱う方法

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

どのコンテナを使うべきか?いまだに'time_t'および'tm'か?それとも'::std::chrono::time_point'か?各データタイプの特徴を知るべきだ。

話題


About: C++

この記事の目次


開始コンテキスト


  • 読者は、C++の基本的知識を持っている。

ターゲットコンテキスト



  • 読者は、各日時データタイプの特徴を知る、状況毎にC++において日時を最適に取り扱う方法を判断できるように。

オリエンテーション


ここでは、C++標準17が使われる。

ここでは、外部ライブラリは使われない。


本体


1: 'time_t'および'tm'のペア



1-1: 'time_t'および'tm'の特徴


Hypothesizer 7
'time_t'および'tm'は、C由来の昔ながらのデータタイプだ。

C++標準では、'time_t'は、あまり具体的に規定されておらず、ただ、ある紀元からの秒数とされている。

紀元はいつなのか?. . .それは、コンパイラー依存である、多分、'1970-01-01T00:00:00 UTC'であるが。

えーと、紀元が規定されていない状態で、ある'time_t'インスタンスがどの日時を表わしているかをどのように知ることができるのだろうか?. . .その'time_t'インスタンスから、'localtime'のようなファンクション(それは密かに紀元を知っている)を介して、'tm'(それは、日時をいくつかのコンポーネント(年、月のような)に分けて持つstructである)インスタンスを取得しなければならない、あえてコンパイラ依存にするのでなければ。. . .したがって、'tm'無しで済ますというわけにはいかない、'time_t'インスタンス間の経過時間のみに関心があるというのでなければ。

他方で、'tm'だけで済ませるわけにはいかないのか、'time_t'無しで?. . .えーと、1つの問題は、'tm'は、そのデータに計算を行なうのに必ずしも好都合でないことだ: 例えば、そのデータに'42'日を追加する。それに、'time_t'のほうが、データサイズにおいて、より効率的だ。

'time_t'および'tm'のデータサイズをチェックしてみよう、以下のように。

@C++ ソースコード
				cout << "###### checking the sizes of 'time_t' and 'tm' Start" << endl << flush;
				{
					time_t l_dateAndTimeInTime_t = 0;
					tm l_dateAndTimeInTm;
					cout << "######### the sizes of 'time_t' and 'tm': " << sizeof (l_dateAndTimeInTime_t) << ", " << sizeof (l_dateAndTimeInTm) << endl << flush;
				}
				cout << "###### checking the sizes of 'time_t' and 'tm' End" << endl << flush;

Linux(実際には、WSL 2)におけるGCC 9.3.0でのアウトプット

@出力
###### checking the sizes of 'time_t' and 'tm' Start
######### the sizes of 'time_t' and 'tm': 8, 56
###### checking the sizes of 'time_t' and 'tm' End

Microsoft Windows 10におけるVisual C++ 2019でのアウトプット

@出力
###### checking the sizes of 'time_t' and 'tm' Start
######### the sizes of 'time_t' and 'tm': 8, 36
###### checking the sizes of 'time_t' and 'tm' End

その差異は、重要かもしれない、多くの日時データをメモリ内にしまっておかなければならないのであれば。

したがって、'time_t'および'tm'は、一緒に使われるように意図されたペアである、基本的に。

'tm'の解像度は明らかに1秒であるが、'time_t'の解像度はいくらなのか?「秒数」というのは、'整数値での秒数'を意味するのか?. . .それを、以下のようにテストしてみよう。

@C++ ソースコード
				cout << "###### checking the resolution of 'time_t' Start" << endl << flush;
				{
					time_t l_epochInTime_t = 0;
					time_t l_possiblySlightlyAdvancedDateAndTimeFromEpochInTime_t = 0.1;
					cout << "######### 'time_t' has a resolution of sub-second: " << ((l_epochInTime_t == l_possiblySlightlyAdvancedDateAndTimeFromEpochInTime_t) ? "false": "true") << endl << flush;
				}
				cout << "###### checking the resolution of 'time_t' End" << endl << flush;

Linux(実際には、WSL 2)におけるGCC 9.3.0またはMicrosoft Windows 10におけるVisual C++ 2019でのアウトプット

@出力
###### checking the resolution of 'time_t' Start
######### 'time_t' has a resolution of sub-second: false
###### checking the resolution of 'time_t' End

そのコードはコンパイルできる(小数値を'time_t'変数に入れようとすることについてコンパイラーは警告するかもしれないが)、しかし、小数部分はどのみち無視される。. . .したがって、解像度は1秒だ、少なくとも、それらのコンパイラーにおいては。


1-2: 典型的な使用方法の例


Hypothesizer 7
以下は、'time_t'および'tm'の典型的な使用方法の例だ。

@C++ ソースコード
				cout << "###### some typical usage of the 'time_t' and 'tm' pair Start" << endl << flush;
				{
					time_t l_42DaysAfterNowInTime_t = 0;
					time (&l_42DaysAfterNowInTime_t);
					l_42DaysAfterNowInTime_t += 60 * 60 * 24 * 42;
					tm l_42DaysAfterNowLocalTimeInTm = *(localtime (&l_42DaysAfterNowInTime_t));
					cout << "######### the 42 days after now local time: " << put_time (&l_42DaysAfterNowLocalTimeInTm, "%Y-%m-%dT%H:%M:%S") << endl << flush;
					tm l_42YearsAnd42DaysAfterNowUtcTimeInTm = *(gmtime (&l_42DaysAfterNowInTime_t));
					cout << "######### the 42 days after now UTC time: " << put_time (&l_42YearsAnd42DaysAfterNowUtcTimeInTm, "%Y-%m-%dT%H:%M:%S") << endl << flush;
					l_42YearsAnd42DaysAfterNowUtcTimeInTm.tm_year += 42;
					// 'tm_mon' and 'tm_mday' may have to be adjusted if they are '2' and '29', but spare me here, please.
					cout << "######### the 42 years and 42 days after now UTC time: " << put_time (&l_42YearsAnd42DaysAfterNowUtcTimeInTm, "%Y-%m-%dT%H:%M:%S") << endl << flush;
					// the 'tm' instance is supposed to be in the local time for 'mktime', but let me correct the datum after the conversion, because adjusting the 'tm' instance is tedious.
					time_t l_42YearsAnd42DaysAfterNowDateAndTimeInTime_t = mktime (&l_42YearsAnd42DaysAfterNowUtcTimeInTm);
					time_t l_epochInTime_t (0);
					l_42YearsAnd42DaysAfterNowDateAndTimeInTime_t += 60 * 60 * (localtime (&l_epochInTime_t))->tm_hour;
					cout << "######### the 42 years and 42 days after now reconstructed local time: " << put_time (localtime (&l_42YearsAnd42DaysAfterNowDateAndTimeInTime_t), "%Y-%m-%dT%H:%M:%S") << endl << flush;
					
				}
				cout << "###### some typical usage of the 'time_t' and 'tm' pair End" << endl << flush;

Linux(実際には、WSL 2)におけるGCC 9.3.0またはMicrosoft Windows 10におけるVisual C++ 2019でのアウトプット

@出力
###### some typical usage of the 'time_t' and 'tm' pair Start
######### the 42 days after now local time: 2021-02-10T21:42:54
######### the 42 days after now UTC time: 2021-02-10T12:42:54
######### the 42 years and 42 days after now UTC time: 2063-02-10T12:42:54
######### the 42 years and 42 days after now reconstructed local time: 2063-02-10T21:42:54
###### some typical usage of the 'time_t' and 'tm' pair End

システム日時は'time_t'インスタンスとして取得できる; 'time_t'は、秒、分、時間、日を加えたり減じたりするような一部の計算をするのに好都合だ、しかし、月、年を加えたり減じたりするのにはそうでないかもしれない(なぜなら、ある月の日数はその月に依存する、等だから)、その場合は、'tm'のほうが良いかもしれない(閏年の判断はそれでも必要であるが); 経過時間は'time_t'で容易に取得できる; 任意の'time_t'インスタンスは'tm'インスタンスへコンバートでき、逆も同様だ。


2: '::std::chrono::time_point'



2-1: '::std::chrono::time_point'への動機


Hypothesizer 7
'::std::chrono::time_point'は、1つの'Clock'と1つの'Duration'をタイプパラメータ群として取るクラステンプレートだ。

「Clock」って何だ?'Clock'とは、基本的に、ある紀元、ある'Duration'、およびある'now ()'メソッドだ。

「Duration」って何だ?'Duration'とは、基本的に、ある解像度だ。

えーと、なぜ、'time_point'は'Duration'を取るのか、'Clock'が既に'Duration'を持っているというのに?'Clock'の'Duration'は、'now ()'メソッドの解像度を決定する、'time_point'の'Duration'が、'time_point'のデータコンテナとしての解像度を決定するのに対して、しかし、'time_point'の'Duration'は省略することができ(そして通常、される)、デフォルトで'Clock'の'Duration'と同じになる。

我々は、既に'time_t'、'tm'ペアを持っているのに、なぜ、'::std::chrono::time_point'を必要とするのだろうか?

動機は、原始的な'time_t'、'tm'ペアに取って変わる便利なオブジェクト指向のデータタイプを持つことではなく、より高く、コントロールされた解像度群を持つことであるようだ。

一般的に言って、'time_point'は、カレンダーの特定の日時を指し示すためというよりも、'time_point'インスタンス間の経過時間を取得するためのものである: 基本的に、'time_point <system_clock>'だけが、カレンダーの特定の日時を知らせてくれ、それさえも、ただ、その責任を'time_t'、'tm'ペアに転嫁することによってである。

留意すべきだが、ある解像度をC++的に持っていることは、本当にその解像度を持っていることを必ずしも意味しない。例えば、1ナノ秒解像度の'now ()'は、1ナノ秒解像度の'time_point'のインスタンスをリターンするが、システムクロックがもっと低い解像度をOS的に持っていれば、勿論、当該'time_point'インスタンスに格納された日時は、その精度を持たない。


2-2: いくつかの'Clock'


Hypothesizer 7
3個の主要な'Clock'がある: '::std::chrono::system_clock'、'::std::chrono::steady_clock'、'::std::chrono::high_resolution_clock'。

'steady_clock'は、安定した'Clock'だが、それは、任意の2個の'now ()'コールの内の後者は、決してより古い日時をリターンしないことを意味する。

えーと、それは特別なことだろうか?どんなクロックがそうでないというのか?

実のところ、'system_clock'はそうでないかもしれない、なぜなら、システムクロックは、NTP、手動、等によって、過去方向へ調整されるかもしれない。

'system_clock'は、システムクロックに結びつけられた'Clock'だ。

えーと、システムクロックに結びつけられていない'Clock'があるというのか?そう、なぜなら、システムクロックに結びつけられていることはクロックを過去方向に動かす可能性があるから、先程、言ったとおり。

'high_resolution_clock'は、最高解像度を持っていることが保証されている'Clock'だ。それは安定であるかもしれないし、ないかもしれない。実際には、'high_resolution_clock'は、GCC 9.3.0においては、'system_clock'の別名であり、Visual C++ 2019においては、'steady_clock'の別名である。

とにかく、それら3個のクロックの解像度は何なのか?. . .それは、コンパイラーに依存するが、私のGCC 9.3.0においては、'system_clock'および'steady_clock'の解像度は、1ナノ秒および1ナノ秒であり、Visual C++ 2019においては、'system_clock'および'steady_clock'の解像度は、100ナノ秒および1ナノ秒である、以下のコードでチェックできるとおり。

@C++ ソースコード
				cout << "###### checking the resolution of 'system_clock' Start" << endl << flush;
				{
					cout << "######### the tick of 'system_clock': " << system_clock::period::num << " / " << system_clock::period::den << endl << flush;
					time_point <system_clock> l_nowInTime_pointOfSystem_clock (system_clock::now ());
					nanoseconds l_durationOfNowFromEpochInNanoseconds = l_nowInTime_pointOfSystem_clock.time_since_epoch ();
					cout << "######### the duration of now from the epoch in nanoseconds: " << l_durationOfNowFromEpochInNanoseconds.count () << endl << flush;
					time_point <system_clock> l_1NanosecondFromNowInTime_pointOfSystem_clock (duration_cast <system_clock::duration> (l_durationOfNowFromEpochInNanoseconds + nanoseconds (1)));
					cout << "######### the resolution of 'system_clock' is 1 nanosecond: " << ((l_1NanosecondFromNowInTime_pointOfSystem_clock == l_nowInTime_pointOfSystem_clock) ? "false": "true") << endl << flush;
				}
				cout << "###### checking the resolution of 'system_clock' End" << endl << flush;
				
				cout << "###### checking the resolution of 'steady_clock' Start" << endl << flush;
				{
					cout << "######### the tick of 'steady_clock': " << steady_clock::period::num << " / " << steady_clock::period::den << endl << flush;
					time_point <steady_clock> l_nowInTime_pointOfSteady_clock (steady_clock::now ());
					nanoseconds l_durationOfNowFromEpochInNanoseconds = l_nowInTime_pointOfSteady_clock.time_since_epoch ();
					cout << "######### the duration of now from the epoch in nanoseconds: " << l_durationOfNowFromEpochInNanoseconds.count () << endl << flush;
					time_point <steady_clock> l_1NanosecondFromNowInTime_pointOfSteady_clock (duration_cast <steady_clock::duration> (l_durationOfNowFromEpochInNanoseconds + nanoseconds (1)));
					cout << "######### the resolution of 'steady_clock' is 1 nanosecond: " << ((l_1NanosecondFromNowInTime_pointOfSteady_clock == l_nowInTime_pointOfSteady_clock) ? "false": "true") << endl << flush;
				}
				cout << "######### checking the resolution of 'steady_clock' End" << endl << flush;
				cout << "###### checking the resolution of 'system_clock' End" << endl << flush;

Linux(実際には、WSL 2)におけるGCC 9.3.0でのアウトプット

@出力
###### checking the resolution of 'system_clock' Start
######### the tick of 'system_clock': 1 / 1000000000
######### the duration of now from the epoch in nanoseconds: 1609332174715479100
######### the resolution of 'system_clock' is 1 nanosecond: true
###### checking the resolution of 'system_clock' End
###### checking the resolution of 'steady_clock' Start
######### the tick of 'steady_clock': 1 / 1000000000
######### the duration of now from the epoch in nanoseconds: 179608188792790
######### the resolution of 'steady_clock' is 1 nanosecond: true
######### checking the resolution of 'steady_clock' End
###### checking the resolution of 'system_clock' End

Microsoft Windows 10におけるVisual C++ 2019でのアウトプット

@出力
###### checking the resolution of 'system_clock' Start
######### the tick of 'system_clock': 1 / 10000000
######### the duration of now from the epoch in nanoseconds: 1609332389157981200
######### the resolution of 'system_clock' is 1 nanosecond: false
###### checking the resolution of 'system_clock' End
###### checking the resolution of 'steady_clock' Start
######### the tick of 'steady_clock': 1 / 1000000000
######### the duration of now from the epoch in nanoseconds: 1528852745499300
######### the resolution of 'steady_clock' is 1 nanosecond: true
######### checking the resolution of 'steady_clock' End
###### checking the resolution of 'system_clock' End

Microsoft Windows 10の日時解像度(少なくとも、私のコンピュータの)は、100ナノ秒であるようだ; したがって、どの'Clock'の'now ()'メソッドもそれ以上の精度を持たない。

それでは、紀元はそれぞれいつなのか?えーと、それらは、コンパイラーに依存し、そのことはオーケーなのだが、残念なことに、一般的に言って、それらを知る方法さえも無いようで(少なくとも、正確には)('system_clock'の紀元は知ることができるが)、それは、'steady_clock'は、基本的に、同一クロックの'time_point'インスタンス間の経過時間を取得するためだけのものであることを意味する。


2-3: 'time_point'のサイズ


Hypothesizer 7
'time_point'のサイズをチェックしてみよう、以下のように。

@C++ ソースコード
				cout << "###### checking the size of 'time_point' Start" << endl << flush;
				{
					time_point <system_clock> l_dateAndTimeInTime_pointOfSystem_clock;
					time_point <steady_clock> l_dateAndTimeInTime_pointOfSteady_clock;
					cout << "######### the sizes of 'time_point <system_clock>' and 'time_point <steady_clock>': " << sizeof (l_dateAndTimeInTime_pointOfSystem_clock) << ", " << sizeof (l_dateAndTimeInTime_pointOfSteady_clock) << endl << flush;
				}
				cout << "###### checking the size of 'time_point' End" << endl << flush;

Linux(実際には、WSL 2)におけるGCC 9.3.0またはMicrosoft Windows 10におけるVisual C++ 2019でのアウトプット

@出力
###### checking the size of 'time_point' Start
######### the sizes of 'time_point <system_clock>' and 'time_point <steady_clock>': 8, 8
###### checking the size of 'time_point' End

おー、予期したよりも良い。


2-4: 任意の'time_point <system_clock>'インスタンスのカレンダー日時を取得する


Hypothesizer 7
任意の'time_point <system_clock>'インスタンスのカレンダー日時は、'system_clock::to_time_t (const time_point & t)'を介して取得できる。


2-5: カレンダー日時の秒以下部分を取得する


Hypothesizer 7
しかし、それは、カレンダー日時を1秒精度のみにて取得する。実際には、私は、秒以下精度が欲しいのだ。典型的には、私は、'time_point <system_clock>'インスタンスを'2020-12-29T11:42:36.859428932'のような形式にて表示したい。.

'system_clock'紀元が任意の日時のある秒フラットであると仮定して(つまり、'1975-02-03T13:45:39'はグッドだが、'1975-02-03T13:45:39.8'はそうでない)、それを以下のようにして取得できる。

@C++ ソースコード
				cout << "###### getting the sub-second part of a 'time_point <system_clock>' instance Start" << endl << flush;
				{
					time_point <system_clock> l_nowInTime_pointOfSystem_clock (system_clock::now ());
					time_t const l_nowInTime_t (system_clock::to_time_t (l_nowInTime_pointOfSystem_clock));
					system_clock::duration l_durationOfNowFromEpochInSystem_clockDuration = l_nowInTime_pointOfSystem_clock.time_since_epoch ();
					int l_fractionInNanoseconds (floor <nanoseconds> (l_durationOfNowFromEpochInSystem_clockDuration).count () - floor <seconds> (l_durationOfNowFromEpochInSystem_clockDuration).count () * 1000000000);
					cout << "######### now: " << put_time (localtime (&l_nowInTime_t), "%Y-%m-%dT%H:%M:%S.") << setfill ('0') << setw (9) << l_fractionInNanoseconds << endl << flush;
				}
				cout << "###### getting the sub-second part of a 'time_point <system_clock>' instance End" << endl << flush;


2-6: Daring to Convert Any 'time_point <steady_clock>' Instance to a 'time_point <system_clock>' Instance 'time_point <steady_clock>'インスタンスを'time_point <system_clock>'へ、あえてコンバートする


Hypothesizer 7
'steady_clock'の'time_point'インスタンスのカレンダー日時は取得できない(基本的には)、それは、その紀元を知ることができないからであり、それは、'steady_clock'の'time_point'インスタンスは'system_clock'の'time_point'インスタンスへコンバートできないことも意味する。 .

注意として、C++標準20('template <class Dest, class Source, class Duration> clock_cast (const std::chrono::time_point <Source, Duration> &amp; t)'を持っているが、それを私はテストできない、なぜなら、私の環境はその標準レベルを少なくとも公式にはサポートしないから)が使えることを私は想定していない。

しかしながら、'steady_clock'の紀元の、'system_clock'の紀元からの差異は、近似的に取得できる、以下のようにして。

@C++ ソースコード
					microseconds l_approximatedEpochDifferenceFromSystem_clockToSteady_clock (duration_cast <microseconds> (system_clock::now ().time_since_epoch ()) - duration_cast <microseconds> (steady_clock::now ().time_since_epoch ()));
					cout << "######### the approximated epoch difference from 'system_clock' to 'steady_clock' in microseconds: " << l_approximatedEpochDifferenceFromSystem_clockToSteady_clock.count () << endl << flush;

その2個の日時は近似的に同一なので、それらの経過時間の間の差異は、近似的にそれらの紀元の間の差異である。'system_clock'の紀元は取得できるので、'steady_clock'の紀元が近似的に取得できる。

えーと、誤差はどのようなものになるだろうか?それは、勿論、プラットフォームに依存するが、私のプラットフォーム(とても低速である)上でのテストをしてみよう、以下のようにして。

@C++ ソースコード
					nanoseconds l_estimatedEpochDifferenceError (duration_cast <nanoseconds> (steady_clock::now ().time_since_epoch ()) - duration_cast <nanoseconds> (steady_clock::now ().time_since_epoch ()));
					cout << "######### the estimated epoch difference error in nanoseconds: " << l_estimatedEpochDifferenceError.count () << endl << flush;

Microsoft Windows 10におけるVisual C++ 2019でのアウトプット

@出力
######### the estimated epoch difference error in nanoseconds: 200

えーと、1マイクロ秒精度は期待できそうだ。

結局、'steady_clock'の任意の'time_point'インスタンスを'system_clock'の'time_point'インスタンスへコンバートできる、以下のようにして。

@C++ ソースコード
				cout << "### daring to convert a 'time_point <steady_clock>' instance to a 'time_point <system_clock>' instance Start" << endl << flush;
				{
					microseconds l_approximatedEpochDifferenceFromSystem_clockToSteady_clock (duration_cast <microseconds> (system_clock::now ().time_since_epoch ()) - duration_cast <microseconds> (steady_clock::now ().time_since_epoch ()));
					time_point <steady_clock> l_nowInTime_pointOfSteady_clock (steady_clock::now ());
					time_point <system_clock> l_nowInTime_pointOfSystem_clock (l_approximatedEpochDifferenceFromSystem_clockToSteady_clock + duration_cast <microseconds> (l_nowInTime_pointOfSteady_clock.time_since_epoch ()));
				}
				cout << "###### daring to convert a 'time_point <steady_clock>' instance  to a 'time_point <system_clock>' instance End" << endl << flush;


2-7: 典型的な使用法の例


Hypothesizer 7
以下は、'time_point'の典型的な使用法の例だ。

@C++ ソースコード
				cout << "###### some typical usage of 'time_point <system_clock>' Start" << endl << flush;
				{
					time_point <system_clock> l_42DaysAfterNowInTime_pointOfSystem_clock (system_clock::now ());
					l_42DaysAfterNowInTime_pointOfSystem_clock += hours (24 * 42);
					time_t const l_42DaysAfterNowInTime_t (system_clock::to_time_t (l_42DaysAfterNowInTime_pointOfSystem_clock));
					cout << "######### the 42 days after now: " << put_time (localtime (&l_42DaysAfterNowInTime_t), "%Y-%m-%dT%H:%M:%S") << endl << flush;
				}
				cout << "###### some typical usage of 'time_point <system_clock>' End" << endl << flush;
				
				cout << "###### some typical usage of 'time_point <steady_clock>' Start" << endl << flush;
				{
					time_point <steady_clock> l_dueDateAndTimeInTime_pointOfSteady_clock (steady_clock::now ());
					l_dueDateAndTimeInTime_pointOfSteady_clock += hours (24 * 42);
					cout << "######### the duration of the due date and time from now in seconds: " << (duration_cast <seconds> ( (l_dueDateAndTimeInTime_pointOfSteady_clock - steady_clock::now ())).count ()) << endl << flush;
				}


3: それで、私はどうするべきなのか?


Hypothesizer 7
それで、私はどうするべきなのか?

もしも、ある解像度が要件であるのなら、その解像度を持ったデータタイプを使用する以外の選択肢はない。

その意味において、'time_t'、'tm'ペアは、それほど汎用的でなく、仕様上は、'time_point <high_resolution_clock>'が最も頼りになるはずだ。

しかし、'high_resolution_clock'は、安定性が明確でないという点において問題だ。

したがって、私の標準は、もしも、安定性が求められるのであれば、'time_point <steady_clock>'を使用し、そうでなければ、'time_point <system_clock>'を使うというものだ。

任意の'time_point <steady_clock>'インスタンスは'time_point <system_clock>'インスタンスへコンバートできる、もしも、1マイクロ秒精度で問題なければ。

'time_t'、'tm'ペアは、'time_point <system_clock>'インスタンスからカレンダー日時を取得するために必要とされる、どのみち。


4: なぜ、一人前の、オブジェクト志向の標準日時タイプがないのか?


Hypothesizer 7
なぜ、C++標準は、一人前の、オブジェクト志向の標準日時タイプを持とうとしないのか、と疑問を持つ?

'time_point'は、経過時間を取り扱うことにフォーカスしたものであって、一人前の日時タイプではない、これまで見てきたとおり。

実際、なぜ、'time_point'インスタンスから紀元が取得できないのか?なぜ、'time_point'インスタンスは直接にカレンダー日時をオファーしないのか?

もしも、それは効率性の考慮のためであると主張する人がいるのであれば、私は、便利なクラス(勿論、効率性の考慮がゆえにそれを用いることが不適切な場合には使用する必要はない)をオファーすることは何の害にもならないだろうと言っているのである。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>