Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2019年01月31日] [更新 2019年01月31日] Delphi 一般・その他
   
TDateTime 型とシステム時刻と .NET の DateTime 型の時刻
動作確認等 Windows 7 U64(SP1) + Delphi XE5(UP2) Pro VCL-32
[整理番号 - 1070]







TDateTime 型とシステム時刻と .NET の DateTime 型の時刻


以下の 2 つの時刻の値の変換とその逆変換のサンプルです.


  • TDateTime 型とシステムファイル時刻
  • TDateTime 型と .NET の DateTime 型の時刻

Delphi の TDateTime 日付時刻型は倍精度実数値です.この値が 0 の時は 1899 年 12 月 30 日 0 時0 分 0 秒です.
Windows のシステムファイル時刻 (または単にシステム時刻) の値は 64 ビットの整数で,1601 年 01 月 01 日 0 時 0 分 0 秒からの経過時間です.時間の単位は 100 ns (ナノ秒) です.
C# 等,.NET の日付時刻の DateTime 型は 64 ビットの整数で,0001 年 01 月 01 日 0 時 0 分 0 秒からの経過時間です.時間の単位は 100 ns (ナノ秒) です.

.NET の DateTime の時刻を Delphi で直接扱うことはありませんが,C# 等で開発したアプリケーションの日付や時刻のデータを扱う時に必要になるかも知れません.
これらの時刻型の値は,以下のリンクの記事の関数類を使用して相互に変換できます.






01_TDateTime 型とシステムファイル時刻との相互変換


Delphi の TDateTime 型の日付時刻の値と,システムファイル時刻との相互変換のサンプルです.



図1
TDateTime 型の値をシステム時刻に変換
  • システム時刻を UTC 時刻に換算した結果
  • その結果を TDateTime 型の値に変換

リスト1
TDateTime 型とシステムファイル時刻との相互変換
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  function TDateTimeToSystemFileTime(ADateTime: TDateTime; ToUTC: Boolean=False): Int64;
  function SystemFileTimeToTDateTime(AInt64: Int64; ToUTC: Boolean=False): TDateTime;

var
  Form1: TForm1;

implementation

uses
  DateUtils;

{$R *.dfm}

//=============================================================================
//  TDateTime の時刻をシステムファイル時刻の値に変換
//  変換後の値は 1601/1/1 9:0:0 からの経過時間 (単位は 100 ns)
//  その逆変換もテスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LInt64    : Int64;
  LDateTime : TDateTime;
begin
  Memo1.Lines.Clear;

  LDateTime := StrToDateTime('2019/01/06 17:26:33');

  // TDateTime の値をシステム時刻に変換
  LInt64 := TDateTimeToSystemFileTime(LDateTime);
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
  Memo1.Lines.Add('$' + IntToHex(LInt64, 16));

  Memo1.Lines.Add('');

  // TDateTime の値をシステム時刻に変換 (UTC に換算して)
  LInt64 := TDateTimeToSystemFileTime(LDateTime, True);
  Memo1.Lines.Add('$' + IntToHex(LInt64, 16));

  // 上で変換した値を TDateTime の値に変換して UTC 時刻になっているかを確認
  LDateTime := SystemFileTimeToTDateTime(LInt64);
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
end;

//-----------------------------------------------------------------------------
//  Delphi の TDateTime 型の時刻の値をシステムファイルの時刻に変換する関数
//  取得する値は 1601/1/1 0:0:0 からの経過時間(単位は 100 ns)
//
//  uses に DateUtils が必要
//-----------------------------------------------------------------------------
function TDateTimeToSystemFileTime(ADateTime: TDateTime; ToUTC: Boolean=False): Int64;
var
  LSystemTime    : TSystemTime;
  LLocalFileTime : TFileTime;
  LUTCFileTime   : TFileTime;
begin
  // TDateTime の時刻をシステム時刻に変換
  DateTimeToSystemTime(ADateTime, LSystemTime);

  // システム時刻をローカルなファイル時刻に変換
  SystemTimeToFileTime(LSystemTime, LLocalFileTime);

  if ToUTC then begin
    // ローカルのファイル時刻を世界協定時刻 (UTC) ファイル時刻へ換算
    LocalFileTimeToFileTime(LLocalFileTime, LUTCFileTime);
    Result := Int64(LUTCFileTime);
  end else begin
    Result := Int64(LLocalFileTime);
  end;
