PostgreSQLのTIMESTAMP WITH TIME ZONE型のデータをクライアントのタイムゾーンで扱いたい

# 確認したいこと
例えば、クライアントが日本にあってデータベース・サーバーが台北にあるとき、TIMESTAMP WITH TIME ZONE型の列に日本のタイムゾーンでデータを取り扱いたい。

## 準備
データベース・サーバーのタイムゾーンがAsia/Taipei担っていることを確認。
### 確認のためのテーブルを生成

``` create_table.sql
CREATE TABLE table1
(
    id          INTEGER
  , date_tz     TIMESTAMP WITH TIME ZONE
  , description TEXT
);
```

## TIMESTAMP WITH TIME ZONE型の列にTO_DATE()でdate型のデータを入れる

``` insert_data1.sql
INSERT INTO table1 (id, date_tz, description) VALUES (
  1
, TO_DATE('2016-01-01','YYYY-MM-DD')
, 'TO_DATE(''2016-01-01'')'
);
```

確認すると

```
SELECT id, date_tz, description FROM table1 ORDER BY id;
 id |        date_tz         |           description
----+------------------------+----------------------------------
  1 | 2016-01-01 00:00:00+08 | TO_DATE('2016-01-01')
(1 行)
```

となり、サーバーのタイムゾーンでデータが入っている。
でも、ここで入れたいのは、日本時間の2016年1月1日0時であって、台北時間の2016年1月1日0時ではない。

## タイムゾーンを指定してデータを入れてみる
AT TIME ZONEを使って、タイムゾーンを指定してデータを入れてみた。

``` insert_data2.sql
INSERT INTO table1 (id, date_tz, description) VALUES (
  2
, TO_DATE('2016-01-01','YYYY-MM-DD') AT TIME ZONE 'Asia/Tokyo'
, 'TO_DATE(''2016-01-01'') AT TIME ZONE ''Asia/Tokyo'''
);
```

確認すると

```
SELECT id, date_tz, description FROM table1 ORDER BY id;
 id |        date_tz         |                 description
----+------------------------+-------------------------------------------------
  1 | 2016-01-01 00:00:00+08 | TO_DATE('2016-01-01')
  2 | 2016-01-01 01:00:00+08 | TO_DATE('2016-01-01') AT TIME ZONE 'Asia/Tokyo'
(2 行)
```

id=2のdate_tzは、台北時間の2016年1月1日1時になってしまった。

やりたかったことは、台北(Asia/Taipei)にあるデータベースサーバーのTIMESTAMP WITH TIME ZONE型の列に、日本時間の2016年1月1日0時のデータを書き込みたかったので、id=2にdate_tzには
```2016-12-31 23:00:00+08```
が入って欲しかったのだけど。

なぜこうなったのか？
まず、```TO_DATE('2016-01-01') AT TIME ZONE 'Asia/Tokyo'```がどうなるのか。

```
SELECT TO_DATE('2016-01-01','YYYY-MM-DD') AT TIME ZONE 'Asia/Tokyo';
      timezone
---------------------
 2016-01-01 01:00:00
(1 行)
```

結果の後ろに+09とか+08が付いてないので、きっとこれはTIMESTAMP WITHOUT TIME ZONE型になっているのではないか。
TIMESTAMP WITHOUT TIME ZONE型をTIMESTAMP WITH TIME ZONE型にCASTされてdate_tz列に入れられるのだろうから、

```
SELECT (TO_DATE('2016-01-01','YYYY-MM-DD') AT TIME ZONE 'Asia/Tokyo')::TIMESTAMP WITH TIME ZONE;
        timezone
------------------------
 2016-01-01 01:00:00+08
(1 行)
```

この結果がdate_tz列に入れられているはず。
insert_data2.sqlの結果とも一致しているので、きっとそうだろう。

つまり、```TO_DATE('2016-01-01','YYYY-MM-DD') AT TIME ZONE 'Asia/Tokyo'```は、台北時間2016年1月1日0時の日本時間は？ということで、結果として2016年1月1日1時という答えが返ってきている。
この結果がAsia/TaipeiのTIMESTAMP WITH TIME ZONE型にCASTされるので、
```2016-01-01 01:00:00+08```
という最終結果になる。

なので、**このやり方は間違い**。

## たぶんこうすればいいのかな？
文字型の'2016-01-01 Asia/Tokyo'をTIMESTAMP WITH TIME ZONE型にCASTしてやると良さそう。

```
SELECT '2016-01-01 Asia/Tokyo'::TIMESTAMP WITH TIME ZONE;
      timestamptz
------------------------
 2015-12-31 23:00:00+08
(1 行)
```

これでINSERTしてみると

``` insert_data3.sql
INSERT INTO table1 (id, date_tz, description) VALUES (
  3
, '2016-01-01 Asia/Tokyo'
, '''2016-01-01 Asia/Tokyo'''
);
```

結果は

```
SELECT id, date_tz, description FROM table1 ORDER BY id;
 id |        date_tz         |                   description
----+------------------------+-------------------------------------------------
  1 | 2016-01-01 00:00:00+08 | TO_DATE('2016-01-01')
  2 | 2016-01-01 01:00:00+08 | TO_DATE('2016-01-01') AT TIME ZONE 'Asia/Tokyo'
  3 | 2015-12-31 23:00:00+08 | '2016-01-01 Asia/Tokyo'
(3 行)
```

id=3のdate_tzは、台北時間の2015年12月31日23時となり、狙った結果になった。

# 結論
タイムゾーンの異なるデータベースサーバーにTIMESTAMP WITH TIME ZONE型のデータを入れるときには、AT TIME ZONEで指定するのではなく、文字型にタイムゾーンの情報を加えてからTIMESTAMP WITH TIME ZONE型にCASTする。

# でも...
最終的にはADO.NETでDataAdapterを使ってやりたいので、文字型にタイムゾーンを付けるやり方はちょっとダメかな。もっと良い方法を考えてみる。