繁体   English   中英

准确计算经过的年数,考虑闰年,在 Oracle

[英]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 有没有好的解决方案?

这是记录在案的行为

如果date1date2是一个月中的同一天或两个月的最后一天,则结果始终为 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

DB Fiddle 上的演示

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的文档中

MONTHS_BETWEEN返回日期date1date2之间的月数。 如果date1晚于date2 ,则结果为正。 如果date1早于date2 ,则结果是否定的。 如果date1date2是一个月中的同一天或两个月的最后一天,则结果始终为 integer。 否则 Oracle 数据库基于 31 天的月份计算结果的小数部分,并考虑时间组件date1date2的差异。

您可以“修复” 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM