2010-09-14 24 views
5

Mi aplicación Java debe enviar un correo electrónico a todos los usuarios una vez al día. Me gustaría entregarlo aproximadamente a las 2 a.m. en función de la hora local de cada usuario. Entonces, un usuario en Nueva York recibiría el mensaje a las 2 AM hora de América/New_York. Un usuario de Los Ángeles recibiría el mensaje a las 2 AM América/Los_Angeles. Mi proceso de correo se ejecutará una vez cada hora del día.Realizar una acción diaria en un momento específico según la zona horaria local del usuario

Planeo usar funciones de geolocalización de HTML5 o geolocalización de direcciones IP para determinar automáticamente el huso horario de cada usuario durante el registro. Por supuesto, el usuario puede modificar la zona horaria manualmente si estos valores son incorrectos. Tengo la intención de almacenar la zona horaria seleccionada en el objeto Usuario como una cadena de identificación de zona horaria, como "América/Nueva_York". Esto persistirá en la base de datos como una cadena, pero esto podría cambiarse si es necesario.

Aspectos a considerar:

  • que necesitan para tener en cuenta el horario de verano, por lo que el usuario siempre recibe el correo electrónico a las 2. Esto significa que no puedo simplemente almacenar GMT-8 o compensaciones UTC similares en el objeto de usuario. Parte del año Los Ángeles está en GMT-7, y otras partes está en GMT-8.
  • Another SO question tiene respuestas que sugieren almacenar información de compensación, pero no veo cómo funcionaría ya que los tiempos en diferentes lugares pueden cambiar a lo largo del año. No voy a actualizar todas las zonas horarias de mis objetos de usuario cada vez que ocurra un evento de cambio de zona horaria en algún lugar del mundo.
  • Estoy usando JodaTime en mi aplicación, por lo que puedo aprovecharlo si lo ayuda.
  • Estoy usando Hibernate para consultas de bases de datos y me gustaría encontrar una solución que pueda manejarse en una consulta de hibernación, sin necesidad de procesar cientos de miles de registros de usuario en Java.

Estoy usando las funciones de cron de programación de primavera (a través de Quartz) para ejecutar un método en 2 minutos después de la hora cada hora del día. En este momento, la mayoría de mis usuarios están en América/Los_Angeles, por lo que estoy forzando manualmente que todos los correos se envíen a las 2:02 AM, hora del Pacífico. El siguiente método se ejecuta cada hora a las: 02 horas después de la hora, pero verifica manualmente la hora y solo continúa si la hora actual en Los Ángeles es en las 2:00 horas. Esto significa que los usuarios de otros lugares recibirán el correo electrónico en un horario diferente a las 2 AM.

@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 
} 

Por lo tanto, lo que necesito es llevar a cabo una consulta de base de datos que me dará todos los usuarios que tienen una hora de la hora 2:00, en base a la zona horaria en su objeto Usuario. ¿Alguna idea sobre cómo lograr esto?

Respuesta

2

He pensado más y tengo una posible solución. No lo he implementado todavía, pero creo que debería funcionar. Básicamente, se trata de realizar la siguiente consulta (NOTA: esto es MySQL específico):

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

Esta consulta selecciona todos los miembros que tienen una hora local actual 03 a.m.-3:59 AM de la siguiente manera:

  1. Obtiene la hora actual del servidor usando now(), que es una hora en UTC ya que el servidor de la base de datos está configurado para la hora UTC.
  2. A continuación, convierte ese tiempo con la función convert_tz() en la zona horaria del miembro como se especifica en la columna timezone de la tabla de miembros. Tenga en cuenta que la clase Miembro contiene un campo de zona horaria en el formato "América/Los_Angeles".
  3. A continuación, comprueba qué hora es con la función hour() para ver si es 3, para las 3AM.
  4. La consulta devuelve todos los miembros que cumplen estos requisitos y envía un correo electrónico a cada uno de ellos.

Estoy un poco preocupado por hacer esta conversión de zona horaria y el cálculo de cada miembro en el sistema cada hora. Tendré que probar cosas para ver qué tipo de carga pone en la base de datos, pero probablemente se ahogará con millones o incluso con 10k de usuarios.

Una alternativa que debería aligerar significativamente la carga, pero no es tan elegante es hacer lo siguiente (nota de este código no se ha probado!):

@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 
     } 
    } 
} 

Este código hace esto:

  1. Ejecutar método a las 2 minutos después de la hora, cada hora de cada día
  2. Consulte la base de datos para obtener una lista de todas las zonas horarias únicas en la tabla de miembros.
  3. Para cada zona horaria encontrada, determine si se encuentra actualmente en la hora 3 AM.
  4. Si la zona horaria está en la hora 3 AM, seleccione todos los miembros en esa zona horaria y envíeles un correo electrónico.

Esto agrega un poco de sobrecarga al comienzo de cada hora, pero no requiere el procesamiento por zona horaria por usuario. Lo más probable es que solo haya unas pocas docenas de zonas horarias exclusivas realmente utilizadas por el sistema. Es muy poco probable que tenga miembros en más de 500 zonas horarias.

Una última cosa. En lugar de enviar un correo electrónico durante las 2 AM, lo moví a las 3AM. Esto se debe a que cuando cambia el horario de verano en la primavera, el tiempo cambia de 2 a 3 a.m. Entonces, no hay nada como las 2:02 AM, lo que significa que los correos electrónicos no se enviarán ese día. Además, en el otoño, el sistema podría enviar 2 correos electrónicos por usuario ya que una hora se repite. Necesito averiguar si las zonas horarias fuera de EE. UU. Cambian el horario en diferentes momentos, porque la hora 3 AM podría ser un problema en otro lugar.

Cuestiones relacionadas