sql - PostgreSQL age() 函数 : different/unexpected results when landing in dfferent month

标签 sql postgresql date-arithmetic

今天,我在运行此查询时遇到了 PostgreSQL 9.6 中无法解释的结果:

SELECT age('2018-06-30','2018-05-19') AS one,
       age('2018-07-01','2018-05-20') AS two; 

两列的预期结果:1 mon 11 days。但是,仅在 2018-05-19 到 2018-06-30 期间,我得到了我的期望,而在 2018-05-20 到 2018-07-01 期间,我会多得到一天:1星期一 12 天

我不明白为什么会这样,据我了解,2018-05-20 2018-07-01 之间只是 1 mon 11 days 的间隔,Postgres 结果在这里是错误的。

我找不到任何关于 PostgreSQL-age(timestamp,timestamp) 函数究竟如何工作的深入信息。但是,我假设该函数执行类似的操作:从开始日期开始,以月份为单位向前移动,直到到达结束月份。从那里,转到结束日期的那一天。总结月份和日期。

因此,根据我的理解,这就是我的情况下应该发生的事情(抱歉,这里太冗长了,但我觉得这是必要的):

从 2018-05-19 开始。前进一个月。登陆时间为 2018-06-19。向前走 N 天,直到到达 2018-06-30:

1 day: 20
2 days: 21
3 days: 22
4 days: 23
5 days: 24
6 days: 25
7 days: 26
8 days: 27
9 days: 28
10 days: 29
11 days: 30

= 1 month 11 days.

对于2018-05-20和2018-07-01之间的时间应该是差不多的:

从 2018-05-20 开始。前进一个月。登陆时间为 2018-06-20。向前走 N 天,直到到达 2018-07-01:

1 day: 21
2 days: 22
3 days: 23
4 days: 24
5 days: 25
6 days: 26
7 days: 27
8 days: 28
9 days: 29
10 days: 30
11 days: 1

= 1 month 11 days.

这是我的错误还是 PostgreSQL 的错误之一? 是否有替代函数/算法按照我描述/预期的方式工作?

最佳答案

age是通过src/backend/utils/adt/timestamp.c中的timestamptz_age函数计算的。评论说:

/* 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.
 */

代码首先将参数转换为struct pg_tm变量tm1tm2(struct pg_tm类似于C 库的 struct tm,但有额外的时区字段),然后计算每个字段的差异 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]

但我不能说这种选择是否在所有情况下都更好,而且无论如何评论都会保护函数,所以我不愿将其称为错误。

您可能想通过黑客邮件列表处理它。

关于sql - PostgreSQL age() 函数 : different/unexpected results when landing in dfferent month,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51167239/

相关文章:

java - 缓存查询结果

sql - oracle中有没有类似mysql中group_concat的函数?

python - Flask-ReSTLess order_by相关模型

php - 获取当年的最后一天作为日期

sql - 在java中使用sql DATEADD函数

MySQL如何填充范围内缺失的日期?

sql - 当可以按 1 排序时,为什么我不能按 1 排序?

java - SQL : Vehicule gets added to DataBase but not his options

sql - 高效插入,对 Postgres 中的大表进行重复检查

postgresql - `pg_ls_dir` 部分目录可以查询,其他目录不可以