For each of my users, I store a tzid
which I convert to a DateTimeZone
holding information about their local time zone.
I want to send the user a daily email at 8 AM local time; if 8 AM is ambiguous for whatever reason like a daylight savings shift, I just need to pick one of the 8 AM; I don't care which.
My job runs hourly, and I have an Instant
containing the last run time of the job, and another Instant
containing the next run time of the job.
Given these two Instant
called previousRun
and nextRun
, and the DateTimeZone
called tz
how would I determine whether the localTime
called eightAM
falls between the bounds of this job run? If it does, I need to send the user an email.
Given these two Instant called previousRun and nextRun, and the DateTimeZone called tz how would I determine whether the localTime called eightAM falls between the bounds of this job run?
Doing this in a general way is somewhat tricky, I think. However, if you can rely on the time you want being well away from midnight and your job will run every hour (so you don't need to consider what happens if it hasn't run between midnight and 8am, for example) I think you could do something like this:
public static bool ShouldSendEmail(Instant previousRun, Instant nextRun,
DateTimeZone zone)
{
// Find the instant at which we should send the email for the day containing
// the last run.
LocalDate date = previousRun.InZone(zone).Date;
LocalDateTime dateTime = date + new LocalTime(8, 0);
Instant instant = dateTime.InZoneLeniently(zone).ToInstant();
// Check whether that's between the last instant and the next one.
return previousRun <= instant && instant < nextRun;
}
You can check the docs for InZoneLeniently
to check exactly what result it will give, but it sounds like you don't really mind: this will still send exactly one email a day, in an hour which contains 8am.
I haven't parameterized this by the time of day precisely because it would be much harder to handle the general case where the time of day could be near midnight.
EDIT: If you can store the "next date to send" then it's easy - and you don't need the previousRun
part:
public static bool ShouldSendEmail(LocalDateTime nextDate, Instant nextRun,
DateTimeZone zone, LocalTime timeOfDay)
{
LocalDateTime nextEmailLocal = nextDate + timeOfDay;
Instant nextEmailInstant = nextDateTime.InZoneLeniently(zone).ToInstant();
return nextRun > nextEmailInstant;
}
Basically that says, "Work out when we next want to send an email - and if the next run will be later than that, we should send it now."
See more on this question at Stackoverflow