data/timestamp型関数の仕様変更二題

# はじめに
にゃーん
この記事は、[PostgreSQL 10全部ぬこ Advent Calendar 2017](https://qiita.com/advent-calendar/2017/pg10-nuko-only) の16日目のエントリです。
今日は軽めのネタで。

# make_date()
PostgreSQLにはmake_date()というSQL関数がある。

```postgres-console
postgres=# \df make_date 
List of functions
-[ RECORD 1 ]-------+-----------------------------------------
Schema              | pg_catalog
Name                | make_date
Result data type    | date
Argument data types | year integer, month integer, day integer
Type                | normal
```
第1引数に整数で年、第2引数に整数で月、第3引数に整数で日を与えるとdate型を生成してくれるという関数だ。

## PostgreSQL 9.6まで
PostgreSQL 9.6までは、年には正の整数しか指定できなかった。

```postgres-console
postgres=# SELECT make_date(2017, 12, 16);
 make_date  
------------
 2017-12-16
(1 row)
```
年に負数や0を指定すると、範囲外というエラーになる。

```postgres-console
postgres=# SELECT make_date(-1, 12, 16);
ERROR:  date field value out of range: -1-12-16
postgres=# SELECT make_date(0, 12, 16);
ERROR:  date field value out of range: 0-12-16
```

## PostgreSQL 10から

「なんで`make_date()`で紀元前の年が生成できねえんじゃ、ゴルァ！」と古代史に関するデータベースを作ろうとしてエラーになったから改造した・・・かどうかは不明だが、PostgreSQL 10からは`make_date()`に、年に負の整数値を指定可能になった。負の整数値を与えた場合には(デフォルトのロケールだと)"BC"表記がつくようだ。

```postgres-console
postgres=# SELECT make_date(-1, 12, 16);
   make_date   
---------------
 0001-12-16 BC
(1 row)
```

ただし、0については、PostgreSQL 10でもエラーになる。**西暦0年は存在しないからだ。**

```postgres-console
postgres=# SELECT make_date(0, 12, 16);
ERROR:  date field value out of range: 0-12-16
```

## おまけ
`make_date()`に与える年の正整数の上限っていくつなのか？というと、それはdate型の上限によって制限されている。date型は5,874,897年までは入力可能っぽい。太陽系が滅亡するまでの数十億年というスケールの年入力には使えないけど、数百万年程度の期間であれば使用には十分だろう。

![space_solar_system.png](https://qiita-image-store.s3.amazonaws.com/0/32626/d38a5e2c-5db0-0c8c-b26c-6e352668f989.png)


# to_timestamp()とto_date()
これらの関数は文字どおり、テキストで表現されたタイムスタンプや日時をtimestamp型やdate型に変更してくれるものだ。

## PostgreSQL 9.6まで
しかし、この関数、意外とチェックがぬるかったりしていた。
例えば、12月32日なんて記述を許容してしまう。

```postgres-console
postgres=# SELECT to_date('2017-12-32','YYYY-MM-DD');
  to_date   
------------
 2018-01-01
(1 row)

postgres=# SELECT to_timestamp('2017-12-32 00:00:00', 'YYYY-MM-DD');
      to_timestamp      
------------------------
 2018-01-01 00:00:00+09
(1 row)
```
誤った閏日を指定しても、3/1にマッピングしてしまう。

```postgres-console
postgres=# SELECT to_date('2018-02-29','YYYY-MM-DD');
  to_date   
------------
 2018-03-01
(1 row)
```

## PostgreSQL 10から
to_date(), to_timestamp()の日付の範囲チェックを厳密にするようになった。

```postgres-console
postgres=# SELECT to_date('2017-12-32','YYYY-MM-DD');
ERROR:  date/time field value out of range: "2017-12-32"
postgres=# SELECT to_timestamp('2017-12-32 00:00:00', 'YYYY-MM-DD');
ERROR:  date/time field value out of range: "2017-12-32 00:00:00"
```

閏日もきちんとチェックするようだ。

```postgres-console
postgres=# SELECT to_date('2018-02-29','YYYY-MM-DD');
ERROR:  date/time field value out of range: "2018-02-29"
postgres=# SELECT to_date('2020-02-29','YYYY-MM-DD');
  to_date   
------------
 2020-02-29
(1 row)
```

# おわりに

* make_date()で紀元前の年入力が可能になった。
* to_date()とto_timestamp()で暦上、誤った指定を厳密にチェックするようになった。

# 参考：該当するリリースノート
本エントリに関連するPostgreSQL 10リリースノートの記載です。

E.2.3.6. Functions

> * Allow make_date() to interpret negative years as BC years (Álvaro Herrera)
> * Make to_timestamp() and to_date() reject out-of-range input fields (Artur Zakirov)