如何得到两个日期之间的日期/月份/年份的差异?

我正在寻找一种在 PostgreSQL 中实现 SQLServer-函数 datediff的方法。也就是说,此函数返回在指定开始日期和结束日期之间跨越的指定日期部件边界的计数(作为有符号整数值)。

datediff(dd, '2010-04-01', '2012-03-05') = 704 // 704 changes of day in this interval
datediff(mm, '2010-04-01', '2012-03-05') = 23  // 23 changes of month
datediff(yy, '2010-04-01', '2012-03-05') = 2   // 2 changes of year

我知道我可以简单地用减法做“ dd”,但是对于另外两个有什么想法吗?

286143 次浏览
SELECT
AGE('2012-03-05', '2010-04-01'),
DATE_PART('year', AGE('2012-03-05', '2010-04-01')) AS years,
DATE_PART('month', AGE('2012-03-05', '2010-04-01')) AS months,
DATE_PART('day', AGE('2012-03-05', '2010-04-01')) AS days;

这将给你 满了年,月,天... 之间的两个日期:

          age          | years | months | days
-----------------------+-------+--------+------
1 year 11 mons 4 days |     1 |     11 |    4

更详细的 Datediff 信息

简单地减去它们:

SELECT ('2015-01-12'::date - '2015-01-01'::date) AS days;

结果是:

 days
------
11
SELECT date_part ('year', f) * 12
+ date_part ('month', f)
FROM age ('2015-06-12'::DATE, '2014-12-01'::DATE) f

结果: 6

几乎与您需要的功能相同(基于 atiruz 的回答,从 给你缩短了 UDF 的版本)

CREATE OR REPLACE FUNCTION datediff(type VARCHAR, date_from DATE, date_to DATE) RETURNS INTEGER LANGUAGE plpgsql
AS
$$
DECLARE age INTERVAL;
BEGIN
CASE type
WHEN 'year' THEN
RETURN date_part('year', date_to) - date_part('year', date_from);
WHEN 'month' THEN
age := age(date_to, date_from);
RETURN date_part('year', age) * 12 + date_part('month', age);
ELSE
RETURN (date_to - date_from)::int;
END CASE;
END;
$$;

用法:

/* Get months count between two dates */
SELECT datediff('month', '2015-02-14'::date, '2016-01-03'::date);
/* Result: 10 */


/* Get years count between two dates */
SELECT datediff('year', '2015-02-14'::date, '2016-01-03'::date);
/* Result: 1 */


/* Get days count between two dates */
SELECT datediff('day', '2015-02-14'::date, '2016-01-03'::date);
/* Result: 323 */


/* Get months count between specified and current date */
SELECT datediff('month', '2015-02-14'::date, NOW()::date);
/* Result: 47 */

我花了一些时间寻找最佳答案,我想我找到了。

这个 sql 将给出两个日期之间的天数作为 integer:

SELECT
(EXTRACT(epoch from age('2017-6-15', now())) / 86400)::int

. . 当今天(2017-3-28)播出的时候,给我提供了:

?column?
------------
77

关于公认答案的误解:

select age('2010-04-01', '2012-03-05'),
date_part('year',age('2010-04-01', '2012-03-05')),
date_part('month',age('2010-04-01', '2012-03-05')),
date_part('day',age('2010-04-01', '2012-03-05'));

..是您将得到日期字符串各部分之间的字面差,而不是两个日期之间的时间量。

即:

Age(interval)=-1 years -11 mons -4 days;

Years(double precision)=-1;

Months(double precision)=-11;

Days(double precision)=-4;

下面是一个具有 output. psql (10.1,server 9.5.10)的完整示例。

你得到58,而不是低于30的价值。
删除 age ()函数,解决了前文提到的问题。

drop table t;
create table t(
d1 date
);


insert into t values(current_date - interval '58 day');


select d1
, current_timestamp - d1::timestamp date_diff
, date_part('day', current_timestamp - d1::timestamp)
from t;


d1     |        date_diff        | date_part
------------+-------------------------+-----------
2018-05-21 | 58 days 21:41:07.992731 |        58

这个问题充满了误解。首先让我们充分理解这个问题。询问者希望得到与运行 微软 SQL 服务器功能 DATEDIFF ( datepart , startdate , enddate )时相同的结果,其中 datepartddmmyy

