C#によるgRPC通信でのTimeStampとDateTimeの解説

2021年2月16日火曜日

技術的備忘録

t f B! P L


「C#によるgRPC通信のサンプルコード」に戻る


WPFアプリとgRPCサービスで、日時を通信する処理を作成してみたところ、案外分かり難い点が多いので、ハマりやすい部分を重点的に解説したいと思う。

Protocol Buffers のC#ライブラリのTimestampと、.NETのDateTimeの仕様の違いから、単純な両者の変換処理では使用できない。

両者を正しく使用するにはUTC,UNIX時刻(Timestamp),タイムゾーン,DateTimeKindの知識が必要となる。

その点を踏まえて、Protocol Buffers の Timestamp と、.NETの DateTime・DateTimeOffsetを使用したクライアント・サーバー型アプリの使い方を解説する。

サンプルコードとして以下のGitHubへのリンクを掲載しておく。

二つの単項メソッドを追加している。

https://github.com/motoi-tsushima/Sample_GrpcService/releases/tag/1.3.0.0

https://github.com/motoi-tsushima/Sample_gRPC_WpfApp/releases/tag/1.3.1.0

 

日時についての基礎知識

Protocol Buffers の Timestamp と、.NETの DateTime の扱い方を解説する前に、日付と日付型(時刻を含む)についての基礎知識の解説をする。

UTCとUNIX時刻(Timestamp)

Protocol Buffers の Timestamp は、その名に違わずUNIZタイムスタンプの値を持つ。

だから gRPCで日付型を扱う為にはUNIXタイムスタンプの知識が必要であり、更にUTCの知識も必要になる。

この認識がないと gRPCで日付型を扱う事ができない。

 

UNIX時刻(Timestamp)

UNIXタイムスタンプは1970年1月1日0時0分0秒からの経過秒数を整数で有する値である。

年月日時分の値は無い。秒数だけだ。

昔は32bitで表していたが、近年は64bit整数で表すものが多くなってきている。

Protocol Buffers の Timestamp も64bit整数で値を有する。

 

UTC(協定世界時)

UTとは国際的に定められた、英国の標準時である「グリニッジ標準時」を表す「世界時」の規格である。

UT1・UT2・UTC の三種類があり、一般市民が使用する「世界時」の規格が「UTC」である。

UTCは、1970年1月1日0時0分0秒からの経過秒数を整数で保持し、それを基に年月日時分秒に変換する規格を定めている。

多くのOSやコンピュータ言語やDBMSで、UNIXタイムスタンプからUTCに変換する機能を有し、世界各国の時差に対応する為に基準となる日付時刻になっている。

多くの場合、日本時間への対応もUTCから変換する事で行っている。

 

時差とタイムゾーン

地球は丸いので世界各国の標準時には時差がある。

各国にはそれぞれの「標準時」が存在する。

この中で東西方向の位置を基準に、南北に重なる国々の標準時は同じになる。

この同じ標準時ごとの分類をタイムゾーンと呼ぶ。

タイムゾーンはUTCとの時差で表す。

日本の場合は、UTCより9時間早いので「UTC+9:0:0」と表す。

台湾やシンガポールなら、UTCより8時間早いので「UTC+8:0:0」と表す。

ニューヨーク(米国東部時間)なら、UTCより5時間遅いので「UTC-5:0:0」と表す。

 

DateTimeとタイムゾーン

.NET で、国内専用の業務システムを開発していると意識しないが、DateTime型は自国とUTCを識別する情報を保有している。

タイムゾーンの情報は保有していないが、その日時がUTCであるか自国であるかを識別するフラグを有する。

この情報はコンストラクタで指定する事ができ、DateTimeKindをコンストラクタ引数に渡す事によって行う。

DateTimeKindは、Local, Unspecified, Utc の三つを区別する。

https://docs.microsoft.com/ja-jp/dotnet/api/system.datetimekind?view=net-5.0

Protocol Buffers の Timestamp は DateTimeKind.Utc を有するDateTime型でなければ使用できない。

それ以外は変換エラーとなる。

https://docs.microsoft.com/ja-jp/dotnet/api/system.datetime?view=net-5.0

 

DateTimeOffsetとタイムゾーン

DateTimeOffset型 はDateTime型の拡張で、DateTime型に加えて、内部にタイムゾーンの情報を有する。

日本のタイムゾーンを有するDateTimeOffset型は、日本標準時と時差「UTC+9:0:0」の情報を持つ。

