Given a DateTimeZone and two Instants, determine if a LocalTime falls between the two Instants

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.

Jon Skeet
people
quotationmark

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."

people

See more on this question at Stackoverflow