5

我的 Java 应用程序需要每天向所有用户发送一封电子邮件。我想根据每个用户的当地时间在凌晨 2 点左右送达。因此,纽约的用户会在美国/纽约时间凌晨 2 点收到消息。洛杉矶的用户将在美国/洛杉矶时间凌晨 2 点收到该消息。我的邮寄过程每天每小时运行一次。

我计划在注册期间使用 HTML5 地理位置功能或 IP 地址地理位置来自动确定每个用户的时区。当然,如果这些值不正确,用户可以手动修改时区。我打算将选定的时区作为时区 ID 字符串存储在 User 对象中,例如“America/New_York”。这将作为字符串保存到数据库中,但如有必要,可以更改。

需要考虑的事项:

  • 我需要考虑夏令时,所以用户总是在凌晨 2 点左右收到电子邮件。这意味着我不能只在用户对象中存储 GMT-8 或类似的 UTC 偏移量。洛杉矶一年中的一部分时间是 GMT-7,而其他部分时间是 GMT-8。
  • 另一个 SO question有建议存储偏移信息的答案,但我不知道这将如何工作,因为不同地方的时间在一年中可能会发生变化。每当世界某处发生时区更改事件时,我不会更新我所有的用户对象时区。
  • 我在我的应用程序中使用 JodaTime,所以如果有帮助,我可以利用它。
  • 我正在使用 Hibernate 进行数据库查询,并希望找到一个可以在 Hibernate 查询中处理的解决方案,而无需在 Java 中处理数十万条用户记录。

我正在使用 Spring Scheduling cron 功能(通过 Quartz)在一天中的每小时 2 分钟运行一个方法。现在我的大多数用户都在 America/Los_Angeles,所以我手动强制所有邮件在太平洋时间凌晨 2:02 发送。以下方法在每小时 02 点每小时运行一次,但它会手动检查时间,并且仅当洛杉矶的当前时间在 2:00 时才会继续。这意味着其他地方的用户将在与凌晨 2 点不同的时间收到电子邮件。

@Scheduled(cron="0 2 * * * *")
public void sendDailyReportEmail()  {
    DateTime now = new DateTime(DateTimeZone.forID("America/Los_Angeles"));
    if (now.getHourOfDay() != 2) {
        log.info("Not 2AM pacific, so skipping email reports");
        return;
    }
    // send email reports
}

因此,我需要执行一个数据库查询,该查询将根据用户对象中的时区为我提供所有在凌晨 2:00 时具有本地时间的用户。关于如何做到这一点的任何想法?

4

1 回答 1

3

我对此进行了更多思考,并有一个可能的解决方案。我还没有实现它,但我认为它应该可以工作。基本上,它涉及执行以下查询(注意:这是特定于 MySQL 的):

select * from members where hour(convert_tz(now(),'UTC',timezone)) = 3;

此查询通过执行以下操作选择当前本地时间介于凌晨 3 点到 3:59 之间的所有成员:

  1. 它使用 获取当前服务器时间now(),这是 UTC 时间,因为数据库服务器配置为 UTC 时间。
  2. convert_tz()然后,它使用函数将该时间转换timezone为成员表列中指定的成员时区。请注意,Member 类包含格式为“America/Los_Angeles”的时区字段。
  3. 接下来,它使用hour()函数检查它是3几点钟,以查看它是否是凌晨 3 点。
  4. 然后查询返回满足这些要求的所有成员并向他们每个人发送电子邮件。

我有点担心每小时为系统中的每个成员进行时区转换和计算。我将不得不进行测试以查看它对数据库施加的负载类型,但它可能会被数百万甚至一万的用户所扼杀。

应该显着减轻负载但不是很优雅的替代方法是执行以下操作(注意此代码未经测试!):

@Scheduled(cron="0 2 * * * *")
public void sendDailyReportEmail()  {

    Query query = session.createQuery(
        "select distinct m.timezone from Member m");
    List<String> timezones = query.list();

    for (String timezone : timezones) {
        DateTime now = new DateTime(DateTimeZone.forID(timezone));
        if (now.getHourOfDay() == 3) {
            Query query2 = session.createQuery(
                "from Member where timezone = :zone");
            query2.setParameter("zone", timezone);
            List<Member> members = query2.list();
            // send email reports
        }
    }
}

此代码执行此操作:

  1. 在每小时的 2 分钟,每天的每个小时执行方法
  2. 在数据库中查询成员表中所有唯一时区的列表。
  3. 对于找到的每个时区,确定当前是否处于凌晨 3 点。
  4. 如果时区位于凌晨 3 点,请选择该时区中的所有成员并向他们发送电子邮件。

这会在每小时开始时增加一点额外开销,但不需要对每个用户进行时区处理。系统实际使用的唯一时区很有可能只有几十个。我不太可能拥有所有 500 多个时区的成员。

最后一件事。我没有在凌晨 2 点发送电子邮件,而是将其移至凌晨 3 点。这是因为当夏令时在春季发生变化时,时间会从凌晨 2 点变为凌晨 3 点。所以没有凌晨 2:02 这样的事情,这意味着当天不会发送电子邮件。此外,在秋季,系统可以为每个用户发送 2 封电子邮件,因为一个小时会不断重复。我需要了解非美国时区是否在不同时间更改时间,因为凌晨 3 点可能是其他地方的问题。

于 2010-12-03T12:57:45.313 回答