在当地时区上午9:00向用户发送时事通讯



我正在使用以下宝石:

  • timezone
  • tzinfo

我正在尝试在user的时区发送一份时事通讯,以便他们在9:00:00收到时事通讯不是我的服务器所在的太平洋标准时间09:00:00。

我按名称存储时区,比如"太平洋时间(美国和加拿大)"。每个用户都有一个列user.time_zone,其中存储了他们的特定时区。

我已经建立了一个job,它每小时检查一次可以接收时事通讯的user(即当地时间为上午9:00的user)。

class NewsletterTimezoneJob < ActiveJob::Base
  def self.perform
      users = User.all.gets_newsletter
      users.each do |user|
      d = Date.current
      t = "09:00:00 -0700"
      t = t.to_time
      newsletter_sendtime = DateTime.new(d.year, d.month, d.day, t.hour, t.min, t.sec, t.zone)
      newsletter_sendtime = newsletter_sendtime.to_s
      if user.is_in_time_around(newsletter_sendtime) 
        NewsletterMailer.send_mailer(user, newsletter_sendtime).deliver_later
      end
    end
  end
end

我在user上构建了一个方法,尝试查看用户的本地时间是否与newsletter_sendtime匹配。

我添加了一个+/-5分钟的窗口来解释服务器速度慢的原因。

User.rb:

def is_in_time_around(timeofday)
   timeofday = timeofday.to_time
   user_zoned =  timeofday.in_time_zone(self.time_zone)
   user_zoned = user_zoned.strftime("%r").to_time
   timeofday = timeofday.strftime("%r").to_time
   if user_zoned >= (timeofday - 5.minutes) && user_zoned <= (timeofday + 5.minutes)
    true
   end
 end

jobmethod每小时运行一次,并每小时发送给不同时区的不同用户;然而,它目前只发送给"太平洋时间(美国和加拿大)"的用户,每个小时从不发送给任何其他"用户"。

我发现您的方法存在一些问题:代码似乎过于复杂,您很可能(正如Jon在上面的评论中所注意到的)只与太平洋时间上午9点进行比较。此外,在您的方法中,您需要遍历所有用户并比较每个用户的时区——当用户表增长时,这将导致糟糕的性能。

我建议一种完全相反的方法,简单地说就是:查找当前本地时间在上午9点左右的所有时区名称,然后选择这些时区中的用户

如果您正在使用Rails,那么使用内置方法一切皆有可能。有了这个答案的帮助,你可以在给定的时区(如太平洋时间)获得给定的时间(如上午9点),如下所示:

ActiveSupport::TimeZone["Pacific Time (US & Canada)"].parse("09:00:00")
# => Tue, 29 Mar 2016 09:00:00 PDT -07:00

TimeZone模块还提供了all方法来获取所有定义的时区。我们需要将所有时区的本地时间(四舍五入到小时)与所需时间(上午9点)进行比较,以选择当前时间为上午9点的区域。首先,让我们将当地时间四舍五入为小时:

Time.zone.now
# => Tue, 29 Mar 2016 13:46:25 CEST +02:00
Time.zone.parse("#{Time.zone.now.hour}:00:00")
# => Tue, 29 Mar 2016 13:00:00 CEST +02:00

接下来,将所有时区的四舍五入本地时间与上午9点进行比较,并获得匹配的时区名称:

local_time = Time.zone.parse("#{Time.zone.now.hour}:00:00")
ActiveSupport::TimeZone.all.select { |tz| tz.parse("09:00:00") == local_time }.map(&:name)
# => ["Greenland", "Mid-Atlantic"]

这一特殊结果表明,虽然布拉格的时间是13小时(下午1点),但例如,格陵兰地区的时间是9小时(上午9点)。

有了这些,剩下的就很容易了:

class NewsletterTimezoneJob < ActiveJob::Base
  def self.perform
    User.all.gets_newsletter.with_time_zone(zones_with_local_time("09:00")).each do |user|
      NewsletterMailer.send_mailer(user).deliver_later
      # for simplicity, I removed the `newsletter_sendtime` parameter from the mailer call
    end
  end
end

其中zones_with_local_time是在任何地方定义的辅助函数,返回本地时间为给定时间的时区:

# if defined in the NewsletterTimezoneJob, it should be a class method
def self.zones_with_local_time(hhmm)
  local_time = Time.zone.parse("#{Time.zone.now.hour}:00:00")
  ActiveSupport::TimeZone.all.select { |tz| tz.parse("#{hhmm}:00") == local_time }.map(&:name)
end

with_time_zone只是User模型中作用于时区条件的一个范围:

# app/models/user.rb
scope :with_time_zone,
    ->(zones) { where("time_zone in (?)", zones) }

现在,您所要做的就是设置一个cron作业,使其每小时运行一次,例如一小时的第一分钟。

如果它帮助了某人,下面可能会帮助

将作业安排在前一天晚上8点(UTC)运行,因为在新西兰和其他一些时区,UTC晚上8点是上午9点。UTC晚上8点是任何时区的上午9点。

# group time_zones by utc_offset (In hours)
ActiveSupport::TimeZone.all.group_by { |tz| tz.utc_offset/3600.0 }.each do |utc_offset_in_hrs, time_zones|
  User.where(time_zone: time_zones).each do |user|
    # Schedule the email as per time_zone, using delayed_job's run_at param
    # Using +13, as that is the highest/first timezone to have 9AM
    NewsletterMailer.send_mailer(user).deliver_later(wait_until: 13.0 - utc_offset_in_hrs)
  end
end

相关内容

  • 没有找到相关文章

最新更新