Sql 在Postgres函数中显示下一个半小时的时间点

Sql 在Postgres函数中显示下一个半小时的时间点,sql,postgresql,postgresql-9.1,Sql,Postgresql,Postgresql 9.1,我试图创建一个函数来显示下一个半小时的时间 所以当当前时间是13:40时,我希望它显示14:00而不是13:30 我所创造的,在最接近的半小时内完成工作,而不是在未来最接近的时间内: CREATE OR REPLACE FUNCTION round_timestamp( ts timestamptz ,round_secs int ) RETURNS timestamptz AS $$ DECLARE

我试图创建一个函数来显示下一个半小时的时间

所以当当前时间是13:40时,我希望它显示14:00而不是13:30

我所创造的,在最接近的半小时内完成工作,而不是在未来最接近的时间内:

CREATE OR REPLACE FUNCTION round_timestamp(
         ts timestamptz
         ,round_secs int
        ) RETURNS timestamptz AS $$
        DECLARE
                _mystamp timestamp;
                _round_secs decimal;
        BEGIN
    _round_secs := round_secs::decimal;
    _mystamp := timestamptz 'epoch' 
            + ROUND((EXTRACT(EPOCH FROM ts))::int / _round_secs) * _round_secs
            * INTERVAL '1 second';
    RETURN _mystamp;
END; $$ LANGUAGE plpgsql IMMUTABLE;
关于如何使此功能显示未来最接近的半小时间隔,您有什么想法吗?

您可以分别使用向上和向下取整

如果您的函数如此简单,那么您也可以用
语言SQL
编写它,这使得它更简单/可读(并且解析器也可以对其进行规划):


简单地说,在向上缩放到时间戳之前,向向下舍入的历元添加1:

CREATE OR REPLACE FUNCTION round_timestamp(ts timestamptz, round_secs int)
    RETURNS timestamptz AS $$
BEGIN
    RETURN timestamptz 'epoch' 
            + (ROUND((EXTRACT(EPOCH FROM ts))::int / round_secs) + 1) * round_secs
            * INTERVAL '1 second';
END; $$ LANGUAGE plpgsql IMMUTABLE;

不需要使用局部变量。

为了避免在使用历元和浮点算法时出错,您可以使用日期算法,另外还有一个好处,就是可以更清楚地了解发生了什么:

create or replace function round_tstz(ts timestamptz)
  returns timestamptz
as $$
  select date_trunc('hour', $1) +
         -- what hour will it be in 30 min?
         case date_trunc('hour', $1 + interval '30 min')
         -- the same: round to next half hour
         when date_trunc('hour', $1) then interval '30 min'
         -- not the same: round to next hour
         else interval '1 hour'
         end;
$$ language sql stable;

# select now()::timestamptz(0);
          now           
------------------------
 2014-12-05 14:34:30+01
(1 row)

# select round_tstz(now()), round_tstz(now() + interval '30 min');
       round_tstz       |       round_tstz       
------------------------+------------------------
 2014-12-05 15:00:00+01 | 2014-12-05 15:30:00+01
(1 row)

您需要将
X
minutes添加到时间戳中的当前分钟数
M
,其中:

X=30-M%30-30*(M%30)=0)::int

将除法的余数从30减去30,然后仅当余数为0时,再减去30,以便保留:00和:30的值,并且不进行四舍五入

例如,如果时间是14:11,那么M=11,您需要添加19分钟才能到达14:30(30-11%30=19),对于14:47,M=47,您需要添加13分钟才能到达15:00(30-47%30=13)。该规则的唯一例外是余数为0的情况,即14:30、15:00等小时数,这就是公式中的最后一项起作用的地方——减去30*的乘积(0或1,取决于除法的余数为30)


考虑到时间戳本身是双精度的,我怀疑这种方法会有很大的不同。与简单的历元提取和(通常)硬件优化舍入相比,由于
timestamp\u trunc()
中的所有逻辑,它的性能可能更差。@Patrick:可能是。。。我可以想象,timestamp_trunc()中的逻辑是C语言,它只会被计算两次,因为优化器可以跳过其中一个函数调用,因为它是稳定的,它比不调用它(包括除法等等)而产生的所有浮点运算要快一点。无论如何,我认为这一点在一天结束时并不重要:我认为重要的是,我所建议的方法是可读性和可理解性的好几倍。但这只是我自己的看法。:-)阅读你的例子,这只是你一天的开始。。。你在凌晨4:26穿这样的衣服干什么???撇开工作时间不谈,timestamp_trunc()的所有变体都以一个大的case语句开始,以评估感兴趣的时间段<代码>示例中的“小时”。在OP的问题上,最好是在不考虑PG的情况下直接讨论第二个问题。@Patrick:呵呵。我的笔记本电脑服务器配置错误:-无论如何,我还没有检查源代码,所以只能相信你的话。我仍然认为最好是编写代码,就好像下一个家伙是一个拿着斧头的精神病患者,他知道你住在哪里;尽管我试图对提取划时代之类的东西持开放态度,但我发现我自己的建议,或者Kouper刚刚发布的建议,让事情变得更加直观。是的,代码清晰性很重要。不过有一个复杂的问题:虽然OP提到了半小时的间隔,但他的代码示例使用了任意秒数来取整。所以如果他想要1864秒,那么你的代码就不能精确到31分4秒。但这对我来说只是一个小问题。如果我当前的时间是15:25,这个函数会使它变成15:30吗?在你的服务器上运行它。如果您为舍入秒指定1800,那么它将是。因为
时间戳具有微秒分辨率,所以将“舍入”值舍入的问题高度依赖于应用程序,我猜这与此无关。
create or replace function round_tstz(ts timestamptz)
  returns timestamptz
as $$
  select date_trunc('hour', $1) +
         -- what hour will it be in 30 min?
         case date_trunc('hour', $1 + interval '30 min')
         -- the same: round to next half hour
         when date_trunc('hour', $1) then interval '30 min'
         -- not the same: round to next hour
         else interval '1 hour'
         end;
$$ language sql stable;

# select now()::timestamptz(0);
          now           
------------------------
 2014-12-05 14:34:30+01
(1 row)

# select round_tstz(now()), round_tstz(now() + interval '30 min');
       round_tstz       |       round_tstz       
------------------------+------------------------
 2014-12-05 15:00:00+01 | 2014-12-05 15:30:00+01
(1 row)
CREATE FUNCTION round_up(timestamptz) RETURNS timestamptz AS $$
  SELECT
    $1 + '1 minute'::interval * (
      30 - (EXTRACT('minute' FROM $1)::int % 30)
         - 30 * ((EXTRACT('minute' FROM $1)::int % 30)=0)::int
    );
$$ LANGUAGE sql IMMUTABLE;