sql >> データベース >  >> RDS >> PostgreSQL

PostgreSQLのage()関数:異なる月に着陸したときの異なる/予期しない結果

    年齢 timestamptz_ageによって計算されます src / backend / utils / adt / timestamp.cの関数 。コメントによると:

    /* timestamptz_age()
     * Calculate time difference while retaining year/month fields.
     * Note that this does not result in an accurate absolute time span
     *  since year and month are out of context once the arithmetic
     *  is done.
     */
    

    コードは最初に引数をstructpg_tmに変換します 変数tm1 およびtm2 struct pg_tm Cライブラリのstructtmに似ています 、ただし追加のタイムゾーンフィールドがあります)、差を計算します tm フィールドごと。

    age( '2018-07-01'、 '2018-05-20')の場合 、その違いの関連フィールドは次のようになります:

    tm_mday = -19
    tm_mon  =   2
    tm_year =   0
    

    これで、負のフィールドが調整されます。 tm_mdayの場合 、コードは次のようになります:

    while (tm->tm_mday < 0)
    {
        if (dt1 < dt2)
        {
            tm->tm_mday += day_tab[isleap(tm1->tm_year)][tm1->tm_mon - 1];
            tm->tm_mon--;
        }
        else
        {
            tm->tm_mday += day_tab[isleap(tm2->tm_year)][tm2->tm_mon - 1];
            tm->tm_mon--;
        }
    }
    

    dt1> dt2以降 、 else 分岐が行われ、コードは5月の日数(31)を追加し、月を1減らして、最終的に

    になります。
    tm_mday = 12
    tm_mon  =  1
    tm_year =  0
    

    それがあなたが得る結果です。

    一見すると、 tm2-> tm_mon のように見えます 選択するのに適切な月ではありません。左の引数の前の月を取る方がよいでしょう:

    day_tab[isleap(tm1->tm_year)][(tm1->tm_mon + 10) % 12]
    

    しかし、その選択がすべての場合に適しているかどうかはわかりません。いずれにしても、コメントは関数を補償するので、バグと呼ぶことを躊躇します。

    ハッカーのメーリングリストで取り上げることをお勧めします。



    1. EntityFrameworkでのMariaDBの使用

    2. Javaでバイト配列をBlobに変換する最も簡単な方法

    3. MySQLクエリ-現在の日付に基づいてレコードを取得します

    4. Googleマップのオーバーレイシェイプをデータベースに保存するにはどうすればよいですか?