end;

//-----------------------------------------------------------------------------
//  システムファイルの時刻を Delphi の TDateTime 型の時刻の値に変換する関数
//  引数には 1601/1/1 0:0:0 からの経過時間(単位は 100 ns) を与える
//
//  uses に DateUtils が必要
//-----------------------------------------------------------------------------
function SystemFileTimeToTDateTime(AInt64: Int64; ToUTC: Boolean=False): TDateTime;
var
  LUTCFileTime : TFileTime;
  LSystemTime  : TSystemTime;
begin
  // UTC に換算した値を取得する場合
  if ToUTC then begin
    LocalFileTimeToFileTime(TFileTime(AInt64), LUTCFileTime);
    FileTimeToSystemTime(LUTCFileTime, LSystemTime);
  end else begin
    FileTimeToSystemTime(TFileTime(AInt64), LSystemTime);
  end;
  Result := SystemTimeToDateTime(LSystemTime);
end;

end.




02_TDateTime 型と .NET の DateTime 型の時刻との相互変換


.NET の DateTime の値は 64 ビットですが,上位の 2 ビットは UTC (協定世界時) のフラグとして使用されています.残りの 62 ビットが実際の時刻の値です.このフラグが .NET でどのように使われているかの確認はしていません.Delphi のプログラムで時刻型の相互変換用に利用するのが目的なので,このフラグは任意に利用できます.
このフラグは 00 (= 0), 01 (= 1), 10 (= 2) の 3 種類あります.その 3 種類についてテストしています.このフラグは,.NET における DateTime 構造体の Kind メンバに相当します.



図2
[Button1] クリック
  • 上から順番に
  • TDateTime 型の値をそのまま .NET の DateTime 型の値に変換した結果
  • その値をそのまま TDateTime 型の値として取得
  • その値の上位 2 ビットを 0 にして TDateTime 型の値に変換した結果
図3
[Button2] クリック
    • 上から順番に
    • TDateTime の値を UTC 換算の .NET の DateTime 型の値に変換した結果
    • その値をそのまま TDateTime 型の値として取得
    • その値の上位 2 ビットを 0 にして TDateTime 型の値に変換した結果
図4
[Button3] クリック
  • 上から順番に
  • TDateTime 型の値を UTC の時刻とみなして,ローカルな時刻に変換して .NET の DateTime 型の値に変換した結果
  • その値をそのまま TDateTime 型の値として取得
  • その値の上位 2 ビットを 0 にして TDateTime 型の値に変換した結果

リスト2
TDateTime 型と .NET の DateTime 型の時刻との相互変換
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    Button3: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

  function DateTimeTodotNETTime(ADateTime: TDateTime; UTCFlag: Byte=0): UInt64;
  function dotNETTimeToDateTime(AUInt64: UInt64; ChFlag: Boolean=True): TDateTime;

var
  Form1: TForm1;

implementation

uses
  DateUtils;

{$R *.dfm}

//=============================================================================
//  Delphi の TDateTime の時刻を .NET の DateTime の値に変換
//  その逆変換もテスト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LDateTime : TDateTime;
  LInt64    : UInt64;
begin
  // 変換元の値
  LDateTime := StrToDateTime('2019/01/06 17:25:59');

  Memo1.Lines.Clear;

  // TDateTime の時刻を .NET の DateTime の値に変換 (UTC の指定なし)
  LInt64 := DateTimeTodotNETTime(LDateTime);
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
  Memo1.Lines.Add('$' + IntToHex(LInt64, 16));

  // 変換後の値を TDateTime 型の値に UTC フラグを考慮して変換
  Memo1.Lines.Add('');
  LDateTime := dotNETTimeToDateTime(LInt64);
  Memo1.Lines.Add('$' +IntToHex(LInt64, 16));
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));

  // 変換後の値を TDateTime 型の値に UTC フラグを無視して変換
  Memo1.Lines.Add('');
  LDateTime := dotNETTimeToDateTime(LInt64, False);
  Memo1.Lines.Add('$' +IntToHex(LInt64, 16));
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
end;

