How to use FluentScheduler to schedule a job from Monday to Friday - c#

I need to schedule a job to run at 9:00AM, 12:00PM and 5:00PM on Monday to Friday only. Did not find any documentation on FluentScheduler.
I can do it by having multiple(separately for 5 days) Schedule of the job but can we have single Schedule to do this repeatedly on the given time and days?

I would have thought the simplest solution would be to have the Execute() method in your IJob check the day of the week at its entry point and bail out immediately on Saturdays or Sundays...

You could use Weekdays i.e:
var schedule = Schedule(yourJob);
schedule.ToRunEvery(0).Weekdays().At(9, 0);
schedule.ToRunEvery(0).Weekdays().At(12, 0);
schedule.ToRunEvery(0).Weekdays().At(17, 0);
ToRunEvery(0) means we need to start now.
ToRunEvery(1) would wait one interval for first execution - in our case 1 week day.

I ran into the same issue. FluentScheduler isn't robust enough to handle very complex schedules. A better solution would be to use http://www.quartz-scheduler.net/ It is very flexible, is supported by Topshelf, and has support for most IoC containers.
For example in my service I used:
config.Service<Service>(sc =>
{ sc.ScheduleQuartzJob(configurator =>
configurator.WithJob(
() => JobBuilder.Create<DataLoadJob>().WithIdentity("DataLoad", "Job1").Build())
.AddTrigger(() => TriggerBuilder.Create().WithIdentity("DataLoadSchedule", "Job1")
.WithSimpleSchedule(builder => builder.WithIntervalInSeconds(10).RepeatForever()).Build()));
sc.ScheduleQuartzJob(configurator =>
configurator.WithJob(
() => JobBuilder.Create<DataMergeJob>().WithIdentity("DataMerge", "Job1").Build())
.AddTrigger(() => TriggerBuilder.Create().WithIdentity("DataMergeSchedule", "Job1")
.WithCronSchedule("0 30 7-20/3 ? * MON-FRI").Build()));
sc.ConstructUsingSimpleInjector();
sc.WhenStarted((s, h) => s.Start(h));
sc.WhenStopped((s, h) => s.Stop(h));
});
This is a fragment from a Topshelf service using SimpleInjector along with Quartz.

public class Example: Registry
{
public Example()
{
Schedule(() =>
{
DayOfWeek[] available = new DayOfWeek[] { DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday };
if (DateTime.Now.DayOfWeek.IsOn(available) && (DateTime.Now.Hour == 8 && DateTime.Now.Minute == 0))//etc
{
//code
}
}).WithName("Example").ToRunEvery(0).Hours().At(0).Between(8, 0, 17, 0);
}
}

Related

How to find given Event datetime is past event or Future event while considering hours aslo

I have Events page, In that displaying past present and future events counts and list. Here I'm facing a issue form QA team.
Example:
**Event date** is : 2022-01-20 08:00:00.000
**Current datetime** is: 2022-01-20 10:00:00.000
now based on above dates , we need to display event as past event because of two hours less than the Current datetime
if (planEvents.Count > 0)
{
switch (statusID)
{
case (int)GenericEnum.EventStatus.TodaysEvents:
events = planEvents.Where(o => o.EventDate.Date == DateTime.Today.Date).Skip(startIndex - 1).Take(pageSize).ToList();
totalCount = planEvents.Where(o => o.EventDate.Date == DateTime.Today.Date).ToList().Count();
break;
}
}
I tried above code but it's only returning date matching records but not hours comparison. can any once please help me.
First off, you should probably use UTC for all times, but that's a different issue. I don't really understand the use case you're after, but you can use the current time and add or subtract time units to create a range start and end. Not tested, but you should have something like this to select from a date range:
var start = DateTime.Now.AddHours(-2); // start from 2 hours ago
var end = DateTime.Now.AddHours(2); // end 2 hours from now
events = planEvents.Where(x => x.EventDate > start && x.EventDate < end);
Considering present events are the one falls within the current hour. Not tested, but it should work
if (planEvents.Any()) //Use Any method to improve performance
{
switch (statusID)
{
case (int)GenericEnum.EventStatus.TodaysEvents:
var todaysEvents = planEvents.Where(o => o.EventDate.Date == DateTime.Today.Date);
var todaysTotalEvents = todaysEvents.Count();
var currentHour = DateTime.Now.Hour;
var pastEvents = todaysEvents.Where(e=> e.EventDate.Hour < currentHour);
var presentEvents = todaysEvents.Where(e=> e.EventDate.Hour == currentHour);
var futureEvents = todaysEvents.Where(e=> e.EventDate.Hour > currentHour);
break;
}
}

