Reputation: 9900
I have an object that is loading configurations from a database. I store the last time that a job was run with a datetime field (called GroupsLastRun
) and I store how often the job should run with a string field called Captureusersandgroups
. Captureusersandgroups
stores three different types 'DAILY', 'WEEKLY' and 'MONTHLY'.
Basically, I have a loop should only continue if the job is due to run. So far I've got to the following point:
if (configEntity.GroupsLastrun > DateTime.Now.AddDays(-1) && configEntity.Captureusersandgroups == "DAILY") continue;
if (configEntity.GroupsLastrun > DateTime.Now.AddDays(-7) && configEntity.Captureusersandgroups == "WEEKLY") continue;
if (configEntity.GroupsLastrun > DateTime.Now.AddDays(-30) && configEntity.Captureusersandgroups == "MONTHLY") continue;
I'm sure (certain) there's a much better approach to this but primarily being a SQL Server developer I lack the critical thinking / tools for approaching this. What is a better approach or what should I learn so I can think about this better?
Upvotes: 2
Views: 962
Reputation: 39085
As an alternative approach, you could possibly store the run period in an enum
like :
enum RunPeriod
{
Daily = 1,
Weekly = 7,
Monthly = 30
}
And then you can store the int
values, instead of string
, on the database. This allows you to do the filtering on the DB side, e.g.:
var configsToRun =
from c in _myContext.Configs
where EntityFunctions.AddDays(c.LastRun,(int)c.RunPeriod) > DateTime.Now);
Upvotes: 1
Reputation: 1500275
A few points:
DateTime.UtcNow
rather than DateTime.Now
(and make sure you store the UTC value as well)So, I'd have code like this:
// Consider whether you actually want DateTime.UtcNow.Date
DateTime now = DateTime.UtcNow;
DateTime deadline;
switch (configEntity.Captureusersandgroups)
{
case "DAILY": deadline = now.AddDays(-1);
case "WEEKYLY": deadline = now.AddDays(-7);
case "MONTHLY": deadline = now.AddMonths(-1);
// I'm assuming there's *always* a schedule
default: throw new InvalidOperationException("Invalid schedule");
}
if (configEntity.GroupsLastrun > deadline)
{
continue;
}
Note that subtracting a month from "now" isn't the same as adding a month from "then". For example, if the last run was on January 30th, the next run won't be until March 1st using the above code - whereas if you added a month to January 30th, it would next be run on February 28th (unless you use the dates of both values). Consider carefully exactly the behaviour you want.
(As a quick plug, I'd also obviously recommend considering my Noda Time library for date/time work. It makes it clearer whether any particular value is a local time, or in some time zone etc.)
Upvotes: 7
Reputation: 149010
Two points:
DateTime.Now
can (potential) return a different date every time it's called. It's also not very fast. You'll get better consistency and a very slight performance boost by only calling it once. DateTime
alone; you'll have to use a Calendar
instead.Try this:
var now = DateTime.UtcNow; // See Jon Skeet's answer
var cal = CultureInfo.InvariantCulture.Calendar;
if (configEntity.GroupsLastrun > now.AddDays(-1) && configEntity.Captureusersandgroups == "DAILY") continue;
if (configEntity.GroupsLastrun > cal.AddWeeks(now, -1) && configEntity.Captureusersandgroups == "WEEKLY") continue;
if (configEntity.GroupsLastrun > now.AddMonths(-1) && configEntity.Captureusersandgroups == "MONTHLY") continue;
Or just use cal
for everything:
var now = DateTime.UtcNow; // See Jon Skeet's answer
var cal = CultureInfo.InvariantCulture.Calendar;
if (configEntity.GroupsLastrun > cal.AddDays(now, -1) && configEntity.Captureusersandgroups == "DAILY") continue;
if (configEntity.GroupsLastrun > cal.AddWeeks(now, -1) && configEntity.Captureusersandgroups == "WEEKLY") continue;
if (configEntity.GroupsLastrun > cal.AddMonths(now, -1) && configEntity.Captureusersandgroups == "MONTHLY") continue;
Upvotes: 3