Edit:
Steps:
Start at target day.
Then move backwards until no events are carried over from another day.
From there, start counting hours, and keep track of carried over hours.
Day cannot last more than ActualDayLength()
Then, once you know that, work your way back to target and then calculate actual occupied hours.
I have tasks that are put on a calendar:
Now let me give this some context:
Each day 'lasts' 7.5 hours here. But I work with a variable called DayHours (which right now is 7.5). (DayHours is also used in Locked Time which Ill describe below).
The goal of this calendar is to schedule 7.5 hour work days for employees.
What I need, is an algorithm that can correctly tell me how many hours are actually occupied in a day.
This seems simple, but is actually quite recursive.
First, a couple of notes. You will notice Case manager, at 14 hours, could be done in 2 days of 7.5 hours with 1 hour left over. It is stretched to 3 days because 1. Schedule, is 5 hours long, and 2. cannot start until the predecessor tasks of the day are complete.
There is also the concept of Locked Time.
In purple is Locked Time. This is a 10 hour block of locked time.
This means, on the 12th, I can only do (7.5 - 7.5) hours of work, and Monday, only (7.5 - 2.5) aswell.
I already have a function to calculate an actual day's available hours to account for this:
public decimal GetActualDayLength(DateTime day, Schedule s)
{
var e = Schedules.GetAllWithElement();
var t = Timeless(day);
var locked = from p in e
where p.EmployeID == s.EmployeID &&
((p.DateTo.Value.Date) >= t &&
Timeless(p.DateFrom.Value) <= t) &&
p.IsLocked
select p;
decimal hrs = 0.0M;
foreach (var c in locked)
{
if (c.Hours.Value <= DaysManager.GetDayHours())
hrs += c.Hours.Value;
else if (Timeless(c.DateTo.Value) != t)
hrs += DaysManager.GetDayHours();
else
{
if (c.Hours.Value % DaysManager.GetDayHours() > 0)
hrs += c.Hours.Value % DaysManager.GetDayHours();
else
hrs += DaysManager.GetDayHours();
}
}
return DaysManager.GetDayHours() - hrs;
}
There is also the concept of carry hours.
Here is an example:
Now let us take Thursday the 18th (The 18th has 1. Case):
To find the number of hours this day has for that employee, we need to first look at the tasks that start, end, or fall within that day.
I don't know how many hours I can do on the 18th because the task ending that day might have had carry hours.
So I go look at Perform unit test's start day. I cant figure that out either because NWDM finishes that day and it might have carry hours.
So now I go evaluate NWDM. Ahh, this one has nothing ending that day, so I know Schedule will take 5 / 7.5 hours available.
So I keep going, adding 7.5 hours each day that I pass.
Then I get to NWDM's last day.
Up until then, I worked 5 + 7.5 + 7.5 + 7.5 hours on it,
So I put in 27.5 hours, so I'll put in (30 - 27.5 = 2.5h) on the 22nd to finish it. So I have 5 hours left to work on Perform Unit Tests.
This means that I will need 1.5h to finish it. Now Case is 1 hour long.
Had case been 7.5 - 1.5 or more, we say the day is full and return DayHours.
Therefore, we are done. The return value is 1.5 + 1 = 2.5.
The function should look a bit like this one:
public decimal GetHours(IEnumerable<Schedule> s, DateTime today)
{
DateTime t = Timeless(today);
decimal hrs = 0;
foreach (Schedule c in s)
{
if (c.Hours.Value <= DaysManager.GetDayHours())
hrs += c.Hours.Value;
else if (Timeless(c.DateTo.Value) != t)
hrs += DaysManager.GetDayHours();
else
{
if (c.Hours.Value % DaysManager.GetDayHours() > 0)
hrs += c.Hours.Value % DaysManager.GetDayHours();
else
hrs += DaysManager.GetDayHours();
}
}
return hrs;
}
To get the events that start, end, or fall within a given day, I use:
public IEnumerable<Schedule> GetAllToday(DateTime date, int employeeID, Schedule current)
{
DateTime t = Timeless(date);
int sid = current == null ? -1 : current.ScheduleID;
var e = Schedules.GetAllWithElement();
return from p in e
where (((Timeless(p.DateTo.Value) >= t &&
Timeless(p.DateFrom.Value) <= t &&
p.EmployeID == employeeID) &&
(p.IsLocked || (Timeless(p.DateFrom.Value) < t &&
(sid == -1 ? true : Timeless(p.DateFrom.Value) < current.DateFrom.Value)) ||
bumpedList.Any(d => d.ScheduleID == p.ScheduleID)) &&
p.ScheduleID != sid) ||
((Timeless(p.DateTo.Value) >= t &&
(Timeless(p.DateFrom.Value) == t || (Timeless(p.DateFrom.Value) < t &&
(sid == -1 ? true : Timeless(p.DateFrom.Value) > current.DateFrom.Value))) &&
p.EmployeID == employeeID) &&
!p.IsLocked &&
!bumpedList.Any(d => d.ScheduleID == p.ScheduleID) &&
p.ScheduleID != sid)) &&
p.ScheduleID != sid
select p;
}
The Schedule has the following relevant fields:
DateFrom
DateTo
Hours
EmployeeID
The Schedule looks something like:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Schedule")]
public partial class Schedule : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _ScheduleID;
private System.Nullable<System.DateTime> _DateFrom;
private System.Nullable<decimal> _Hours;
private System.Nullable<int> _EmployeID;
private System.Nullable<int> _RecurringID;
private System.Nullable<int> _Priority;
private System.Nullable<System.DateTime> _DateTo;
private bool _IsLocked;
private System.Nullable<int> _BumpPriority;
private EntitySet<Case> _Cases;
private EntitySet<Project> _Projects;
private EntitySet<Task> _Tasks;
private EntitySet<Task> _Tasks1;
private EntityRef<Employee> _Employee;
private EntityRef<Recurring> _Recurring;
#region Extensibility Method Definitions
partial void OnLoaded();
partial void OnValidate(System.Data.Linq.ChangeAction action);
partial void OnCreated();
partial void OnScheduleIDChanging(int value);
partial void OnScheduleIDChanged();
partial void OnDateFromChanging(System.Nullable<System.DateTime> value);
partial void OnDateFromChanged();
partial void OnHoursChanging(System.Nullable<decimal> value);
partial void OnHoursChanged();
partial void OnEmployeIDChanging(System.Nullable<int> value);
partial void OnEmployeIDChanged();
partial void OnRecurringIDChanging(System.Nullable<int> value);
partial void OnRecurringIDChanged();
partial void OnPriorityChanging(System.Nullable<int> value);
partial void OnPriorityChanged();
partial void OnDateToChanging(System.Nullable<System.DateTime> value);
partial void OnDateToChanged();
partial void OnIsLockedChanging(bool value);
partial void OnIsLockedChanged();
partial void OnBumpPriorityChanging(System.Nullable<int> value);
partial void OnBumpPriorityChanged();
#endregion
public Schedule()
{
this._Cases = new EntitySet<Case>(new Action<Case>(this.attach_Cases), new Action<Case>(this.detach_Cases));
this._Projects = new EntitySet<Project>(new Action<Project>(this.attach_Projects), new Action<Project>(this.detach_Projects));
this._Tasks = new EntitySet<Task>(new Action<Task>(this.attach_Tasks), new Action<Task>(this.detach_Tasks));
this._Tasks1 = new EntitySet<Task>(new Action<Task>(this.attach_Tasks1), new Action<Task>(this.detach_Tasks1));
this._Employee = default(EntityRef<Employee>);
this._Recurring = default(EntityRef<Recurring>);
OnCreated();
}
}
Could anyone help me with developing an algorithm that can do this?
Even though you question is very complex and not very clearly explained, I'll try to answer it. Or more precisely hint you how you should decompose and solve it (or how I would solved it).
What I need, is an algorithm that can correctly tell me how many hours are actually occupied in a day.
At first I do not see the real problem in case you have DateTo value available for Schedule. Unless it equals to DateFrom + Hours. In such case it does not reflect the real DateTo but somewhat irrelevant value instead.
I will presume any Schedule is defined by starting time DateFrom and duration Hours. DateTo is calculated value and efficiently computing is the real core of the problem.
So I think this function getting available hours in any time range is pretty straightforward. Speaking in pseudo-code:
TimeSpan GetAvailableTime(DateRange range)
var tasks = FindIntersectingTasks(range)
' now the algorithm which finds available hours on given collection
' of tasks
' firstly - we need to determine relevant ranges which intersect
' with given range
var occupiedRanges = New List<DateRange>(tasks.Count)
for each task in tasks
var intersection = range.Intersect(
new DateRange(task.DateFrom, task.DateTo)
)
if Not intersection.IsEmpty
occupiedRanges.Add(intersection)
end
end
' secondly - sort ranges by start so we can easily merge them
ranges.Sort(range => range.DateFrom)
var mergedOccupiedRanges = new List(DateRange)
' thirdly - merge ranges so that we have collection with
' non-overlaping ranges (and also sorted)
for each occupiedRange in occupiedRanges
' range may merge only it there is non-empty intersection
if occupiedRange.CanMerge(mergedOccupiedRanges.Last)
var mergedRange = range.Merge(mergedOccupiedRanges.Last)
mergedOccupiedRanges.RemoveLast()
mergedOccupiedRanges.Add(mergedRange)
end
end
' fourthly - it is simple now to determine available/occupied hours
var timeAvailable = range.Duration
for each mergedRange in mergedOccupiedRanges
timeAvailable -= mergedRange.Duration
end
return timeAvailable
end
IEnumerable<Schedule> FindIntersectingTasks(DateRange range)
return From schedule In allEvents
Where schedule.DateFrom <= range.To
And schedule.DateTo >= range.From
end
You may need some adjustments as the DateTime expects normal 24-hour days.
Like such:
create a list of days with free time.
for each item in the list, add the maximum amount of time available to your task.
If the time needed for your project reaches 0, stop adding blocks.
This does not answer the exact question, but I would suggest simplifying your logic by extending your classes (objects) with some helper methods, e.g. methods/properties that return lists of days occupied.
If you cannot access these classes (i.e. they are not from your code base) - then create new classes and map to those.
Also - the .NET DateTime class has some very useful properties and enums like "DayOfWeek" and "TimeOfDay" that might be useful for you.
Related
I'm having trouble figuring out which tradings session any particular time is in.
There are four possible sessions, show in this picture taken from ForexFactory.com
I have this method that I need to check is currentTime is during the specified trading session.
public bool IsTradingSession(TradingSession tradingSession, DateTime currentTime)
{
//currentTime is in local time.
//Regular session is 5PM - next day 5PM, this is the session in the picture.
//Irregular sessions also occur for example late open (3AM - same day 5PM) or early close (5PM - next day 11AM)
DateTime sessionStart = Exchange.ToLocalTime(Exchange.CurrentSessionOpen);
DateTime sessionEnd = Exchange.ToLocalTime(Exchange.CurrentSessionClose);
if(tradingSession == TradingSession.Sydney)
return ....... ? true : false;
if(tradingSession == TradingSession.Tokyo)
return ....... ? true : false;
if(tradingSession == TradingSession.London)
return ....... ? true : false;
if (tradingSession == TradingSession.NewYork)
return ....... ? true : false;
return false;
}
Use:
bool isSydneySession = IsTradingSession(TradingSession.Sydney, CurrentTime);
bool isTokyoSession = IsTradingSession(TradingSession.Tokyo, CurrentTime);
bool isLondonSession = IsTradingSession(TradingSession.London, CurrentTime);
bool isNewYorkSession = IsTradingSession(TradingSession.NewYork, CurrentTime);
Thank you
I'd suggest writing a simple function for each trading session, which takes a DateTime and returns a bool indicating if it's open at that time.
var sydneyOpen = new TimeSpan(17, 0, 0);
var sydneyClose = new TimeSpan(2, 0, 0);
Func<DateTime, bool> isOpenInSydney = d =>
(d.TimeOfDay > sydneyOpen || d.TimeOfDay < sydneyClose);
// same for other markets, write a function to check against two times
Then place these into a Dictionary<TradingSession, Func> like this for generic retrieval...
var marketHours = new Dictionary<TradingSession, Func<DateTime, bool>>();
marketHours.Add(TradingSession.Sydney, isOpenInSydney);
// add other markets...
And then your existing method simply selects the appropriate function for the given TradingSession and applies it
public bool IsTradingSession(TradingSession tradingSession, DateTime currentTime)
{
var functionForSession = marketHours[tradingSession];
return functionForSession(currentTime);
}
I don't believe you need UTC time here as long as your application only runs in a single timezone, but daylight savings might cause problems.
A nice way to account for the problem of trading sessions which cover two days, as opposed to just one day, is to write a helper that precisely considers whether it's a 'cross-day' trading session and applies a different rule for you:
private bool IsBetween(DateTime now, TimeSpan open, TimeSpan close)
{
var nowTime = now.TimeOfDay;
return (open < close
// if open is before close, then now must be between them
? (nowTime > open && nowTime < close)
// otherwise now must be *either* after open or before close
: (nowTime > open || nowTime < close));
}
and then
var sydneyOpen = new TimeSpan(17, 0, 0);
var sydneyClose = new TimeSpan(2, 0, 0);
Func<DateTime, bool> isOpenInSydney = d => IsBetween(d, sydneyOpen, sydneyClose);
You can compare with > & < or compare ticks.
See related questions: Check if datetime instance falls in between other two datetime objects
To avoid the multiple if statements, you could also create a TradingSession object with start and end time and define a property/function to check if in session. When I have big switch or if blocks, it usually indicates a missed OO opportunity :)
TradingSession sydneySession = new TradingSession
{
StartTimeUtc = ...;
EndTimeUtc = ...;
}
The trading session object could then have a property IsInSession.
public bool IsInSession
{
get {
return DateTime.UTCNow >= StartTimeUtc && DateTime.UTCNow <= EndTimeUtc;
}
}
This uses UTC time to eliminate time zone issues.
You need to normalize your local times to UTC. You can then compare times across regions.
For each trading session, you need to know the session start and end time in UTC.
You need the current time in UTC. DateTime.UtcNow, for example.
You can then perform range comparisons for each trading session window:
if(tradingSession == TradingSession.Sydney)
return currentTimeUtc >= sydneyStartTimeUtc && currentTimeUtc <= sydneyEndTimeUtc;
etc...
If you're trying to validate that a transaction time is valid for a transaction on a particular trading session, this will work fine.
If however you're trying to figure out what trading session a trade belongs to based on its time, you will have multiple answers sometimes because the trading sessions overlap. Multiple trading sessions may be valid for a given time.
I am developing c#.net solution where i have to calculate Start Date based on provided End date and Duration (back days) without weekends.
i.e. End Date: 05/5/2011
Back days: 5
Start Date = (05/5/2011) - 5 days (Excludes weekends)
Start Date = 29/04/2011
thanks you,
Something like this is probably what I'd do:
DateTime CalcStartDate(DateTime endTime, int daysBack)
{
DateTime startTime = endTime.Date;
while (daysBack > 0)
{
startTime = startTime.AddDays(-1);
if (startTime.DayOfWeek != DayOfWeek.Saturday && startTime.DayOfWeek != DayOfWeek.Sunday)
{
--daysBack;
}
}
return startTime;
}
Or even better, Bala's suggestion of using a library. Date and time is messy, a hardened/tested library is usually a good choice.
Bala R has the answer correctly. Here is a link to an article on how to use the AddBusinessDays() method:
http://www.codeproject.com/KB/cs/AddBusinessDay.aspx
I don't know how far back you are going to go. If it is a lot of days back then looping through the days might be a little CPU intensive. Well, probably not with modern processors...
I decided to implement a solution without a loop.
My code is a little more difficult to read, but it should be more efficient performance-wise.
public static class DateTimeExtensions
{
public static DateTime SubtractBusinessDays(this DateTime fromDateTime, int days)
{
var subtractDays = days % 5;
var dayNumber = fromDateTime.DayOfWeek == DayOfWeek.Sunday ? 7 : (int)fromDateTime.DayOfWeek;
var addDays = Math.Max(dayNumber - 5, 0);
var result = fromDateTime.AddDays(addDays - subtractDays - (days / 5 * 7));
if ((addDays + dayNumber) % 7 <= subtractDays)
result = result.AddDays(-2);
return result;
}
}
.NET gives me the current time in both local and UTC time zones (or any other) in the DateTime struct.
Given only hour/minute variables, find when the next occurrence of this time period (eg 6:30 PM/AM) will occur, with the ability to retrieve more future times at will.
It sounds easy but indeed friends this has been breaking my noodle for a good while.
Edit:
Example:
~-------|now|------??-----------|future known time|------------~
~-------2pm------??2-----------9am------------~
??2 = 19
If I understand correctly you want to know how much time has to pass in order to hit the next given hour:minute. You can use the TimeSpan struct for this.
//this is your target time from 1 to 12 h
var future = new TimeSpan(11, 30, 0);
//TimeOfDay gives you the time elapsed since midnight as a TimeSpan
var difference = future.Subtract(DateTime.Now.TimeOfDay);
//check for negative TimeSpan,
//it means the target time occurs on the next day, just add 24 hours
if (difference < TimeSpan.Zero)
difference = difference.Add(TimeSpan.FromDays(1));
Now you have a TimeSpan that represents what you need. You can use its properties to express it as you seem fit. For example:
difference.TotalHours; //(double) total time as a fractional hour
difference.Hours; //(int) just the hour component of the total time
As for retreiving more future times (am and pm), you can just add 12 more hours to difference to get the next occurence.
This was cooded in stackoverflow form, so it's probably there are some typos. Either way, you'll get the big picture.
public DateTime search(int hour, int min) {
if (hour >= 12)
return partialSearch(hour - 12, hour, min);
else
return partialSearch(hour, hour + 12, min);
}
public DateTime partialSearch(int morningHour, int afternoonHour, int min) {
DateTime now = DateTime.now;
if (now.hour == morningHour || now.hour == afternoonHour) {
if (now.minutes <= min) {
return now.AddMinutes(min - now.minutes);
}
now = now.AddHour(1);
}
now = now.AddMinutes(-now.Minutes); // set the minutes to 0
while(now.hour != morningHour || now.hour != afternoonHour) {
now = now.AddHour(1);
}
return now.addMinutes(min);
}
Not sure if I fully understood your requirements, but it seems you are looking for something like this?
public class TimeIterator
{
public DateTime CurrDateTime { get; set; }
public IEnumerable<DateTime> GetTimes(short count)
{
for (short i = 1; i <= count; i++)
yield return this.CurrDateTime.AddHours(i * 12);
}
public TimeIterator()
{
this.CurrDateTime = DateTime.Now;
}
}
This code can be easily adjusted to work with any time interval - not just 12 hour interval
I am new at C#. I'd like to check whether a time is between 2 given hours, and if so then do something. Can anyone give me an example?
pseudocode example:
int starthour = 17;
int endhour = 2;
if ( hour between starthour and endhour){
dosomething();
}
How do I write a check on whether hour is between starthour and endhour? In C#, the time is returned in AM/PM format so I don't know if it will understand the 17 number as "5 PM".
Assuming you're talking about the current time, I'd do something like this:
// Only take the current time once; otherwise you could get into a mess if it
// changes day between samples.
DateTime now = DateTime.Now;
DateTime today = now.Date;
DateTime start = today.AddHours(startHour);
DateTime end = today.AddHours(endHour);
// Cope with a start hour later than an end hour - we just
// want to invert the normal result.
bool invertResult = end < start;
// Now check for the current time within the time period
bool inRange = (start <= now && now <= end) ^ invertResult;
if (inRange)
{
DoSomething();
}
Adjust the <= in the final condition to suit whether you want the boundaries to be inclusive/exclusive.
If you're talking about whether a time specified from elsewhere is within a certain boundary, just change "now" for the other time.
Actually, if we're dealing with pure hours here like a Abelian Ring from 0 to 23 and 0 again, I believe the following is actually a working solution:
(start <= end && start <= t && t <= end) or (start > end && (start <= t || t <= end))
Complex though this is, it is essentially an if-else where you have a different algorithm depending on whether start <= end or not, where t is the time you wish to test. In the first case, start and end are normal order, so t must be both greater than start and less than end. In the case where start is greater than end, the times outside the opposite range are what we want:
NOT(end < t and t < start)
Using DeMorgan's theorem:
NOT(end < t) or NOT(t < start)
NOT(t < start) or NOT(end < t)
t >= start or end >= t
start <= t or t <= end
This should solve your and my problems.
#JonSkeet
The thing is, looking at your algorithm, let's assume for a moment the time is 1am, day 1.
Now holds 1am Day 1
Today holds midnight Day 1
Start holds 5pm Day 1 (given the original example)
End holds 2am Day 1 (again from the example)
End holds 2am Day 2 (since start > end)
Now, unless I'm mistaken, start ≰ now since start is 5pm Day 1 and now is 1am Day 1 which is before now, therefore the test fails but the original question wanted 1am included in the range since 1am is between 5pm and 2am. Did I miss something?
#Brian
Also, looking at your code, I think you can detect 1am but now you would have a problem with 10pm (22:00) since your times become:
Start is 17
End is 26
Now is 22 + 24 = 46! so you will fail in the less-than test.
Clearly, the general case is very tricky! More so when you're restricted to Google Spreadsheets as I am.
When subtracting DateTimes, you get a TimeSpan struct that you can query for things like the total number of hours (the TotalHours property):
TimeSpan ts = starttime - endtime;
if(ts.TotalHours > 2)
{
dosomething();
}
If you want to see if the times are identical, then you can use TotalMilliseconds - for identical DateTimes, this will be equal to 0.
If you want to compare minutes also like I do you can use this snippet of code in java.
//Initialize now, sleepStart, and sleepEnd Calendars
Calendar now = Calendar.getInstance();
Calendar sleepStart = Calendar.getInstance();
Calendar sleepEnd = Calendar.getInstance();
//Assign start and end calendars to user specified star and end times
long startSleep = settings.getLong("startTime", 0);
long endSleep = settings.getLong("endTime", 0);
sleepStart.setTimeInMillis(startSleep);
sleepEnd.setTimeInMillis(endSleep);
//Extract hours and minutes from times
int endHour = sleepEnd.get(Calendar.HOUR_OF_DAY);
int startHour = sleepStart.get(Calendar.HOUR_OF_DAY);
int nowHour = now.get(Calendar.HOUR_OF_DAY);
int endMinute = sleepEnd.get(Calendar.MINUTE);
int startMinute = sleepStart.get(Calendar.MINUTE);
int nowMinute = now.get(Calendar.MINUTE);
//get our times in all minutes
int endTime = (endHour * 60) + endMinute;
int startTime = (startHour * 60) + startMinute;
int nowTime = (nowHour * 60) + nowMinute;
/*****************What makes this 100% effective***************************/
//Test if end endtime is the next day
if(endTime < startTime){
if(nowTime > 0 && nowTime < endTime)
nowTime += 1440;
endTime += 1440;
}
/**************************************************************************/
//nowTime in range?
boolean inRange = (startTime <= nowTime && nowTime <= endTime);
//in range so calculate time from now until end
if(inRange){
int timeDifference = (endTime - nowTime);
now.setTimeInMillis(0);
now.add(Calendar.MINUTE, timeDifference);
sleepInterval = now.getTimeInMillis() / 1000;
editor.putBoolean("isSleeping", true);
editor.commit();
Log.i(TAG, "Sleep Mode Detected");
returned = true;
}
bool CheckHour(DateTime check, DateTime start, DateTime end)
{
if (check.TimeOfDay < start.TimeOfDay)
return false;
else if (check.TimeOfDay > end.TimeOfDay)
return false;
else
return true;
}
int starthour = 17;
int endhour = 2;
int nowhour = DateTime.Now.Hour;
if (endhour < starthour)
{
endhour+=24;
nowhour+=24;
}
if (starthour <= nowhour && nowhour <= endhour)
{
dosomething();
}
I'm not sure which I prefer between this code and Jon Skeet's code.
Using Jon Skeet's solution above I added a fix where if start time is after beginning time eg You start the job after 6pm at night and end it the next morning at 5am. then you need to check this and apply another day to the end time. Hope it helps, I personally have spent too much time on this piece of work. have a great day :)
if (stopHour < startHour)
{
end = today.AddHours(stopHour+24);
}
Full Code is below.
private static bool IsInRunHours()
{
try
{
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
// after = 18 before = 5
// Only take the current time once; otherwise you could get into a mess if it
// changes day between samples.
DateTime now = DateTime.Now;
DateTime today = now.Date;
Int32 startHour = ConfigurationManager.AppSettings["UpdateRunAfter"].ToInt();
Int32 stopHour = ConfigurationManager.AppSettings["UpdateRunBefore"].ToInt();
DateTime start = today.AddHours(startHour);
DateTime end = today.AddHours(stopHour);
if (stopHour < startHour)
{
end = today.AddHours(stopHour+24);
}
//ConfigurationManager.AppSettings["UpdateRunBefore"].ToInt()
//ConfigurationManager.AppSettings["UpdateRunAfter"].ToInt()
// Cope with a start hour later than an end hour - we just
// want to invert the normal result.
bool invertResult = end < start;
// Now check for the current time within the time period
bool inRange = (start <= now && now <= end) ^ invertResult;
if (inRange)
{
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
I feel like this is math problem more than anything. My company has employees all over the country. Some parts of the company are on an "odd" pay cycle and some are on "even". I call the starting date of a given pay period a "payperiod". I need to do two things:
1) determine the payperiod in which a given date falls
//Something like this:
public static DateTime getPayPeriodStartDate(DateTime givenDate, string EvenOrOdd)
{ .. }
2) get a list of payperiods between two dates:
//Something like this:
public static List<DateTime> getPayPeriodsBetween(DateTime start, DateTime end, string EvenOrOdd)
{ .. }
I'm using a couple dates as fixed standards on which to base any future pay period dates. The fixed standard dates for even and odd are as follows:
Even - 01/04/09
Odd - 01/11/09
Each pay period starts on the sunday of the week and goes for two weeks. For instance, using the standard dates above, the first even pay period starts on 01/04/09 and ends on 01/17/09. The first odd pay period starts on 01/11/09 and ends on 01/24/09. As you can see, there is some overlap. We have thousands of employees so it's necessary to split them up a bit.
I have a solution that is based on week numbers but it's clunky and has to be "fixed" every new year. I'm wondering how you would handle this.
Not fully optimized or tested, but this is what I came up with:
const int DaysInPeriod = 14;
static IEnumerable<DateTime> GetPayPeriodsInRange(DateTime start, DateTime end, bool isOdd)
{
var epoch = isOdd ? new DateTime(2009, 11, 1) : new DateTime(2009, 4, 1);
var periodsTilStart = Math.Floor(((start - epoch).TotalDays) / DaysInPeriod);
var next = epoch.AddDays(periodsTilStart * DaysInPeriod);
if (next < start) next = next.AddDays(DaysInPeriod);
while (next <= end)
{
yield return next;
next = next.AddDays(DaysInPeriod);
}
yield break;
}
static DateTime GetPayPeriodStartDate(DateTime givenDate, bool isOdd)
{
var candidatePeriods = GetPayPeriodsInRange(givenDate.AddDays(-DaysInPeriod), givenDate.AddDays(DaysInPeriod), isOdd);
var period = from p in candidatePeriods where (p <= givenDate) && (givenDate < p.AddDays(DaysInPeriod)) select p;
return period.First();
}
I haven't tested for many test cases, but I think this fits the bill:
public static DateTime getPayPeriodStartDate(DateTime givenDate, string EvenOrOdd)
{
DateTime newYearsDay = new DateTime(DateTime.Today.Year, 1, 1);
DateTime firstEvenMonday = newYearsDay.AddDays((8 - (int)newYearsDay.DayOfWeek) % 7);
DateTime firstOddMonday = firstEvenMonday.AddDays(7);
TimeSpan span = givenDate - (EvenOrOdd.Equals("Even") ? firstEvenMonday : firstOddMonday);
int numberOfPayPeriodsPast = span.Days / 14;
return (EvenOrOdd.Equals("Even") ? firstEvenMonday : firstOddMonday).AddDays(14 * numberOfPayPeriodsPast);
}
public static List<DateTime> getPayPeriodsBetween(DateTime start, DateTime end, string EvenOrOdd)
{
DateTime currentPayPeriod = getPayPeriodStartDate(start, EvenOrOdd);
if (currentPayPeriod < start) currentPayPeriod = currentPayPeriod.AddDays(14);
List<DateTime> dtList = new List<DateTime>();
while (currentPayPeriod <= end)
{
dtList.Add(currentPayPeriod);
currentPayPeriod = currentPayPeriod.AddDays(14);
}
return dtList;
}
I am sure it can be improved.
I had a need to do something similar and was able to do it very easily using LINQ. Simply build up a List for even and odd and then query between dates from the odd/even as necessary. Also, I recommend you move to an emum for parameters like EvenOrOdd where you have fixed values.
I had a similar problem a few months ago, and I ended up writing a quick script to create entries in a database for each pay period so I never had to actually do the math. This way, The system works the same speed, and doesn't have to do any slow iterations every time a period is requested.
That being said, you can always take the starting date, and add two weeks (or however long your periods are) over and over until you reach the dates you specify in the function call. This is a bit ugly, and the longer it sits in production, the slower it gets (since the dates are getting further and further apart).
Both ways are trivial to implement, it's just a matter of what kind of resources you have at hand to tackle the issue.
So, for number 1: Start with either 1/4/2009 or 1/11/2009 (depending on even/odd pay week) and add 2 weeks until the givenDate is less than the date you're testing + 2 weeks. That's the start of the period.
For number 2: Same thing, start at the date and add 2 weeks until you're within the date range. While you're there, add each item to a list. As soon as you're past the last date, break out of your loop and return your shiny new list.
If you used my method and went with a database to house all this info, it turns into 2 simple queries:
1)SELECT * FROM payperiods WHERE startdate<=givenDate ORDER BY startdate LIMIT 1
2) SELECT * FROM payperiods WHERE startdate>=givenDate AND enddate<=givenDate ORDER BY startdate
It works perfectly. I have tested.
public static DateTime GetFirstDayOfWeek(DateTime dayInWeek)
{
CultureInfo _culture = (CultureInfo)CultureInfo.CurrentCulture.Clone();
CultureInfo _uiculture = (CultureInfo)CultureInfo.CurrentUICulture.Clone();
_culture.DateTimeFormat.FirstDayOfWeek = DayOfWeek.Monday;
_uiculture.DateTimeFormat.FirstDayOfWeek = DayOfWeek.Monday;
System.Threading.Thread.CurrentThread.CurrentCulture = _culture;
System.Threading.Thread.CurrentThread.CurrentUICulture = _uiculture;
// CultureInfo defaultCultureInfo = CultureInfo.CurrentCulture;
DayOfWeek firstDay = _culture.DateTimeFormat.FirstDayOfWeek;
DateTime firstDayInWeek = dayInWeek.Date;
// Logic Of getting pay period Monday(Odd monday)
int i = Convert.ToInt32(firstDay);
while (firstDayInWeek.DayOfWeek != firstDay)
if (i % 2 != 0)
{ firstDayInWeek = firstDayInWeek.AddDays(-1); }
else
{
firstDayInWeek = firstDayInWeek.AddDays(-2);
}
return firstDayInWeek;
}