//=============================================================================
//  Delphi の TDateTime の時刻を .NET の DateTime の値に変換
//  その逆変換もテスト
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LDateTime : TDateTime;
  LInt64    : UInt64;
begin
  // 変換元の値
  LDateTime := StrToDateTime('2019/01/06 17:25:59');

  Memo1.Lines.Clear;

  // TDateTime の時刻を .NET の DateTime の値に UTC 時刻に換算して変換
  LInt64 := DateTimeTodotNETTime(LDateTime, 1);
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
  Memo1.Lines.Add('$' + IntToHex(LInt64, 16));

  // 変換後の値を TDateTime 型の値に UTC フラグを考慮して変換
  Memo1.Lines.Add('');
  LDateTime := dotNETTimeToDateTime(LInt64);
  Memo1.Lines.Add('$' +IntToHex(LInt64, 16));
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));

  // 変換後の値を TDateTime 型の値に UTC フラグを無視して変換
  Memo1.Lines.Add('');
  LDateTime := dotNETTimeToDateTime(LInt64, False);
  Memo1.Lines.Add('$' +IntToHex(LInt64, 16));
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
end;

//  Delphi の TDateTime の時刻を .NET の DateTime の値に変換
//  その逆変換もテスト
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  LDateTime : TDateTime;
  LInt64    : UInt64;
