【问题标题】:How can I set the time in postgres in order to test timezone behaviour using RSpec?如何在 postgres 中设置时间以使用 RSpec 测试时区行为?
【发布时间】:2013-12-01 16:09:49
【问题描述】:

我希望能够跨不同时区测试调度程序组件的行为。然而,触发预定行为的功能在 postgres 中使用基于时间的查询:

例如

# find reminders which have not been sent for "today" in the local date
Schedule.where('evening_reminder_last_sent_on_local_date != 
                                     DATE( NOW() AT TIME ZONE time_zone )')

我希望能够在 RSpec 中测试此行为并确保它在一天中正常播放,并且如果我在 12 月 25 日凌晨 1 点 UTC 向日本用户发送提醒,然后在 10 点 UTC,他们的提醒“今天”将显示为 not 已发送(因为在日本是第二天早上 7 点左右)。

但是,为了做到这一点,我需要能够在 postgres 中设置日期时间。这可能吗?

请注意...这不是要破坏 Rails 的时间

挑战在于不要在 Rails 中浪费时间——我知道该怎么做。问题是如何在 Postgres 中设置时间。

【问题讨论】:

    标签: ruby-on-rails postgresql rspec


    【解决方案1】:

    Postgres 使用底层操作系统的日期/时间设置(至少在类 UNIX 系统上)。要暂存各种时间戳,您必须使用date 命令设置操作系统的时间。

    我当然不建议在执行其他任何重要操作的系统上这样做。 cronjobs 可能会运行混乱和其他令人不快的副作用。

    相反,将函数 now() 替换为用户定义的服务器端函数,例如:

    CREATE OR REPLACE FUNCTION now_test()
      RETURNS timestamptz AS $func$SELECT '2013-12-01 12:34'::timestamptz$func$ LANGUAGE SQL;
    

    (上述转换假定会话的当前时区设置。或者,您可以使用文字提供时区或时间偏移量。)
    然后你可以测试你的代码:

    Schedule.where('evening_reminder_last_sent_on_local_date != 
                                 DATE(now_test() AT TIME ZONE time_zone)')
    

    用各种时间戳修改上面的SQL函数就可以了。

    【讨论】:

    • 这是非常好的建议。然而,它为我带来了另一个问题,即如何在 Rails 中管理 postgres 函数。我已将此作为一个单独的问题提出,并很想知道您的想法 - stackoverflow.com/questions/20314724/…
    • @ErwinBrandstetter 不用担心 :)
    【解决方案2】:

    之前的建议确实非常好,但是因为我想用Delorean gem 轻松测试我的查询,所以我想出了这个解决方法:

    Schedule.where('evening_reminder_last_sent_on_local_date != 
                   DATE( ?::timestamp AT TIME ZONE time_zone )', Time.now)
    

    为了方便起见,我采用了前面的示例,但我不得不在 Postgres 中使用 now() 函数来操作时间。我不能只注入我的红宝石时间而不使用::timestamp

    此外,在您的情况下,也许您可​​以使用timestamptz

    【讨论】:

      【解决方案3】:

      我有一个类似的情况,除了我想应用偏移量而不是修改时区。这让我可以将 postgres 对NOW() 的响应与我的应用程序对datetime.now() 的响应同步(通过libfaketime 篡改)。

      我最终得到了一条将now() 重命名为system_now() 的语句,但前提是system_now() 尚不存在:

      DO $$
          DECLARE
          found_func pg_proc%rowtype;
          BEGIN
          SELECT * FROM pg_proc WHERE proname = 'system_now' INTO found_func;
          IF FOUND THEN
              RAISE NOTICE 'DB Already Faked';
          ELSE
              RAISE NOTICE'Faking DB Time';
              ALTER FUNCTION now RENAME TO system_now;
          END IF;
      END $$;
      

      ...另一个重新定义NOW() 以包含偏移量:

      CREATE OR REPLACE FUNCTION now() RETURNS timestamptz
      AS $func$
          SELECT system_now() + INTERVAL '1225288 seconds';
      $func$ LANGUAGE SQL;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多