如何返回每天应在指定时间通知约会的用户列表

时间:2018-08-17 15:30:25

标签: ruby-on-rails postgresql

我有一个用户列表,需要根据他们的时区在每天的不同时间收到通知。

所以我基本上有2个表:Schedule和User

Schedule
-id
-scheduled_time

Users
-id
-schedule_id
-timezone

因此,可能会有9AM的时间表。 与此9AM时间表相关联的用户,他们的时区也设置为UTC -4或UTC -5。

我将提供一项服务,该服务每隔x分钟轮询一次以查询数据库。

这是我可以直接在PostgreSQL中执行的查询,还是该应用程序依赖于我使用的ORM?

2 个答案:

答案 0 :(得分:0)

解析一天中的时间戳听起来很烦人,我建议您从Rails模型中进行解析。

如果您对从数据库中执行此操作一无所知,请参见以下问题:Convert a UTC timezone in postgresql to EST (local time),这是另一篇文章,描述了如何使用Postgres中的时区:http://blog.untrod.com/2016/08/actually-understanding-timezones-in-postgresql.html

但是我的感觉是,如果使用ActiveSupport :: TimeWithZone数学,以便将每个用户与给定通知他们的给定UTC时间相关联,您将获得一个更简单,更轻松的解决方案。

示例:

时间表X在世界标准时间16:00运行,时间表Y在世界标准时间17:00运行。

用户A在UTC-0700(PDT)中,用户B在UTC-0600(山地)中。

如果B希望在上午10:00收到通知,请在您的应用程序中进行数学运算,以便将它们与Schedule X关联。 如果A希望在10:00 AM收到通知,请在您的应用程序中进行数学运算,以便将它们与计划Y关联。

让我知道您是否需要帮助。提示:

答案 1 :(得分:0)

  

我将提供一项服务,该服务每隔x分钟轮询一次以查询数据库。

     

这是我可以直接在Postgresql中执行的查询吗??

是的,可以。想象使用PostgreSQL服务器时间(函数now()current_timestamp)为

SELECT u.*  
FROM schedule s INNER JOIN users u 
  ON u.schedule_id=s.id 
WHERE now()::time=(s.scheduled_time+u.timezone)

但是,正如我们将看到的,“每x分钟” 无法与Schedule表同步。有两种同步方式:

  1. 使用<=并用布尔值的日标记更新用户表(每天24小时后进行清理以重新计划)。没有风险。

  2. =round(time,x) here一起使用...当您的“每x分钟” 假设不完善时,风险可能很小。

让我们模拟所有事情:

CREATE TABLE schedule (id serial PRIMARY KEY,  scheduled_time time);
INSERT INTO schedule (scheduled_time) VALUES 
   (now()), ('9:00AM'::time), ('16:30'::time);

CREATE TABLE users (
  id serial,
  schedule_id int references schedule(id),
  timezone interval  -- see pg_timezone_names.utc_offset
);
INSERT INTO users (schedule_id,timezone) VALUES
 (2, '00:00:00'::interval), (2, '-04:00:00'::interval), (1, '-05:00:00'::interval),
 (1, '-05:00:00'::interval), (1, '-04:00:00'::interval), (2, '-05:00:00'::interval);

CREATE VIEW vw_user_scheduled AS
SELECT u.*, s.scheduled_time, s.scheduled_time+u.timezone AS ref_time
    FROM schedule s INNER JOIN users u 
      ON u.schedule_id=s.id 
;

示例vw_user_scheduled是:

 id | schedule_id | timezone  | scheduled_time  |    ref_time     
----+-------------+-----------+-----------------+-----------------
  1 |           2 | 00:00:00  | 09:00:00        | 09:00:00
  2 |           2 | -04:00:00 | 09:00:00        | 05:00:00
  3 |           1 | -05:00:00 | 20:54:27.898109 | 15:54:27.898109
  4 |           1 | -05:00:00 | 20:54:27.898109 | 15:54:27.898109
  5 |           1 | -04:00:00 | 20:54:27.898109 | 16:54:27.898109
  6 |           2 | -05:00:00 | 09:00:00        | 04:00:00

假设您采用舍入解决方案和x=5(将每5分钟轮询一次以查询数据库的服务),则schedule_time 9AM(服务器时间)的用户将不是集合{id = 1,id = {2}和id = 6}(如scheduled_time列所示,但只有用户id = 1正确的ref_time。

SELECT * from vw_user_scheduled 
WHERE round_minutes(ref_time,5)=round_minutes(now(),5)

cited round time适用于时间数据类型。

CREATE FUNCTION round_minutes(time, integer) RETURNS time AS $$ 
  SELECT 
     date_trunc('hour', $1)::time 
     +  (cast(($2::varchar||' min') as interval) 
         * round( 
            (date_part('minute',$1)::float + date_part('second',$1)/ 60.)::float 
            / $2::float
         ))
$$ LANGUAGE SQL IMMUTABLE STRICT;

PS:由于问题不显示您的SQL表,因此有很多方法可以在表上模拟时间和时区格式。