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

Postgres: Is Date between now and interval for EVERY birthday

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

用户模型

我的时间间隔 = 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;

Returns

空的。 Postgres 假设省略年份我实际上是指第 1 年。

我想要的

此查询 return 所有生日在该区间内的用户,与年份无关。

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

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

这个答案有问题

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

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

我根据当前年份和用户的生日月份和日期生成了一个新日期,然后查看它是否在接下来的 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)

在这里,我在我的项目中实现了 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);
}