UTCを参照する時はこの時差情報から、UTCに変換する。

これにより世界中のタイムゾーンの日付型を、一つのシステムに共存できる。

Protocol Buffers の Timestamp は DateTimeOffset型 に対応しており、Timestamp へ変換する時UTCへ変換する。

https://docs.microsoft.com/ja-jp/dotnet/api/system.datetimeoffset?view=net-5.0

 

TimeSpan の使い方

TimeSpan は、日数、時間、分、秒、および秒の小数部の正または負の数として計測される時間間隔 (時間または経過時間) を表す。

異なる二つのDateTime型の日数時間差を表す時などに使用する。

DateTimeOffset型を生成する時にタイムゾーンをコンストラクタ引数に渡す器として使用する。

Protocol Buffers の異なる二つの Timestamp型の時間差を表す型に、Duration型 が有る。

これを .NETの型に変換する場合は、TimeSpan型のオブジェクトに変換する。

 

Timestamp型とTimeSpan型は、名前が似ているので最初は混乱するが、両者は全然違うものだ。

 

Timestamp と、.NETの 日付型との二つの扱い方

Protocol Buffers の Timestamp型は、UTC以外の標準時を受け付けない仕様の為、日本標準時などローカルの標準時を使用する場合は、工夫が必要になる。

Protocol Buffers の Timestamp型と、.NETの日付型を扱う方法は大きく二つになると思う。

 

一つは、全てDateTime型だけでシステムを開発する方法。

 

もう一つは、DateTimeOffset型を中心に使用し、UTCと日本標準時を明確に使い分ける扱い方だ。

 

日本国内のみDateTimeだけで扱う

全ての日付をDateTimeで扱うのなら、全ての日付型を「UTC」と解釈してシステム全体を開発する必要がある。

つまり、Protocol Buffers の Timestamp型を、日本標準時が「UTC」であると欺いて使用する事になる。

日本国内でのみ使用するシステムなら、この方がシンプルで良い。

特にペナルティーもない。

 

国際対応でDateTimeOffsetで扱う

正攻法で Protocol Buffers の Timestamp型を .NET で使用するなら、厳格にタイムゾーンを区別して日付型を扱う必要がある。

.NETの日付型には DateTimeOffset型を使用する。

初めから世界各国のタイムゾーンが共存できるように開発する。

 

gRPCサービス側はUTC中心に日付型を扱う

どちらの方法でも、シリアライズは全てUNIXタイムスタンプ(UTCと同じグリニッジ標準時)になるので、システム的には gRPCサービス側では「UTC」日付を中心に情報処理を行う事になる。

クライアント側はUNIXタイムスタンプを、ローカル標準時に変換して使用する。

gRPCサービスへローカル標準時を送る時は、UNIXタイムスタンプへ変換する。

gRPCサービス側は、常にUNIXタイムスタンプを受け取る。

 

日本国内のみDateTimeだけで扱うサンプル

日本国内のみ、C#のDateTimeだけで扱うgRPCサンプルコード

 


国際対応でDateTimeOffsetで扱うサンプル

国際対応で、C#のDateTimeOffsetで扱うgRPCサンプルコード

 


最初に国内のみか、国際対応するか、考える

gRPCサービスで日付型を使用するシステムを開発する時は、最初の設計段階から「国内のみで使用する」のか「国際的に使用できる」ようにするか、考えておくべきだ。

コーディング段階で考えてはいけない。

設計段階、できれば要件定義の段階で、どちらかに決めるべきだ。

 

コーディング段階で考えると、各担当者ごとにタイムゾーンの扱いがバラバラになり、「どうしよう、どうしよう」と散々議論と試行錯誤した挙げ句、結局「国際化対応するしかない」という結論に気がつき、膨大な手戻り作業を行う事になる。

 

前工程で決められる事は、決めておこう。

優柔不断と曖昧さは「敵」だ。

前工程こそ「コスト」にシビアであるべきだが、多くのリーダーやマネージャーは後工程でコストにシビアになり、赤字を膨らませる。

この悪習は排除すべきだと思う。

 


「C#によるgRPC通信のサンプルコード」に戻る


このブログを検索

Translate

人気の投稿

自己紹介

自分の写真
オッサンです。実務経験は Windows環境にて C#,VB.NET ,SQL Server T-SQL,Oracle PL/SQL,PostgreSQL,MariaDB。昔はDelphi,C,C++ など。 趣味はUbuntu,PHP,PostgreSQL,MariaDBかな ?基本無料のやつ。

QooQ