C# get every two hour between two datetime object

I would like to get a list of every two hour between two datetime object
Below is what I have tried for every one hour:
TimeSpan betweenTime = EndTime - StartTime; // 15:00PM - 09:00AM
IEnumerable<int> hoursBetween = Enumerable.Range(0, (int)betweenTime.TotalHours).Select(i => StartTime.AddHours(i).Hour);
//Output for hoursBetween
9
10
11
12
13
14
//if between is every two hour
//the expected Output be like
9
11
13
You could use
IEnumerable<int> hoursBetween = Enumerable.Range(0, (int)Math.Ceiling(betweenTime.TotalHours / 2)).Select(i => StartTime.AddHours(i * 2).Hour);
You can add an additional where to only use values that when mod by 2 are 0 e.g.:
IEnumerable<int> hoursBetween = Enumerable.Range(0, (int)betweenTime.TotalHours).Where(i => i % 2 == 0).Select(i => startTime.AddHours(i).Hour);
You can also use a variable in the Where clause to handle different steps e.g.
var step = 2;
IEnumerable<int> hoursBetween = Enumerable.Range(0, (int)betweenTime.TotalHours).Where(i => i % step == 0).Select(i => startTime.AddHours(i).Hour);
Why don’t you make a if function that return the hour when StartTime is higher of 2 hour from the previous time.
I don’t explain well so here is an exemple:
int StartTime = //as exemple 15//;
int OldTime = StartTime;
//...
if(StartTime = OldTime + 2) {
OldTime += 2;
//do some stuff
}
This is not tested and I am kind of a beginner so let me know if this was useful or not.

IObservable - Ignore new elements for a span of time

I'm trying to "throttle" an IObservable in (what I think is) a different way of the standard throttle methods.
I want to ignore values for 1s following a first non ignored value in the stream.
For example, if 1s=5 dashes
source: --1-23--45-----678901234
result: --1-----4------6----1---
Any ideas on how to achieve this?
Here is an idiomatic way to do this in Rx, as an extension method - an explanation and example using your scenario follows.
The desired function works a lot like Observable.Throttle but emits qualifying events as soon as they arrive rather than delaying for the duration of the throttle or sample period. For a given duration after a qualifying event, subsequent events are suppressed:
public static IObservable<T> SampleFirst<T>(
this IObservable<T> source,
TimeSpan sampleDuration,
IScheduler scheduler = null)
{
scheduler = scheduler ?? Scheduler.Default;
return source.Publish(ps =>
ps.Window(() => ps.Delay(sampleDuration,scheduler))
.SelectMany(x => x.Take(1)));
}
The idea is to use the overload of Window that creates non-overlapping windows using a windowClosingSelector that uses the source time-shifted back by the sampleDuration. Each window will therefore: (a) be closed by the first element in it and (b) remain open until a new element is permitted. We then simply select the first element from each window.
In the following example, I have repeated exactly your test scenario modelling one "dash" as 100 ticks. Note the delay is specified as 499 ticks rather than 500 due to the resolution of passing events between multiple schedulers causing 1 tick drifts - in practice you wouldn't need to dwell on this as single tick resolutions is unlikely to be meaningful. The ReactiveTest class and OnNext helper methods are made available by including the Rx testing framework nuget package rx-testing:
public class Tests : ReactiveTest
{
public void Scenario()
{
var scheduler = new TestScheduler();
var test = scheduler.CreateHotObservable<int>(
// set up events as per the OP scenario
// using 1 dash = 100 ticks
OnNext(200, 1),
OnNext(400, 2),
OnNext(500, 3),
OnNext(800, 4),
OnNext(900, 5),
OnNext(1500, 6),
OnNext(1600, 7),
OnNext(1700, 8),
OnNext(1800, 9),
OnNext(1900, 0),
OnNext(2000, 1),
OnNext(2100, 2),
OnNext(2200, 3),
OnNext(2300, 4)
);
test.SampleFirst(TimeSpan.FromTicks(499), scheduler)
.Timestamp(scheduler)
.Subscribe(x => Console.WriteLine(
"Time: {0} Value: {1}", x.Timestamp.Ticks, x.Value));
scheduler.Start();
}
}
Note that output is as per your scenario:
Time: 200 Value: 1
Time: 800 Value: 4
Time: 1500 Value: 6
Time: 2000 Value: 1
This should do the trick. There may be a shorter implementation.
The accumulate in the Scan stores the Timestamp of the last kept Item and marks whether to Keep each item.
public static IObservable<T> RateLimit<T>(this IObservable<T> source, TimeSpan duration)
{
return observable
.Timestamp()
.Scan(
new
{
Item = default(T),
Timestamp = DateTimeOffset.MinValue,
Keep = false
},
(a, x) =>
{
var keep = a.Timestamp + duration <= x.Timestamp;
return new
{
Item = x.Value,
Timestamp = keep ? x.Timestamp : a.Timestamp,
Keep = keep
};
}
})
.Where(a => a.Keep)
.Select(a => a.Item);
}

