🚀

PostgreSQLのTIMESTAMPの値が四捨五入される

2022/01/16に公開

PostgreSQLのTIMESTAMPの秒フィールドに、小数点以下6桁より小さい値を指定すると四捨五入されて想定外の日時が設定される場合がある。
TIMESTAMPの秒フィールドの精度は小数点以下0〜6桁の範囲で指定が可能。

INSERT INTO foo
VALUES (1, TIMESTAMP '2021-12-31 23:59:59')
     , (2, TIMESTAMP '2021-12-31 23:59:59.9')
     , (3, TIMESTAMP '2021-12-31 23:59:59.99')
     , (4, TIMESTAMP '2021-12-31 23:59:59.999')
     , (5, TIMESTAMP '2021-12-31 23:59:59.9999')
     , (6, TIMESTAMP '2021-12-31 23:59:59.99999')
     , (7, TIMESTAMP '2021-12-31 23:59:59.999999')
     , (8, TIMESTAMP '2021-12-31 23:59:59.9999999');
# SELECT * FROM foo;
 id |          foo_time
----+----------------------------
  1 | 2021-12-31 23:59:59
  2 | 2021-12-31 23:59:59.9
  3 | 2021-12-31 23:59:59.99
  4 | 2021-12-31 23:59:59.999
  5 | 2021-12-31 23:59:59.9999
  6 | 2021-12-31 23:59:59.99999
  7 | 2021-12-31 23:59:59.999999
  8 | 2022-01-01 00:00:00  <- 四捨五入されて日付がかわる
(8 rows)

以下、発覚するまでのメモ

TIMESTAMPカラムを持つテーブルを用意

CREATE TABLE foo
(
    id       INTEGER,
    foo_time TIMESTAMP
);

LocalDateとLocalTime.MAXでTIMESTAMPカラムの値を作ってINSERT

LocalDateとLocalTime.MAXを使ってTIMESTAMPカラムの値を生成。
2022-01-01 23:59:59.999999999 のレコードを作ろうとした。
O/R MapperにはjOOQを使用。

val date = LocalDate.of(2022, 1, 1)

dslContext
    .insertInto(FOO, FOO.ID, FOO.FOO_TIME)
    .values(1, date.atTime(LocalTime.MAX))
    .execute()

想定と異なるINSERT結果が返ってくる

INSERT結果を確認するため、SELECTしてみると 2022-01-01 23:59:59.99999999 ではなく、翌日の 2022-01-02 00:00:00 になっていた。

# SELECT * FROM foo;
 id |      foo_time
----+---------------------
  1 | 2022-01-02 00:00:00
(1 row)

Discussion