这一功能的定义如下:

此函数返回指定的起始日期和终止日期之间的指定日期部件边界的计数(作为有符号整数值)。

这意味着有多少天的边界,月的边界,或年的边界,被跨越。而不是他们之间隔了多少天,多少月,多少年。这就是为什么 datediff(yy, '2010-04-01', '2012-03-05')是2而不是1。这两个日期之间的时间间隔不到两年,也就是说只过去了整整一年,但是从2010年到2011年,以及从2011年到2012年,跨越了两年的界限。

以下是我正确复制逻辑的最佳尝试。

-- datediff(dd`, '2010-04-01', '2012-03-05') = 704 // 704 changes of day in this interval
select ('2012-03-05'::date - '2010-04-01'::date );
-- 704 changes of day


-- datediff(mm, '2010-04-01', '2012-03-05') = 23  // 23 changes of month
select (date_part('year', '2012-03-05'::date) - date_part('year', '2010-04-01'::date)) * 12 + date_part('month', '2012-03-05'::date) - date_part('month', '2010-04-01'::date)
-- 23 changes of month


-- datediff(yy, '2010-04-01', '2012-03-05') = 2   // 2 changes of year
select date_part('year', '2012-03-05'::date) - date_part('year', '2010-04-01'::date);
-- 2 changes of year

我想详细阐述 Riki _ tiki _ tavi 的答案,并得到那里的数据。我已经创建了一个 datediff 函数,它几乎完成 sql 服务器所做的所有工作。这样我们就可以考虑任何单位。

create function datediff(units character varying, start_t timestamp without time zone, end_t timestamp without time zone) returns integer
language plpgsql
as
$$
DECLARE
diff_interval INTERVAL;
diff INT = 0;
years_diff INT = 0;
BEGIN
IF units IN ('yy', 'yyyy', 'year', 'mm', 'm', 'month') THEN
years_diff = DATE_PART('year', end_t) - DATE_PART('year', start_t);


IF units IN ('yy', 'yyyy', 'year') THEN
-- SQL Server does not count full years passed (only difference between year parts)
RETURN years_diff;
ELSE
-- If end month is less than start month it will subtracted
RETURN years_diff * 12 + (DATE_PART('month', end_t) - DATE_PART('month', start_t));
END IF;
END IF;


-- Minus operator returns interval 'DDD days HH:MI:SS'
diff_interval = end_t - start_t;


diff = diff + DATE_PART('day', diff_interval);


IF units IN ('wk', 'ww', 'week') THEN
diff = diff/7;
RETURN diff;
END IF;


IF units IN ('dd', 'd', 'day') THEN
RETURN diff;
END IF;


diff = diff * 24 + DATE_PART('hour', diff_interval);


IF units IN ('hh', 'hour') THEN
RETURN diff;
END IF;


diff = diff * 60 + DATE_PART('minute', diff_interval);


IF units IN ('mi', 'n', 'minute') THEN
RETURN diff;
END IF;


diff = diff * 60 + DATE_PART('second', diff_interval);


RETURN diff;
END;
$$;

还有一个解决方案,针对“年龄”差异的版本:

SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('year', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x

    2

(1行)

这几个月都是同样的把戏:

SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('month', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x

   23

(1行)

在实际查询中,可以使用一些时间戳序列按小时/天/周/等分组,而不是生成 _ 系列。

这个 'count(distinct(date_trunc('month', ts)))'可以在选择的“左边”右边使用:

SELECT sum(a - b)/count(distinct(date_trunc('month', c))) FROM d

为了简短起见,我在这里使用了 gener_ Series ()。

@ WebWanderer 的回答非常接近使用 SQL 服务器的 DateDiff,但是不准确。这是因为使用了 age ()函数。

例如,“2019-07-29”和“2020-06-25”之间的天数应该返回332,但是,使用 age ()函数它将返回327。因为 age ()返回’10 mon 27天’,它将每个月视为30天,这是不正确的。

您应该使用时间戳来获得准确的结果。 例如:。

ceil((select extract(epoch from (current_date::timestamp - <your_date>::timestamp)) / 86400))