reactive extensions sliding time window

I have a sequence of stock ticks coming in and I want to take all the data in the last hour and do some processing on it. I am trying to achieve this with reactive extensions 2.0. I read on another post to use Interval but i think that is deprecated.
Would this extension method solve your problem?
public static IObservable<T[]> RollingBuffer<T>(
this IObservable<T> #this,
TimeSpan buffering)
{
return Observable.Create<T[]>(o =>
{
var list = new LinkedList<Timestamped<T>>();
return #this.Timestamp().Subscribe(tx =>
{
list.AddLast(tx);
while (list.First.Value.Timestamp < DateTime.Now.Subtract(buffering))
{
list.RemoveFirst();
}
o.OnNext(list.Select(tx2 => tx2.Value).ToArray());
}, ex => o.OnError(ex), () => o.OnCompleted());
});
}
You are looking for the Window operators!
Here is a lengthy article I wrote on working with sequences of coincidence (overlapping windows of sequences)
http://introtorx.com/Content/v1.0.10621.0/17_SequencesOfCoincidence.html
So if you wanted to build a rolling average you could use this sort of code
var scheduler = new TestScheduler();
var notifications = new Recorded<Notification<double>>[30];
for (int i = 0; i < notifications.Length; i++)
{
notifications[i] = new Recorded<Notification<double>>(i*1000000, Notification.CreateOnNext<double>(i));
}
//Push values into an observable sequence 0.1 seconds apart with values from 0 to 30
var source = scheduler.CreateHotObservable(notifications);
source.GroupJoin(
source, //Take values from myself
_=>Observable.Return(0, scheduler), //Just the first value
_=>Observable.Timer(TimeSpan.FromSeconds(1), scheduler),//Window period, change to 1hour
(lhs, rhs)=>rhs.Sum()) //Aggregation you want to do.
.Subscribe(i=>Console.WriteLine (i));
scheduler.Start();
And we can see it output the rolling sums as it receives values.
0, 1, 3, 6, 10, 15, 21, 28...
Very likely Buffer is what you are looking for:
var hourlyBatch = ticks.Buffer(TimeSpan.FromHours(1));
Or assuming data is already Timestamped, simply using Scan:
public static IObservable<IReadOnlyList<Timestamped<T>>> SlidingWindow<T>(this IObservable<Timestamped<T>> self, TimeSpan length)
{
return self.Scan(new LinkedList<Timestamped<T>>(),
(ll, newSample) =>
{
ll.AddLast(newSample);
var oldest = newSample.Timestamp - length;
while (ll.Count > 0 && list.First.Value.Timestamp < oldest)
list.RemoveFirst();
return list;
}).Select(l => l.ToList().AsReadOnly());
}

DateTime interval restriction in C#

