Recurring jobs between dates

Hi all. Is it possible to setup recurring jobs that only run between certain dates. For example:

  1. Every 5 mins starting the 1st December 2016 until 25th December
  2. Every 2 hours starting 1st December but with no end date

I have considered creating a schedule job that creates the recurring job on the right date, and then another scheduled job that kills it on another specified date. But that does seem like a bit of an over kill and could get messy. Plus if the setup schedule or date change for any reason that I would now have to update these two job handling jobs.

Thanks in advance for any help.

Cron syntax used in Hangfire doesn’t allow to specify a year, so you cannot setup an exact start/end date. But you still can set the time range with a custom attribute for recurring jobs, and make a job filter preventing them from being executed before/after specified dates.

Thanks for the quick reply.

Can you let me know what you mean by “custom attribute for recurring jobs”. Can’t see anything along these lines in the documentation.

In regards to the job filter, I take it that I would create my own and implement the OnPerforming method, check the current date and time and if not within the custom attributes that specify the start and end, then prevent execution.

Sorry hangfire is new to me so just want to be sure before I start working on this.

Thanks again.

Yep, you got is right. It would be something like:

public class DateIntervalAttribute : JobFilterAttribute, IClientFilter, IServerFilter
{
    private readonly DateTime? _notBefore, _notAfter;

    public DateIntervalAttribute(string notBefore = null, string notAfter = null)
    {
        _notBefore = string.IsNullOrEmpty(notBefore) ? (DateTime?)null : DateTime.Parse(notBefore, CultureInfo.InvariantCulture);
        _notAfter = string.IsNullOrEmpty(notAfter) ? (DateTime?)null : DateTime.Parse(notAfter, CultureInfo.InvariantCulture);
    }

    public void OnCreating(CreatingContext filterContext)
    {
        if (_notBefore.HasValue && _notBefore.Value > DateTime.Now)
        {
            filterContext.Canceled = true;
        }
        else if (_notAfter.HasValue && _notAfter.Value < DateTime.Now)
        {
            filterContext.Canceled = true;
            
            // delete recurring job definition after expiration
            if (filterContext.Parameters.HasKey("RecurringJobId"))
            {
                string recurringJobId = (string) filterContext.Parameters["RecurringJobId"];
                RecurringJob.RemoveIfExists(recurringJobId);
            }
        }
    }

    public void OnCreated(CreatedContext filterContext)
    {
    }

    public void OnPerforming(PerformingContext filterContext)
    {
        if (_notBefore.HasValue && _notBefore.Value > DateTime.Now)
        {
            filterContext.Canceled = true;
        }
        else if (_notAfter.HasValue && _notAfter.Value < DateTime.Now)
        {
            filterContext.Canceled = true;
        }
    }

    public void OnPerformed(PerformedContext filterContext)
    {
    }
}

Then just apply this attribute to your job method:

[DateInterval("2016-12-01", "2016-12-25")]
public void JobMethod()
{
   // do work
}
2 Likes

Thanks @pieceofsummer for the very useful example here.

I am trying to do something similar, but I would like to be able to specify a different notBefore and notAfter for each recurring job I set up. My initial idea was to handle this within the job method itself, passing the dates in as arguments to this method.

This would look something like this:

public void JobMethod(DateTime? notBefore, DateTime? notAfter, PerformContext context)
{
    if (notBefore.HasValue && notBefore.Value > DateTime.Now)
    {
        return;
    }

    if (notAfter.HasValue && notAfter.Value < DateTime.Now)
    {
        // Can we get the recurringJobId at this point so we can remove the job?
        RecurringJob.RemoveIfExists(recurringJobId);
        return;
    }

    // do work
}

The code that creates the recurring job would then look something like this:

string recurringJobId = Guid.NewGuid().ToString();
DateTime? notBefore = new DateTime(2021, 1, 1);
DateTime? notAfter = new DateTime(2022, 1, 1);
string cronExpression = "0 9 1 * *";

_recurringJobManager.AddOrUpdate<JobHandler>(
    recurringJobId,
    j => j.JobMethod(notBefore, notAfter, null),
    cronExpression);

However, I can’t see how to obtain the RecurringJobId from within the job method. Is it possible to get this from PerformContext?

I could just pass this in to JobMethod as another argument, but it would be cleaner to obtain this from the context.

More fundamentally, do you see any other issues with removing the recurring job from within the job method itself, rather than one of the interception methods such as OnCreating?

Alternatively, when using a job filter like your example, is there a way of specifying notBefore and notAfter for each recurring job, as this seems a much cleaner solution.

Many thanks in advance.