Postgres:从现在到每个生日的间隔之间的日期

时间:2018-06-13 15:39:00

标签: postgresql postgresql-9.6

我希望能够返回从现在到指定时间间隔过生日的用户。

用户模型

  • name(string)
  • 生日(约会)

我的时间间隔 = 30天

我的数据集

|---------------------|------------------|------------------|
|      id             |     name         |     birthday     |
|---------------------|------------------|------------------|
|          1          |        Tim       |   27/06/1994     |
|---------------------|------------------|------------------|

我的尝试

SELECT * FROM User where User.birthday BETWEEN NOW()::timestamp AND to_char(NOW() + interval '30 days','MM-DD')::timestamp;

返回

空。 Postgres假设通过省略我实际上意味着第1年的年份。

我想要什么

此查询返回生日所在的所有用户,无论年份如何。

尝试回答(非常感谢@Mureinik)

SELECT *
FROM   user
WHERE  birthday BETWEEN TO_CHAR(NOW(), 'MM-DD') AND
TO_CHAR(NOW() + INTERVAL '30 days','MM-DD')

此答案的问题

  • 如果明年30天后,这将无效。
  • 如果年份是闰年,这个间隔不需要是31天吗?

3 个答案:

答案 0 :(得分:2)

我根据当前年份和用户的生日月份和日期生成了一个新日期,然后查看是否在接下来的30天内。

select *
from user
where date(date_part('year', current_date)||'-'||date_part('month', birthday)||'-'||date_part('day', birthday))
    between current_date and current_date + interval '30 days';

例如:

# select * from user;
 id | name  |  birthday
----+-------+------------
  1 | Tim   | 1994-06-27
  2 | Steve | 1982-06-23
  3 | Dave  | 1980-07-29
(3 rows)

# select *
from user
where date(date_part('year', current_date)||'-'||date_part('month', birthday)||'-'||date_part('day', birthday))
    between current_date and current_date + interval '30 days';
 id | name  |  birthday
----+-------+------------
  1 | Tim   | 1994-06-27
  2 | Steve | 1982-06-23
(2 rows)

答案 1 :(得分:0)

您可以将between的参数转换为字符串并使用词典比较。由于格式是固定宽度,因此应该没问题:

SELECT *
FROM   user
WHERE  birthday BETWEEN TO_CHAR(NOW(), 'MM-DD') AND
                        TO_CHAR(NOW() + INTERVAL '30 days','MM-DD')

答案 2 :(得分:0)

这里,我在支持 QueryDsl 的项目中实现了什么:

private static final DateTimeFormatter DTF_MM_dd = DateTimeFormatter.ofPattern("MM-dd");

protected BooleanExpression betweenLocalDatesIgnoringYear(DatePath<LocalDate> localDate, LocalDate from, LocalDate to) {
    if (from == null && to == null) {
        return Expressions.asBoolean(true).isTrue();
    }
    if (Objects.equals(from, to)) {
        return eqLocalDateIgnoringYear(localDate, from);
    }
    Set<String> MM_DDs = new HashSet<>();
    for (LocalDate date = (from != null ? from : LocalDate.now());
         date.isBefore(to != null ? to.plusDays(1) : from.plusMonths(1).plusDays(1));
         date = date.plusDays(1)) {

        MM_DDs.add(DTF_MM_dd.format(date));
    }
    if (MM_DDs.contains("02-28")) {
        MM_DDs.add("02-29");
    }
    if (MM_DDs.isEmpty()) {
        return Expressions.asBoolean(true).isTrue();
    }
    return localDateIgnoringYearIn(localDate, MM_DDs.stream().toArray(String[]::new));
}

protected BooleanExpression eqLocalDateIgnoringYear(DatePath<LocalDate> pathLocalDate, LocalDate localDate) {
    if (localDate == null) {
        return Expressions.asBoolean(true).isTrue();
    }
    return localDateIgnoringYearIn(pathLocalDate, DTF_MM_dd.format(localDate));
}

private BooleanExpression localDateIgnoringYearIn(DatePath<LocalDate> pathLocalDate, String... values) {
    return Expressions.stringTemplate("FUNCTION('to_char', {0},'MM-DD')", pathLocalDate).in(values);
}