The problem:
I am in process of implementing a scheduler for my advisor in school. The scheduler supposes to setup a 15 minutes interval time slot from 8:00 AM to 5:00 PM, Monday to Friday. In addition, the advisor will have to specify the start and end dates of the scheduler. The scheduler will also feature an option to specify if the 15 minutes time slot is not open. Meaning my advisor will be able to mark specific time slot as NOT AVAILABLE.
What I have so far:
I have created a simple class:
public class TimeSlot
{
public DateTime dateTime
{
get;
set;
}
public bool isAvailable
{
get;
set;
}
TimeSlot(DateTime dt, bool Avalible)
{
dateTime = dt;
isAvailable = Avalible;
}
}
The class basically represents an object for one time slot in the scheduler. I also have a list of time slots that keeps a list of the valid time slots:
List<TimeSlot> TSList = new List<TimeSlot>();
Note that a valid time slot means the following:
Date is within: Monday to Friday.
Time is within: 8:00 AM to 5:00 PM
Time slots are within: 15 minutes interval.
In addition, I have a method that fill in the TSList as the following:
private void button_Next_Click(object sender, RoutedEventArgs e)
{
/* Getting the values of fromDate and toDate from the GUI controls*/
DateTime fromDate = datePicker1.SelectedDate.Value;
DateTime toDate = datePicker2.SelectedDate.Value;
while (fromDate <= toDate)
{
/*This ensures that we only deal with days Monday to Friday*/
if (fromDate.DayOfWeek.ToString() != "Saturday" && fromDate.DayOfWeek.ToString() != "Sunday")
{
/*PROBLEM HERE!!*/
}
/*Updating fromDate: Incrementing fromDate by 1 day*/
fromDate = fromDate.AddDays(1);
}
}
Notes that I was only able to satisfy the first condition in my valid time slot conditions. Thus, I was only able to restrict the dates to be within Monday to Friday range.
The questions:
I am trying to achieve the missing two valid conditions for a time slot:
How to restrict the times to be only 8:00am to 5:00 pm?
How to make time slots separated by 15 minutes interval?
First, please use DayOfWeek.Saturday and DayOfWeek.Sunday for the comparision, converting to a string is not necessary...
Then just use a simple loop like
DateTime startSlot = fromDate.Date.AddHours(8); // Starts at 8:00AM
while (startSlot.Hour < 17) {
// Construct time slot class
startSlot = startSlot.AddMinutes(15);
}
This gives you startSlot values starting at 8:00am at every date ranging to 5pm (i.e. the last one is 4:45pm).
Why are you considering building this out of nothing?
Why are you not starting with one of the many calendar management programs that are available off the shelf? For example, Microsoft Outlook contains calendar and schedule management, and you can do all of what you describe, easily. It also integrates with other scheduling tools via .ICS files, it syncs with mobile devices, syncs with Google Calendar, and so on.
But there are lots of other options. Google Calendar is another obvious one.
I don't know why you would ever consider starting from scratch. Unless it's an academic exercise (and no, I don't mean that you work in academia), then you should use larger building blocks to start.
It's like building a structure, starting with sand and water, instead of pre-fabricated concrete block.
Just quick implementation. Let me know if you need some comments.
// Round interval
const int roundInterval = 15;
var remainder = fromDate.TimeOfDay.Minutes % roundInterval;
var curTime = remainder == 0 ? fromDate : fromDate.AddMinutes(roundInterval - remainder);
curTime = curTime.AddSeconds(-curTime.TimeOfDay.Seconds);
var delta = TimeSpan.FromMinutes(roundInterval);
while (curTime < toDate)
{
while (curTime.DayOfWeek == DayOfWeek.Saturday || curTime.DayOfWeek == DayOfWeek.Sunday)
{
curTime = curTime.Date.AddDays(1);
}
if (curTime.TimeOfDay.Hours < 8)
{
curTime = curTime.AddHours(8 - curTime.TimeOfDay.Hours);
curTime = curTime.AddMinutes(-curTime.TimeOfDay.Minutes);
continue;
}
if (curTime.TimeOfDay.Hours >= 17)
{
curTime = curTime.AddHours(24 - curTime.TimeOfDay.Hours);
curTime = curTime.AddMinutes(-curTime.TimeOfDay.Minutes);
continue;
}
TSList.Add(new TimeSlot(curTime, true));
curTime = curTime.Add(delta);
}
}
DateTime myScheduledTimeSlot = new DateTime(2010, 10, 26, 8, 45, 0);
// Use existing check to check day of week constraint...
// Check if the datetime falls on a correct minute boundary
switch (myScheduledTimeSlot.Minute)
{
case 0:
case 15:
case 30:
case 45:
// The time slot is valid
break;
default:
// The time slot is not valid
break;
}
It is pretty simple to check whether it falls in a 15 minute slot as you don't have weird boundaries keeping every hour identical. I'd recommend checking out Quart.NET if you want to save some time doing eventing/scheduling.

Categories

Resources