Postgres make_date处理异常

时间:2015-10-14 09:11:04

标签: postgresql

所以我存储了用户的生日和月份。我发送生日优惠,这些优惠以不同的间隔到期,即“生日后的天数”。

我已经成功构建了公式(下面是它的一部分)但是我遇到了这个问题:

('2015-10-10'::date >= make_date(2015, users.birth_month, users.birth_day) ...

如果用户于2月29日出生,则make_date会针对无效年份(ERROR: date field value out of range: 2015-02-29)提出异常

我怎样才能优雅地处理这个? (我有其他方法可以解决这个问题,但是他们要求我为闰年提供特殊待遇)

2 个答案:

答案 0 :(得分:1)

这可以通过结合使用make_dateinterval PostgreSQL功能(有关更多信息,请参见https://www.postgresql.org/docs/9.6/functions-datetime.html)来实现。

技巧是确保make_date中的日期始终有效。通过使用每月的第一天,然后使用interval(减去1天)来增加所需的天数(同时避免出现ERROR: date field value out of range错误),可以轻松实现这一目标。

通过创建一个小表users来举例说明:-

create table users ( 
  id serial primary key,
  birth_day integer,
  birth_month integer
);

...并在其中填充一些数据...

insert into users (birth_day, birth_month) values
                  (1,         2),
                  (28,        2),
                  (29,        2),
                  (30,        2),
                  (31,        2);

如果年份为2015,则可以运行以下查询:-

select birth_day,
       birth_month,
       make_date(2015, birth_month, 1) + (birth_day  - 1) as birthday
  from users;

╔═══════════╦═════════════╦══════════════╗
║ birth_day ║ birth_month ║   birthday   ║
╠═══════════╬═════════════╬══════════════╣
║         1 ║           2 ║ "2015-02-01" ║
║        28 ║           2 ║ "2015-02-28" ║
║        29 ║           2 ║ "2015-03-01" ║    <---- wrapped to march 1st
║        30 ║           2 ║ "2015-03-02" ║
║        31 ║           2 ║ "2015-03-03" ║
╚═══════════╩═════════════╩══════════════╝

但是,如果年份为2016(le年),则为:-

select birth_day,
       birth_month,
       make_date(2016, birth_month, 1) + (birth_day  - 1) as birthday
  from users;

╔═══════════╦═════════════╦══════════════╗
║ birth_day ║ birth_month ║   birthday   ║
╠═══════════╬═════════════╬══════════════╣
║         1 ║           2 ║ "2016-02-01" ║
║        28 ║           2 ║ "2016-02-28" ║
║        29 ║           2 ║ "2016-02-29" ║
║        30 ║           2 ║ "2016-03-01" ║    <---- wrapped to march 1st
║        31 ║           2 ║ "2016-03-02" ║
╚═══════════╩═════════════╩══════════════╝

感谢a_horse_with_no_name指出如果需要的话,您不需要使用interval-只需添加即可,即+ ((birth_day - 1) || ' days')::interval可以简化为+ (birth_day - 1)

答案 1 :(得分:0)

好的,我已经为02-29岁的生日添加了一个特例。这可以解决我的问题,但我非常希望听到其他建议。

('2015-10-10'::date >= 
   case when users.birth_month = 2 and users.birth_day = 29 make_date(2015,2, 28)
        else make_date(2015, users.birth_month, users.birth_day)
   end
)

在这种情况下,用户将失去一天(我可以通过3月1日让他们赢得一天),但你明白了这一点=)