begin
  LDateTime := StrToDateTime('2019/01/06 17:25:59');

  Memo1.Lines.Clear;

  // TDateTime の時刻を .NET の DateTime の値にローカル時刻にとして変換
  LInt64 := DateTimeTodotNETTime(LDateTime, 2);
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
  Memo1.Lines.Add('$' + IntToHex(LInt64, 16));

  // 変換後の値を TDateTime 型の値に UTC フラグを考慮して変換
  Memo1.Lines.Add('');
  LDateTime := dotNETTimeToDateTime(LInt64);
  Memo1.Lines.Add('$' +IntToHex(LInt64, 16));
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));

  // 変換後の値を TDateTime 型の値に UTC フラグを無視して変換
  Memo1.Lines.Add('');
  LDateTime := dotNETTimeToDateTime(LInt64, False);
  Memo1.Lines.Add('$' +IntToHex(LInt64, 16));
  Memo1.Lines.Add(FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
end;

//-----------------------------------------------------------------------------
//  TDateTime 型の値を .NET の DateTime の値に変換する関数
//
//  引数の UTCFlag の値による変換結果の違い
//  このフラグは .NET framework 2.0 で導入された
//  0 : そのまま変換
//  1 : 引数の時刻をローカルな時刻とみなしてUTC 時刻に換算して変換
//  2 : 引数の時刻を UTC の時刻とみなしてローカルな時刻に換算して変換 
//
//  TDateTime 型は倍精度実数値で 1899/12/30 が基準点
//  システムファイル 時刻は 1601/01/01 が基準の 64 ビット整数値 (100 ns 単位)
//  .NET の DateTime は 0001/01/01 が基準の 64 ビット整数値 (100 ns 単位)
//
//  uses に DateUtils が必要
//-----------------------------------------------------------------------------
function DateTimeTodotNETTime(ADateTime: TDateTime; UTCFlag: Byte=0): UInt64;
var
  LUTCFlag       : Byte;
  LSystemTime    : TSystemTime;
  LLocalFileTime : TFileTime;
  LFileTime      : TFileTime;
  LUTCFileTime   : TFileTime;
begin
  LUTCFlag := UTCFlag;
  if LUTCFlag > 2 then LUTCFlag := 0;

  // TDateTime の時刻をシステム時刻に変換
  DateTimeToSystemTime(ADateTime, LSystemTime);
  // 1600年分 を加算
  LSystemTime.wYear := LSystemTime.wYear + 1600;

  if UTCFlag = 1 then begin
    // UTC 時刻に変換して取得
    SystemTimeToFileTime(LSystemTime, LLocalFileTime);
    LocalFileTimeToFileTime(LLocalFileTime, LUTCFileTime);
    Result := Int64(LUTCFileTime) or (UInt64(1) shl 62);
  end else
  if UTCFlag = 2 then begin
    // ローカルな時刻に換算して取得
    SystemTimeToFileTime(LSystemTime, LFileTime);
    FileTimeToLocalFileTime(LFileTime, LLocalFileTime);
    Result := Int64(LLocalFileTime) or (UInt64(1) shl 63);
  end else begin
    // 変換なし
    SystemTimeToFileTime(LSystemTime, LLocalFileTime);
    Result := Int64(LLocalFileTime);
  end;
end;

//-----------------------------------------------------------------------------
//  .NET の DateTime をローカルな時刻として TDateTime 型の値に変換する関数

//  引数の上位 2 ビットは UTC かローカルの値かのフラグ
//  このフラグは .NET framework 2.0 で導入された
//  ただし ChFlag の値が False の時はこの上位 2 ビットを無視して変換する 
//  00 (= 0) : 指定なし
//  01 (= 1) : UTC
//  10 (= 2) : ローカル 
//
//  TDateTime 型は倍精度実数値で 1899/12/30 が基準点
//  システムファイル 時刻は 1601/01/01 が基準の 64 ビット整数値 (100 ns 単位)
//  .NET の DateTime は 0001/01/01 が基準の 64 ビット整数値 (100 ns 単位)
//
//  uses に DateUtils が必要
//-----------------------------------------------------------------------------
function dotNETTimeToDateTime(AUInt64: UInt64; ChFlag: Boolean=True): TDateTime;
var
  LUTCFlag       : Byte;
  LUInt64        : UInt64;
  LSystemTime    : TSystemTime;
  LLocalFileTime : TFileTime;
begin
  // 最上位の 2 ビットの値を取得
  LUTCFlag := AUInt64 shr 62;
  if not ChFlag then LUTCFlag := 0;

  // 最上位の 2 ビットを 0 にした値に変換
  LUInt64 := AUInt64 and $3FFFFFFFFFFFFFFF;

  if LUTCFlag = 0 then begin
    FileTimeToSystemTime(TFileTime(LUInt64), LSystemTime);
  end else
  if LUTCFlag = 1 then begin
    FileTimeToLocalFileTime(TFileTime(LUInt64), LLocalFileTime);
    FileTimeToSystemTime(TFileTime(LLocalFileTime), LSystemTime);
  end else begin
    FileTimeToSystemTime(TFileTime(LUInt64), LSystemTime);
  end;

  // 1600年分大きいので減算
  LSystemTime.wYear := LSystemTime.wYear - 1600;
  Result := SystemTimeToDateTime(LSystemTime);
end;

end.




03_ローカル時刻と協定世界時 ( UTC ) の相互変換


上のサンプル内に UTC (協定世界時) に変換する部分があります.
TDateTime 型の日付時刻の値を UTC の時刻に変換したり,逆に UTC の時刻をローカルの時刻に換算するだけであれば,TTimeZone クラスのプロパティやメソッドが利用できます.



図5
ローカル時刻を UTC 時刻に変換

リスト3
TDateTime 型におけるローカル時刻と協定世界時 ( UTC ) の相互変換
implementation

uses
  System.DateUtils;

{$R *.dfm}

//=============================================================================
//  ローカル時刻を協定世界時 (UTC) に変換
//  uses に DateUtils が必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LDateTime : TDateTime;
begin
  Memo1.Lines.Clear;

  Memo1.Lines.Add(TTimeZone.Local.DisplayName);
  Memo1.Lines.Add(TTimeZone.Local.ID);
  Memo1.Lines.Add(TTimeZone.Local.Abbreviation);

  LDateTime := Now;
  Memo1.Lines.Add('ローカル時刻 :' + FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));

  LDateTime := TTimeZone.Local.ToUniversalTime(LDateTime);
  Memo1.Lines.Add('協定世界時 :' + FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
end;

//=============================================================================
//  協定世界時 (UTC) をローカル時刻に変換
//  uses に DateUtils が必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LDateTime : TDateTime;
begin
  Memo1.Lines.Clear;

  LDateTime := Now;
  Memo1.Lines.Add('協定世界時 :' + FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));

  LDateTime := TTimeZone.Local.ToLocalTime(LDateTime);
  Memo1.Lines.Add('ローカル時刻 :' + FormatDateTime('yyyy/mm/dd hh:nn:ss', LDateTime));
end;