我有两个日期格式为无时区时间戳。
我想比较它们并获得它们之间的月份数值:
select age(NOW(), '2012-03-24 14:44:55.454041+03')
给出:
4 years 9 mons 2 days 21:00:27.165482
这里的诀窍是我需要将此结果转换为一个月的值。
所以:
要将YEARS
转换为Months
:
select EXTRACT(YEAR FROM age) * 12 + EXTRACT(MONTH FROM age)
FROM age(NOW(), '2012-06-24 14:44:55.454041+03') AS t(age)
我得到57
4*12+9
。
我的问题是我不知道如何转换日子。
在上面的例子中,我需要在几个月内将'2 days'
转换为它的值。
'2 days'
不是0
个月!
在30天的月份中,15天是0.5个月。
我该怎么做?
最终结果应为57.something
答案 0 :(得分:3)
您可以通过以下方式进行粗略估算:
select (extract(epoch from timestamptz '2012-06-24 14:44:55.454041+03')
- extract(epoch from timestamptz '2017-03-27 00:00:00+03'))
/ extract(epoch from interval '30.44 days') rough_estimation
(您可以使用extract(epoch from interval '1 month')
除以进行更粗略的估算。)
原始公式的问题在于它旨在提供两个日期之间的完整月份差异。如果您想要计算天数,则会出现一个有趣的问题:在您的示例中,结果应为57 months
和2 days 21:00:27.165482
。但是在2 days 21:00:27.165482
部分计算在哪个月?平均长度的月份(30.44 days
)?如果您想要准确,请注意,在您的示例中,差异实际上仅为56 months
,加上7 days
中的2012-06
差不多30
天)和27 days
2017-03
(31
天)。你应该问自己的问题:是否真的值得一个先进的公式,它考虑两个范围内的每个月的天数?
编辑:为了完整性,这是一个功能,可以考虑两个范围:
create or replace function abs_month_diff(timestamptz, timestamptz)
returns numeric
language sql
stable
as $func$
select extract(year from age)::numeric * 12 + extract(month from age)::numeric
+ (extract(epoch from (lt + interval '1 month' - l))::numeric / extract(epoch from (lt + interval '1 month' - lt))::numeric)
+ (extract(epoch from (g - gt))::numeric / extract(epoch from (gt + interval '1 month' - gt))::numeric)
- case when gt <= l or lt = l then 1 else 0 end
from least($1, $2) l,
greatest($1, $2) g,
date_trunc('month', l) lt,
date_trunc('month', g) gt,
age(gt, l)
$func$;
(注意:如果您使用timestamp
代替timestamptz
,则此函数为immutable
而非stable
。就像date_trunc
函数一样。)< / p>
所以:
select age('2017-03-27 00:00:00+03', '2012-06-24 14:44:55.454041+03'),
abs_month_diff('2017-03-27 00:00:00+03', '2012-06-24 14:44:55.454041+03');
将产生:
age | abs_month_diff
---------------------------------------+-------------------------
4 years 9 mons 2 days 09:15:04.545959 | 57.05138456751051843959
http://rextester.com/QLABV31257(过时)
编辑:功能已更正,以便在差异少于一个月时生成准确的结果。
见f.ex:
set time zone 'utc';
select abs_month_diff('2017-02-27 00:00:00+03', '2017-02-24 00:00:00+03'), 3.0 / 28,
abs_month_diff('2017-03-27 00:00:00+03', '2017-03-24 00:00:00+03'), 3.0 / 31,
abs_month_diff('2017-04-27 00:00:00+03', '2017-04-24 00:00:00+03'), 3.0 / 30,
abs_month_diff('2017-02-27 00:00:00+00', '2017-03-27 00:00:00+00'), 2.0 / 28 + 26.0 / 31;
http://rextester.com/TIYQC5325(过时)
编辑2 :此功能基于以下公式,计算一个月的长度:
select (mon + interval '1 month' - mon)
from date_trunc('month', now()) mon
这甚至会考虑DST更改。 F.ex.在我的国家/地区昨天有一个DST更改(2017-03-26
),所以今天(2017-03-27
)上述查询报告:30 days 23:00:00
。
编辑3 :功能再次得到纠正(感谢@Jonathan注意到边缘情况的边缘情况)。
答案 1 :(得分:1)
这应该做:
select (EXTRACT(YEAR FROM age) * 12 + EXTRACT(MONTH FROM age) + EXTRACT(DAY FROM age) / 30)::numeric
FROM age(NOW(), '2012-06-24 14:44:55.454041+03') AS t(age)
您还可以添加ROUND()
以使其更漂亮:
select ROUND((EXTRACT(YEAR FROM age) * 12 + EXTRACT(MONTH FROM age) + EXTRACT(DAY FROM age) / 30)::numeric,2) as Months
FROM age(NOW(), '2012-06-24 14:44:55.454041+03') AS t(age)
答案 2 :(得分:1)
您可以使用与此类似的内容获得近似值:
SELECT A, B*12+C-1+E/30 MONTHSBETWEEN
FROM (
SELECT current_timestamp A, EXTRACT(YEAR FROM current_timestamp) B, EXTRACT(MONTH FROM current_timestamp) C, EXTRACT(DAYS FROM current_timestamp) E
) X;
用这样的东西或更好的精度:
SELECT A, B*12+C-1+E/ DATE_PART('days',
DATE_TRUNC('month', A)
+ '1 MONTH'::INTERVAL
- '1 DAY'::INTERVAL
) MONTHSBETWEEN
FROM (
SELECT current_timestamp A, EXTRACT(YEAR FROM current_timestamp) B, EXTRACT(MONTH FROM current_timestamp) C, EXTRACT(DAYS FROM current_timestamp) E
) X;