My Java application needs to send an email out to all users once per day. I'd like to deliver it at approximately 2AM based on each user's local time. So a user in New York would get the message at 2AM America/New_York time. A user in Los Angeles would get the message at 2AM America/Los_Angeles time. My mailing process would run once each hour of the day.
I plan to use HTML5 geo-location features or IP Address geo-locating to automatically determine each user's time zone during sign up. Of course, the user can modify the timezone manually if these values are incorrect. I intend to store the selected timezone in the User object as a Timezone ID string, such as "America/New_York". This will persist to the database as a string, but this could be changed if necessary.
Things to consider:
I'm using Spring Scheduling cron features (via Quartz) to run a method at 2 minutes past the hour every hour of the day. Right now most of my users are in America/Los_Angeles, so I'm manually forcing all mail to be sent at 2:02AM Pacific time. The following method runs every hour at :02 past the hour, but it manually checks the time and only proceeds if the current time in Los Angeles is in the 2:00 hour. This means users in other places will get the email at a different time than 2AM.
@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
}
So, what I need is to perform a database query that will give me all users that have a local time in the 2:00AM hour, based on the time zone in their User object. Any thoughts on how to accomplish this?
I've given this more thought and have a possible solution. I haven't implemented it yet, but I think it should work. Basically, it involves performing the following query (NOTE: this is MySQL specific):
select * from members where hour(convert_tz(now(),'UTC',timezone)) = 3;
This query selects all members that have a current local time between 3AM and 3:59AM by doing the following:
now()
, which is a time in UTC since the database server is configured for UTC time. convert_tz()
function to the timezone of the member as specified in the timezone
column of the members table. Note that the Member class contains a timezone field in the format "America/Los_Angeles".hour()
function to see if it is 3
, for 3AM.I'm a little concerned about doing this timezone conversion and calculation for every member in the system every hour. I'll have to test things to see what kind of load it puts on the database, but it will probably choke with millions or even 10k's of users.
An alternative that should significantly lighten the load, but isn't quite as elegant is to do the following (NOTE this code is UNTESTED!):
@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
}
}
}
This code does this:
This adds a little extra overhead at the beginning of each hour, but requires no per-user timezone processing. Chances are that there will only be a few dozen unique time zones actually used by the system. It's very unlikely that I'll have members in all 500+ timezones.
One last thing. Instead of sending email during the 2AM hour, I moved it to 3AM. This is because when daylight savings time changes in the spring, time changes from 2AM to 3AM. So there is no such thing as 2:02AM, meaning emails wouldn't get sent that day. Furthermore, in the fall, the system could send out 2 emails per user since an hour repeats itself. I need to find out if non-USA timezones change time at different times, because the 3AM hour might be a problem elsewhere.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With