データベースの日付型列とDateTimeのKindについて でDateTimeのKindの状態によって、PostgreSQLのTIMESTAMP WITH TIME ZONE
型の列にどのように書き込まれるかを確認した。
今度は、データベースのテーブルをDataTableに取り込んだとき、日付型のデータがどうなるか確認してみる。
環境は前回に引き続き
Visual Studio 2017
Npgsql 4.0.2
PostgreSQL 9.2.4
で検証してみた。
現在、データベースにtable1というテーブルがあり、データは次のようになっているとする。
id | datetime_tz | datetime |
---|---|---|
1 | 2018-01-01 00:00:00+09 | 2018-01-01 00:00:00 |
2 | 2018-01-01 09:00:00+09 | 2018-01-01 00:00:00 |
3 | 2018-01-01 09:00:00+09 | 2018-01-01 00:00:00 |
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using Npgsql;
namespace DateTimeOffsetTestNpgsql
{
class Program
{
static void Main(string[] args)
{
NpgsqlConnection conn = new NpgsqlConnection("Server=xxx.xxx.xxx.xxx;User Id=xxxx;Password=xxxx;Database=xxxx;Pooling=false;");
NpgsqlDataAdapter da = new NpgsqlDataAdapter();
da.SelectCommand = new NpgsqlCommand("SELECT id, datetime_tz, datetime FROM table1 ORDER BY id", conn);
conn.Open();
DataTable table1 = new DataTable();
da.Fill(table1);
foreach (DataRow row in table1.Rows)
{
var id = row["id" ];
var datetime_tz = row["datetime_tz"];
var datetime = row["datetime" ];
Console.WriteLine("id=" + id.ToString());
Console.WriteLine("datetime_tz=" + datetime_tz.ToString() + " Kind=" + ((DateTime)datetime_tz).Kind.ToString());
Console.WriteLine("datetime =" + datetime .ToString() + " Kind=" + ((DateTime)datetime ).Kind.ToString());
Console.WriteLine();
}
conn.Close();
Console.ReadKey();
}
}
}
結果は
id=1
datetime_tz=2018/01/01 0:00:00 Kind=Unspecified
datetime =2018/01/01 0:00:00 Kind=Unspecified
id=2
datetime_tz=2018/01/01 9:00:00 Kind=Unspecified
datetime =2018/01/01 0:00:00 Kind=Unspecified
id=3
datetime_tz=2018/01/01 9:00:00 Kind=Unspecified
datetime =2018/01/01 0:00:00 Kind=Unspecified
となり、DateTimeのKindが全てUnspecifiedとなった。
NpgsqlDataReaderで読み込んだときには
- DATETIME WITH TIME ZONE型はKind=Local
- DATETIME WITHOUT TIME ZONE型はKind=Unspecified
となっていたが、DataTableに読み込むとうまくKindまで設定してくれていない。
今度は、DataTableにデータを入れて、DataAdapter.Updateでデータベースに書き込んでみる。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using Npgsql;
namespace DateTimeOffsetTestNpgsql
{
class Program
{
static void Main(string[] args)
{
DateTime dateTimeLocal = new DateTime(2018, 1, 1, 0, 0, 0, DateTimeKind.Local );
DateTime dateTimeUtc = new DateTime(2018, 1, 1, 0, 0, 0, DateTimeKind.Utc );
DateTime dateTimeUnspecified = new DateTime(2018, 1, 1, 0, 0, 0, DateTimeKind.Unspecified);
NpgsqlConnection conn = new NpgsqlConnection("Server=xxx.xxx.xxx.xxx;User Id=xxxx;Password=xxxx;Database=xxxx;Pooling=false;");
NpgsqlDataAdapter da = new NpgsqlDataAdapter();
da.SelectCommand = new NpgsqlCommand("SELECT id, datetime_tz, datetime FROM table1 ORDER BY id", conn);
da.InsertCommand = new NpgsqlCommand("INSERT INTO table1 (id, datetime_tz, datetime) VALUES (:id, :datetime_tz, :datetime)", conn);
da.InsertCommand.Parameters.Add(new NpgsqlParameter("id" , NpgsqlTypes.NpgsqlDbType.Integer , 0, "id" ));
da.InsertCommand.Parameters.Add(new NpgsqlParameter("datetime_tz", NpgsqlTypes.NpgsqlDbType.TimestampTz, 0, "datetime_tz"));
da.InsertCommand.Parameters.Add(new NpgsqlParameter("datetime" , NpgsqlTypes.NpgsqlDbType.Timestamp , 0, "datetime" ));
DataTable table1 = new DataTable();
table1.Columns.Add(new DataColumn("id" , typeof(int) ));
table1.Columns.Add(new DataColumn("datetime_tz", typeof(DateTime)));
table1.Columns.Add(new DataColumn("datetime" , typeof(DateTime)));
DataRow row1 = table1.NewRow();
row1["id" ] = 1;
row1["datetime_tz"] = dateTimeLocal;
row1["datetime" ] = dateTimeLocal;
table1.Rows.Add(row1);
DataRow row2 = table1.NewRow();
row2["id" ] = 2;
row2["datetime_tz"] = dateTimeUtc;
row2["datetime" ] = dateTimeUtc;
table1.Rows.Add(row2);
DataRow row3 = table1.NewRow();
row3["id" ] = 3;
row3["datetime_tz"] = dateTimeUnspecified;
row3["datetime" ] = dateTimeUnspecified;
table1.Rows.Add(row3);
Console.WriteLine("row1[id ]=" + ((int)row1["id"]).ToString());
Console.WriteLine("row1[datetime_tz]=" + ((DateTime)row1["datetime_tz"]).ToString() + " Kind=" + ((DateTime)row1["datetime_tz"]).Kind.ToString());
Console.WriteLine("row1[datetime ]=" + ((DateTime)row1["datetime" ]).ToString() + " Kind=" + ((DateTime)row1["datetime" ]).Kind.ToString());
Console.WriteLine("row2[id ]=" + ((int)row2["id"]).ToString());
Console.WriteLine("row2[datetime_tz]=" + ((DateTime)row2["datetime_tz"]).ToString() + " Kind=" + ((DateTime)row2["datetime_tz"]).Kind.ToString());
Console.WriteLine("row2[datetime ]=" + ((DateTime)row2["datetime" ]).ToString() + " Kind=" + ((DateTime)row2["datetime" ]).Kind.ToString());
Console.WriteLine("row3[id ]=" + ((int)row3["id"]).ToString());
Console.WriteLine("row3[datetime_tz]=" + ((DateTime)row3["datetime_tz"]).ToString() + " Kind=" + ((DateTime)row3["datetime_tz"]).Kind.ToString());
Console.WriteLine("row3[datetime ]=" + ((DateTime)row3["datetime" ]).ToString() + " Kind=" + ((DateTime)row3["datetime" ]).Kind.ToString());
Console.WriteLine();
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand("DELETE FROM table1", conn))
{
cmd.ExecuteNonQuery();
}
da.Update(table1);
DataTable table2 = table1.Clone();
da.Fill(table2);
foreach (DataRow row in table2.Rows)
{
var id = row["id" ];
var datetime_tz = row["datetime_tz"];
var datetime = row["datetime" ];
Console.WriteLine("id=" + id.ToString());
Console.WriteLine("datetime_tz=" + datetime_tz.ToString() + " Kind=" + ((DateTime)datetime_tz).Kind.ToString());
Console.WriteLine("datetime =" + datetime .ToString() + " Kind=" + ((DateTime)datetime ).Kind.ToString());
Console.WriteLine();
}
conn.Close();
Console.ReadKey();
}
}
}
結果、データベースのテーブル table1 は
id | datetime_tz | datetime |
---|---|---|
1 | 2018-01-01 09:00:00+09 | 2018-01-01 00:00:00 |
2 | 2018-01-01 09:00:00+09 | 2018-01-01 00:00:00 |
3 | 2018-01-01 09:00:00+09 | 2018-01-01 00:00:00 |
となり、実行結果の出力は |
row1[id ]=1
row1[datetime_tz]=2018/01/01 0:00:00 Kind=Unspecified
row1[datetime ]=2018/01/01 0:00:00 Kind=Unspecified
row2[id ]=2
row2[datetime_tz]=2018/01/01 0:00:00 Kind=Unspecified
row2[datetime ]=2018/01/01 0:00:00 Kind=Unspecified
row3[id ]=3
row3[datetime_tz]=2018/01/01 0:00:00 Kind=Unspecified
row3[datetime ]=2018/01/01 0:00:00 Kind=Unspecified
id=1
datetime_tz=2018/01/01 9:00:00 Kind=Unspecified
datetime =2018/01/01 0:00:00 Kind=Unspecified
id=2
datetime_tz=2018/01/01 9:00:00 Kind=Unspecified
datetime =2018/01/01 0:00:00 Kind=Unspecified
id=3
datetime_tz=2018/01/01 9:00:00 Kind=Unspecified
datetime =2018/01/01 0:00:00 Kind=Unspecified
となった。
どうやら、DataTableのDateTime型の列にDateTimeのKindがLocalやUtcのデータを入れても、DataTable側の列はKind=Unspecifiedのままのようだ。
そのため、Npgsql 3.x系以降では前回検証したように、
- Kind=Unspecifiedは、UTCから日本時間に変換されて書き込まれる。
ので、データベースには2018-01-01 09:00:00+09が書き込まれている。
Npgsql 2.x系の場合は
- Kind=Unspecifiedは、日本時間で書き込まれる。
だったので、おそらくデータベースには2018-01-01 00:00:00+09で書き込まれるはずである(あとで検証してみた)。
なんで、DataRowのDateTime型の列にDateTime型の変数を渡したときに、Kindが引き継がれないの?という疑問が残るけど、行毎にKindが異なるとマズイんだろうなーっということは想像できる。
これだとDataTableとDataAdapterを使って、ちゃんと日時データを取り扱えない。。。困った。
※Npgsql 2.x系だとUnspecifiedがLocalと同じ挙動なので、なんとなく思ったように動くけど、3.x以降だとダメじゃん。
途方に暮れながら更に調べてみると、DataColumnにDateTimeModeというプロパティがあることを発見した!
DataColumn.DateTimeMode プロパティ
DataSetDateTime 列挙型
DataTableのDateTime型の列のDateTimeModeをLocalに変更してやればいいのではないか?
ということで、上記検証ソースのtable1の定義後に
table1.Columns["datetime_tz"].DateTimeMode = DataSetDateTime.Local;
を追加して実行してみると、データベースのtable1は
id | datetime_tz | datetime |
---|---|---|
1 | 2018-01-01 00:00:00+09 | 2018-01-01 00:00:00 |
2 | 2018-01-01 09:00:00+09 | 2018-01-01 00:00:00 |
3 | 2018-01-01 00:00:00+09 | 2018-01-01 00:00:00 |
となり、実行結果の出力は |
row1[id ]=1
row1[datetime_tz]=2018/01/01 0:00:00 Kind=Local
row1[datetime ]=2018/01/01 0:00:00 Kind=Unspecified
row2[id ]=2
row2[datetime_tz]=2018/01/01 9:00:00 Kind=Local
row2[datetime ]=2018/01/01 0:00:00 Kind=Unspecified
row3[id ]=3
row3[datetime_tz]=2018/01/01 0:00:00 Kind=Local
row3[datetime ]=2018/01/01 0:00:00 Kind=Unspecified
id=1
datetime_tz=2018/01/01 0:00:00 Kind=Local
datetime =2018/01/01 0:00:00 Kind=Unspecified
id=2
datetime_tz=2018/01/01 9:00:00 Kind=Local
datetime =2018/01/01 0:00:00 Kind=Unspecified
id=3
datetime_tz=2018/01/01 0:00:00 Kind=Local
datetime =2018/01/01 0:00:00 Kind=Unspecified
となり、狙った通りの動作になった。