[英]Accurately calculate number of years elapsed, accounting for leap years, in Oracle
在 Oracle 中,我需要计算自给定日期以来经过的年数的从 1 开始的值,考虑闰年。
最初的计算没有考虑闰年。 它是
CEIL ((SYSDATE- prog_start_dt) / 365))
然后我决定使用MONTHS_BETWEEN
,但我的新计算中有一个错误:
CEIL(MONTHS_BETWEEN(SYSDATE, prog_start_dt) / 12)
错误是,如果我在prog_start_date
之后正好 1 年,无论时间如何,该值都会少 1。 前任。:
sysdate = 2020-07-01 15:30:00
prog_start_dt = 2019-07-01 00:00:00
--> 1. CEIL ((SYSDATE- prog_start_dt) / 365)) --> Result: 2
--> 2. CEIL(MONTHS_BETWEEN(SYSDATE, prog_start_dt) / 12) --> Result 1
正确的结果是2
。 所以我需要顶级公式的行为,但要考虑闰年,显然不会是365
。 有没有好的解决方案?
这是记录在案的行为:
如果
date1
和date2
是一个月中的同一天或两个月的最后一天,则结果始终为 integer。
如果你真的想解决这个问题,那么一个选择是添加一些逻辑来处理这种特定情况:当比较的日期属于同一月份和日期时,然后检查时间部分以查看年份是否已经过去:
ceil(months_between(sysdate, prog_start_dt) / 12)
+ case when to_char(sysdate, 'mm-dd') = to_char(prog_start_dt, 'mm-dd')
and to_char(sysdate, 'hh24:mi') > to_char(prog_start_dt, 'hh24:mi')
then 1
else 0
end
with t as (
select
to_date('2020-07-01 15:30:00', 'yyyy-mm-dd hh24:mi:ss') date1,
to_date('2019-07-01 00:00:00' , 'yyyy-mm-dd hh24:mi:ss') date2
from dual
)
select
ceil(months_between(date1, date2) / 12)
+ case when to_char(date1, 'mm-dd') = to_char(date2, 'mm-dd')
and to_char(date1, 'hh24:mi') > to_char(date2, 'hh24:mi')
then 1
else 0
end year_diff
from t
| YEAR_DIFF | | --------: | | 2 |
MONTHS_BETWEEN
返回日期date1
和date2
之间的月数。 如果date1
晚于date2
,则结果为正。 如果date1
早于date2
,则结果是否定的。 如果date1
和date2
是一个月中的同一天或两个月的最后一天,则结果始终为 integer。 否则 Oracle 数据库基于 31 天的月份计算结果的小数部分,并考虑时间组件date1
和date2
的差异。
您可以“修复” MONTHS_BETWEEN
,因此当月份的日期相同(或者如果它们都是该月的最后一天)时,它并不总是使用 integer 值:
MONTHS_BETWEEN( end_date, start_date )
+
CASE
WHEN EXTRACT( DAY FROM start_date ) = EXTRACT( DAY FROM end_date )
OR ( start_date = LAST_DAY( start_date ) AND end_date = LAST_DAY( end_date ) )
THEN ( end_date
- ADD_MONTHS( start_date, MONTHS_BETWEEN( end_date, start_date ) )
) / 31
ELSE 0
END
因此,您的查询将是:
SELECT start_date,
end_date,
CEIL(
(
MONTHS_BETWEEN( end_date, start_date )
+
CASE
WHEN EXTRACT( DAY FROM start_date ) = EXTRACT( DAY FROM end_date )
OR ( start_date = LAST_DAY( start_date ) AND end_date = LAST_DAY( end_date ) )
THEN ( end_date
- ADD_MONTHS( start_date, MONTHS_BETWEEN( end_date, start_date ) )
) / 31
ELSE 0
END
)
/ 12
) AS years
FROM test_data;
对于一些测试数据:
CREATE TABLE test_data ( start_date, end_date ) AS
SELECT DATE '2019-07-01',
DATE '2020-07-01' + INTERVAL '15:30:00' HOUR TO SECOND
FROM DUAL
UNION ALL
SELECT DATE '2019-07-31',
DATE '2020-02-29' + INTERVAL '15:30:00' HOUR TO SECOND
FROM DUAL
UNION ALL
SELECT DATE '2019-02-28',
DATE '2020-02-29' + INTERVAL '15:30:00' HOUR TO SECOND
FROM DUAL;
这输出:
START_DATE | END_DATE | 年份:----------------- |:------------------ | ----: 2019-07-01 00:00:00 | 2020-07-01 15:30:00 | 2 2019-07-31 00:00:00 | 2020-02-29 15:30:00 | 1 2019-02-28 00:00:00 | 2020-02-29 15:30:00 | 2
db<> 在这里摆弄
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.