Calculating a date around working days/hours? - c#

I am currently working on a website to track projects. In it, it is possible to create Service Level Agreements (SLAs). These are configurable with days of the week that a project can be worked on and also the timespan on each of those days. Eg. on Monday it might be between 08:00 and 16:00 and then on friday from 10:00 to 14:00. They are also configured with a deadline time depending on priority. Eg. a project created with the "Low" priority has a deadline time of two weeks, and a project with "High" priority has a deadline of four hours.
The problem I'm having is calculating the deadline AROUND the hours described earlier. Say I create a project on Monday at 14:00 with a "High" priority. That means I have four hours for this project. But because of the working hours, I have two hours on monday (untill 16:00) and then another two hours on Friday. That means the Deadline must be set for Friday at 12:00.
I've spent quite some time googling this, and I can find quite a few examples of finding out how many working hours there are between a given start end ending date. I just can't figure out how to convert it into FINDING the ending datetime, given a starting time and an amount of time untill the deadline.
The day/timespans are stored in an sql database in the format:
Day(Eg. 1 for Monday) StartHour EndHour
The StartHour/EndHour are saved as DateTimes, but of course only the time part is important.
The way I figure it is, I have to somehow iterate through these times and do some datetime calculations. I just can't quite figure out what those calculations should be, what the best way is.
I found this Question here on the site as I was writing this. It is sort of what I want and I'm playing with it right now, but I'm still lost on how exactly to make it work around my dynamic work days/hours.

Here's some C# code which might help, it could be much cleaner, but it's a quick first draft.
class Program
{
static void Main(string[] args)
{
// Test
DateTime deadline = DeadlineManager.CalculateDeadline(DateTime.Now, new TimeSpan(4, 0, 0));
Console.WriteLine(deadline);
Console.ReadLine();
}
}
static class DeadlineManager
{
public static DateTime CalculateDeadline(DateTime start, TimeSpan workhours)
{
DateTime current = new DateTime(start.Year, start.Month, start.Day, start.Hour, start.Minute, 0);
while(workhours.TotalMinutes > 0)
{
DayOfWeek dayOfWeek = current.DayOfWeek;
Workday workday = Workday.GetWorkday(dayOfWeek);
if(workday == null)
{
DayOfWeek original = dayOfWeek;
while (workday == null)
{
current = current.AddDays(1);
dayOfWeek = current.DayOfWeek;
workday = Workday.GetWorkday(dayOfWeek);
if (dayOfWeek == original)
{
throw new InvalidOperationException("no work days");
}
}
current = current.AddHours(workday.startTime.Hour - current.Hour);
current = current.AddMinutes(workday.startTime.Minute - current.Minute);
}
TimeSpan worked = Workday.WorkHours(workday, current);
if (workhours > worked)
{
workhours = workhours - worked;
// Add one day and reset hour/minutes
current = current.Add(new TimeSpan(1, current.Hour * -1, current.Minute * -1, 0));
}
else
{
current.Add(workhours);
return current;
}
}
return DateTime.MinValue;
}
}
class Workday
{
private static readonly Dictionary<DayOfWeek, Workday> Workdays = new Dictionary<DayOfWeek, Workday>(7);
static Workday()
{
Workdays.Add(DayOfWeek.Monday, new Workday(DayOfWeek.Monday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 16, 0, 0)));
Workdays.Add(DayOfWeek.Tuesday, new Workday(DayOfWeek.Tuesday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 16, 0, 0)));
Workdays.Add(DayOfWeek.Wednesday, new Workday(DayOfWeek.Wednesday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 16, 0, 0)));
Workdays.Add(DayOfWeek.Thursday, new Workday(DayOfWeek.Thursday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 16, 0, 0)));
Workdays.Add(DayOfWeek.Friday, new Workday(DayOfWeek.Friday, new DateTime(1, 1, 1, 10, 0, 0), new DateTime(1, 1, 1, 14, 0, 0)));
}
public static Workday GetWorkday(DayOfWeek dayofWeek)
{
if (Workdays.ContainsKey(dayofWeek))
{
return Workdays[dayofWeek];
}
else return null;
}
public static TimeSpan WorkHours(Workday workday, DateTime time)
{
DateTime sTime = new DateTime(time.Year, time.Month, time.Day,
workday.startTime.Hour, workday.startTime.Millisecond, workday.startTime.Second);
DateTime eTime = new DateTime(time.Year, time.Month, time.Day,
workday.endTime.Hour, workday.endTime.Millisecond, workday.endTime.Second);
if (sTime < time)
{
sTime = time;
}
TimeSpan span = eTime - sTime;
return span;
}
public static DayOfWeek GetNextWeekday(DayOfWeek dayOfWeek)
{
int i = (dayOfWeek == DayOfWeek.Saturday) ? 0 : ((int)dayOfWeek) + 1;
return (DayOfWeek)i;
}
private Workday(DayOfWeek dayOfWeek, DateTime start, DateTime end)
{
this.dayOfWeek = dayOfWeek;
this.startTime = start;
this.endTime = end;
}
public DayOfWeek dayOfWeek;
public DateTime startTime;
public DateTime endTime;
}

There's a recursive solution that could work, try thinking along these lines:
public DateTime getDeadline(SubmitTime, ProjectTimeAllowed)
{
if (SubmitTime+ProjectTimeAllowed >= DayEndTime)
return getDeadline(NextDayStart, ProjectTimeAllowed-DayEndTime-SubmitTime)
else
return SubmitTime + ProjectTimeAllowed
}
Obviously this is quite rough pseudocode. Hopefully it just gives you another way to think about the problem.

Here's how I would do it. The algorithm is to see whether the issue can be closed today and if not, use all of today's time to reduce the issue's remaining time and go to tomorrow.
Find the time you have to close the issue as a TimeSpan (I'm calling this the issue's remaining time)
For each working day, create a DateTime that has only the time of the start and end.
Set the start time to now.
Loop:
Find today's remaining time by subtracting today's end time minus the start time (the result should be a TimeSpan)
If today's remaining time is greater than the issue's remaining time, take today's date and today's starttime + issue remaining time
If the issue's remaining time is greater, set the issue's remaining time to be the issue's remaining time minus today's remaining time, move to tomorrow, and go to the top of the loop.

Using Stu's answer as a starting point, modify the IsInBusinessHours function to look up you business hours for the date parameter. A procedure like the following could be used:
CREATE PROCEDURE [dbo].[IsInBusinessHours]
#MyDate DateTime
AS
BEGIN
SELECT CASE Count(*) WHEN 0 THEN 0 ELSE 1 END AS IsBusinessHour
FROM WorkHours
WHERE (DATEPART(hour, StartHours) <= DATEPART(hour, #MyDate)) AND (DATEPART(hour, EndHours) > DATEPART(hour, #MyDate)) AND (Day = DATEPART(WEEKDAY,
#MyDate))
END

Related

Calculate number of minutes between configurable hours

I have a Journey object that has a DateTime JourneyStartTime { get; set; } and a DateTime JourneyEndTime { get; set; }. I want to calculate the total number of minutes that the journey spent between 10pm and 6am (note this goes over midnight).
I have tried using TimeSpans for indicating the 10pm and 6am, but I am not sure if that is the best data type to use.
The domain for this logic is insurance based. Company X wants to score drivers that drive between X - Y hours. Those hours ought to be configurable. Here's a scenario:
A journey takes place on the same day between 5pm and 6pm. Company X Inurance is interested in journeys between 10pm and 6am. How many minutes did that journey spend in the time period that Company X is interested in?
The answer to the above is: 0, but my code is giving 60 minutes (here is a dotnetFiddle).
Here is the code.
CODE
using System;
public class Program
{
public static void Main()
{
var shortSameDayJourney = new Journey {
JourneyId = 1,
// start of journey - 5pm - start
JourneyStartTime = new DateTime(2018, 12, 17, 17, 00, 00, DateTimeKind.Utc),
// end of journey - 6pm - end
JourneyEndTime = new DateTime(2018, 12, 17, 18, 00, 00, DateTimeKind.Utc)
};
var scoreTimePeriod = new InsurerTimePeriodScoreSetting {
// start of insurer's time period.
StartOfTimePeriod = DateTime.Now + TimeSpan.FromHours(22),
// end of insurer's time period.
EndOfTimePeriod = DateTime.Now + TimeSpan.FromHours(30)
};
var minutesInTimePeriod = getNumberOfMinutesThatJourneyWasInTimePeriod(shortSameDayJourney, scoreTimePeriod);
Console.WriteLine("Number of minutes the journey was within the time period the insurer had sepcified:");
Console.WriteLine(minutesInTimePeriod + " minutes");
}
public static double getNumberOfMinutesThatJourneyWasInTimePeriod(
Journey journey,
InsurerTimePeriodScoreSetting insurerTimePeriod) {
var JourneyStart = journey.JourneyStartTime;
var JourneyEnd = journey.JourneyEndTime;
var timeSpan = insurerTimePeriod.EndOfTimePeriod - insurerTimePeriod.StartOfTimePeriod;
var startDif = (JourneyStart - insurerTimePeriod.StartOfTimePeriod);
var endDif = (insurerTimePeriod.EndOfTimePeriod - JourneyEnd);
var time = timeSpan - startDif - endDif;
return time.TotalMinutes;
}
}
public class Journey {
public int JourneyId {get;set;}
// journey start date and time in UTC, comes form a tracking device on vehicle.
public DateTime JourneyStartTime {get;set;}
// journey end date and time in UTC, comes form a tracking device on vehicle.
public DateTime JourneyEndTime {get;set;}
}
public class InsurerTimePeriodScoreSetting {
public DateTime StartOfTimePeriod {get;set;}
public DateTime EndOfTimePeriod {get;set;}
}
Timespan only gives your raw time between 2 DateTime's
so i had to changed your Journey initialization so i will be able to compare on the same day
var shortSameDayJourney = new Journey
{
JourneyId = 1,
// start of journey - 5pm - start
JourneyStartTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 17, 00, 00, DateTimeKind.Utc),
// end of journey - 6pm - end
JourneyEndTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 18, 00, 00, DateTimeKind.Utc)
};
same for InsurerTimePeriodScoreSetting
var scoreTimePeriod = new InsurerTimePeriodScoreSetting
{
// start of insurer's time period. 18/12 22:00
StartOfTimePeriod = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 22, 0, 0, DateTimeKind.Utc), // DateTime.Now + TimeSpan.FromHours(22),
// end of insurer's time period. 19/12 6:00
EndOfTimePeriod = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day + 1, 6, 0, 0, DateTimeKind.Utc) // DateTime.Now + TimeSpan.FromHours(30)
};
now all you need to do is a simple check - if Journey time is between InsurerTimePeriodScoreSetting
if (JourneyStart >= insurerTimePeriod.StartOfTimePeriod && JourneyEnd <= insurerTimePeriod.EndOfTimePeriod)
{
// your same calculation here
}
else
return 0;

Get total hours that fall between a specific time period

the condition is Time in and Time out (e.g 02/01/2015 02:55 'til 02/02/2015 05:55) that is more than a day. I already computed the total hours of Time in and Time out, and I want to know if the total hours has passed between 23:00(11:00PM ) up to 06:00AM and get the total of it
var hours = (datevalue1 - datevalue2).TotalHours;
or
Timespace ts= (datevalue1 - datevalue2);
var hours = ts.Value.TotalHours;
Try this way.. DateTime.Parse().Subtract()
eg:
string startTime = "11:00 PM";
string endTime = "6:00 AM";
TimeSpan duration = DateTime.Parse(endTime).Subtract(DateTime.Parse(startTime));
Console.WriteLine(duration);
Console.ReadKey();
OR
TimeSpan is the object you need:
TimeSpan span = (DateTime.Now - DateTime.Now);
String.Format("{0} days, {1} hours, {2} minutes, {3} seconds",
span.Days, span.Hours, span.Minutes, span.Seconds);
You can calculate it by passing over time. when its night time add it to TimeSpan.
DateTime timeIn = new DateTime(2015, 09, 29, 10, 11, 3); // 29-09-2015 at 10:11:03
DateTime timeOut = new DateTime(2015, 10, 1, 2, 19, 18); // 01-10-2015 at 02:19:38
TimeSpan nightTime = new TimeSpan(); //total amount of night time
TimeSpan passLength = new TimeSpan(0, 0, 0, 1); // length of time to pass at each iteration (1s)
while (timeIn < timeOut) // do it until timeIn reaches timeOut
{
timeIn = timeIn.Add(passLength); // add 1 second to timeIn
if (timeIn.Hour < 6 || timeIn.Hour == 23) // if we are in range of night time
{
nightTime = nightTime.Add(passLength); // add 1 second to night time
}
}
Console.WriteLine(nightTime);
You can do lot of optimizations. for long times its not good idea to add 1 sec each time. you can add 1 day to TimeIn at each iterate then add only 6 hours to night time. after you get close to Timeout decrease length time
Here is a better way. first get days fast. then get rest of the time.
DateTime timeIn = new DateTime(2015, 09, 29, 10, 11, 3); // 29-09-2015 at 10:11:03
DateTime timeOut = new DateTime(2015, 10, 1, 2, 19, 18); // 01-10-2015 at 02:19:38
// Get days
TimeSpan passLength = new TimeSpan(1, 0, 0, 0); // one day per iterate
while (timeIn + passLength < timeOut)
{
timeIn = timeIn.Add(passLength);
nightTime = nightTime.Add(new TimeSpan(0, 7, 0, 0)); // 7 hours of a day passed is considered night time
}
// Get rest of the time
passLength = new TimeSpan(0, 0, 0, 1); // one second per iterate
while (timeIn < timeOut) // do it until timeIn reaches timeOut
{
timeIn = timeIn.Add(passLength); // add 1 second to timeIn
if (timeIn.Hour < 6 || timeIn.Hour == 23) // if we are in range of night time
{
nightTime = nightTime.Add(passLength); // add 1 second to night time
}
}
Console.WriteLine(nightTime);
You shouldn't be worry about rest of the time calculation performance. since the rest of the time is now less than 1 day which is only 86400 seconds.
Less than 86400 iterates should be fine for today's processors speed. how ever you can still optimize it farther away but you don't get much more performance.
A little bit different and faster approach:
static void Main(string[] args)
{
TimeSpan result = new TimeSpan();
DateTime dt1 = new DateTime(2015, 09, 29, 10, 11, 03);
DateTime dt2 = new DateTime(2015, 10, 01, 02, 19, 38);
DateTime d1 = new DateTime(dt1.Year, dt1.Month, dt1.Day, 0, 0, 0); //Date only
DateTime d2 = new DateTime(dt2.Year, dt2.Month, dt2.Day, 0, 0, 0); //Date only
//Count night time in first day
result += DateTime.Compare(dt1, d1.AddHours(6)) > 0 ? new TimeSpan(6, 0, 0) : new TimeSpan(dt1.Hour, dt1.Minute, dt1.Second);
if (DateTime.Compare(dt1, d1.AddHours(23)) > 0) result += new TimeSpan(dt1.Hour - 23, dt1.Minute, dt1.Second);
//Count night time in last day
result += DateTime.Compare(dt2, d2.AddHours(6)) > 0 ? new TimeSpan(6, 0, 0) : new TimeSpan(dt2.Hour, dt2.Minute, dt2.Second);
if (DateTime.Compare(dt2, d2.AddHours(23)) > 0) result += new TimeSpan(dt1.Hour - 23, dt2.Minute, dt2.Second);
//Count night time in middle days
int daysBetween = (int)(d2 - d1).TotalDays - 1;
result += new TimeSpan(daysBetween * 7, 0, 0);
Console.WriteLine("Night time: " + result);
Console.ReadKey();
}
Compare EndTime with your Range(23:00-06:00)
that is in your Case, check wether EndTime 05:55 < 06:00 and EndTime 05:55 > 23:00
You can subtract the DateTime values to get the TimeSpan in between. Then you can get the TotalHours in that
var hours = timeOut.Subtract(timeIn).TotalHours;
For example
timeIn = 29-09-2015 10:11:03;
timeOut = 01-10-2015 02:19:38;
hours = 52.14303137125;

Absolute difference of a TimeSpan object with a timing interval in scale of month and year

I am in some trouble within a simple timing manipulation in C#.
The user defines two DateTime objects, as the start and the end of a time interval:
DateTime From = new DateTime(Y, Mo, D, H, Mi, S, Ms);
DateTime To = new DateTime(YY, MMo, DD, HH, MMi, SS, MMs);
Then a delay parameter, is which a TimeSpan object, would be taken into account:
TimeSpan delay = new TimeSpan(day, month, hour, second);
Now the program should return the deviation of the time interval, corresponding to the delay parameter.
Now, there are two problems:
1- Time span has no Year and Month parameters, whereas the difference between From and To might be more than Day... How can I feed Year and Month into the TimeSpan object?!... (I know that there is no defined constructor for this aim)
2- The final difference, which I try to catch by below code snippet just produces garbage:
var diff = (To - From).duration() - delay;
How should I resolve this case?!
I am appreciated if anyone can handle above cases...
This is the sort of thing that my Noda Time project is designed to handle. It has a Period type which does know about months and years, not just a fixed number of ticks. For example:
LocalDateTime start = new LocalDateTime(2014, 1, 1, 8, 30);
LocalDateTime end = new LocalDateTime(2014, 9, 16, 12, 0);
Period delay = new PeriodBuilder {
Months = 8,
Days = 10,
Hours = 2,
Minutes = 20
}
.Build();
Period difference = (Period.Between(start, end) - delay).Normalize();
Here difference would be a period of 5 days, 1 hour, 10 minutes. (The Normalize() call is to normalize all values up to days... otherwise you can have "1 hour - 10 minutes" for example.) The Period API is going to change a bit for Noda Time 2.0, but it will still have the same basic ideas.)
If you you choose to round down and add extension methods :
public static class Extensions
{
private const double DaysInYear = 365.242;
private const double DaysInMonth = 30.4368;
public static int GetDays(this TimeSpan ts)
{
return (int)((ts.TotalDays % DaysInYear % DaysInMonth));
}
public static int GetMonths(this TimeSpan ts)
{
return (int)((ts.TotalDays % DaysInYear) / DaysInMonth);
}
public static int GetYears(this TimeSpan ts)
{
return (int)(ts.TotalDays / DaysInYear);
}
}
It would be easy as:
var oldDate = new DateTime(2002, 7, 15);
var newDate = new DateTime(2014, 9, 16, 12, 3, 0);
// Difference
var ts = newDate - oldDate;
var dm = ts.Minutes; //3
var dh = ts.Hours; //12
var dd = ts.GetDays(); //2
var dM = ts.GetMonths(); //2
var dY = ts.GetYears(); //12
Note that this is an approximation and would apply only if you can make assumptions that
DaysInYear = 365.242
DaysInMonth = 30.4368
are correct.

Generating weekly dates

I am sure this has been done before so i am looking for an efficient solution instead of own custom solution.
Given 2 dates, I am trying to generate the accurate weekly date (for creating weekly orders).
EDIT: I need to use .NET standard library to do this.
Example below,
Given 28/02/2012 and 6/03/2012.
so, the weekly dates generated are
- Week From(Start Monday): Week To(End Sunday):
- 27/02/2012 - 04/03/2012
- 05/03/2012 - 11/03/2012
Another example (1 month)
Given 01/02/2012 and 29/02/2012
so, the weekly dates generated are
- Week From(Start Monday): Week To(End Sunday):
- 30/01/2012 - 05/02/2012
- 06/02/2012 - 12/02/2012
- 13/02/2012 - 19/02/2012
- 20/02/2012 - 26/02/2012
- 27/02/2012 - 04/03/2012
I am doing this in c#. Has this been done before? Mind sharing the solutions?
Cheers
Here's a solution using Noda Time. Admittedly it requires a <= operator which I'm just implementing right now - but that shouldn't take long :)
using System;
using NodaTime;
class Test
{
static void Main()
{
ShowDates(new LocalDate(2012, 2, 28), new LocalDate(2012, 3, 6));
ShowDates(new LocalDate(2012, 2, 1), new LocalDate(2012, 2, 29));
}
static void ShowDates(LocalDate start, LocalDate end)
{
// Previous is always strict - increment start so that
// it *can* be the first day, then find the previous
// Monday
var current = start.PlusDays(1).Previous(IsoDayOfWeek.Monday);
while (current <= end)
{
Console.WriteLine("{0} - {1}", current,
current.Next(IsoDayOfWeek.Sunday));
current = current.PlusWeeks(1);
}
}
}
Obviously it's possible to do this in normal DateTime as well, but there's no real representation of "just a date" which makes the code less clear - and you'd need to implement Previous yourself.
EDIT: For example, in this case you might use:
using System;
class Test
{
static void Main()
{
ShowDates(new DateTime(2012, 2, 28), new DateTime(2012, 3, 6));
ShowDates(new DateTime(2012, 2, 1), new DateTime(2012, 2, 29));
}
static void ShowDates(DateTime start, DateTime end)
{
// In DateTime, 0=Sunday
var daysToSubtract = ((int) start.DayOfWeek + 6) % 7;
var current = start.AddDays(-daysToSubtract);
while (current <= end)
{
Console.WriteLine("{0} - {1}", current, current.AddDays(6));
current = current.AddDays(7);
}
}
}
Assuming you don't have to figure out that the start date is a monday:
var slots = new List<Tuple<DateTime, DateTime>>();
DateTime start = new DateTime(2012, 2, 28);
DateTime end = new DateTime(2012, 3, 6);
for (DateTime i = start; i < end; i = i.AddDays(7))
{
slots.Add(new Tuple<DateTime, DateTime>(i, i.AddDays(6)));
}
foreach (var slot in slots)
{
Console.WriteLine("{0}\t{1}", slot.Item1.ToString("dd/MM/yyyy"), slot.Item2.ToString("dd/MM/yyyy"));
}
Edit: Assuming you have to figure out what monday and what sunday covers the date range, you can move one day backwards till you hit a monday, and a day forward till you hit a sunday.
class Program
{
static void Main(string[] args)
{
var slots = new List<Tuple<DateTime, DateTime>>();
DateTime start = FirstMonday(new DateTime(2012, 2, 28));
DateTime end = FirstSunday(new DateTime(2012, 3, 6));
for (DateTime i = start; i < end; i = i.AddDays(7))
{
slots.Add(new Tuple<DateTime, DateTime>(i, i.AddDays(6)));
}
foreach (var slot in slots)
{
Console.WriteLine("{0}\t{1}", slot.Item1.ToString("dd/MM/yyyy"), slot.Item2.ToString("dd/MM/yyyy"));
}
Console.ReadLine();
}
static DateTime FirstMonday(DateTime date)
{
while (date.DayOfWeek != DayOfWeek.Monday) date = date.AddDays(-1);
return date;
}
static DateTime FirstSunday(DateTime date)
{
while (date.DayOfWeek != DayOfWeek.Sunday) date = date.AddDays(1);
return date;
}
}
This solution allows you to customize your start and end DayOfWeek:
Solution:
public Dictionary<DateTime, DateTime> GetWeeklyDateTimes(DateTime from, DateTime to, DayOfWeek startDay, DayOfWeek endDay)
{
int startEndSpan = 7 - endDay - startDay;
// Subtract days until it falls on our desired start day
from = from.AddDays(startDay - from.DayOfWeek);
// Add days until it falls on our desired end day
to = to.AddDays(to.DayOfWeek - endDay + 2);
Dictionary<DateTime, DateTime> dateTimes = new Dictionary<DateTime, DateTime>();
while (to.Subtract(from).Days > startEndSpan)
{
dateTimes.Add(from, from.AddDays(startEndSpan));
from = from.AddDays(startEndSpan + 1);
}
return dateTimes;
}
Example Usage:
// DateTime(2012, 2, 1) corresponds to Year 2012, Month February, Day 1
Dictionary<DateTime, DateTime> dateTimes = GetWeeklyDateTimes(new DateTime(2012, 2, 1), new DateTime(2012, 2, 29), DayOfWeek.Monday, DayOfWeek.Sunday);
foreach (KeyValuePair<DateTime, DateTime> entry in dateTimes)
{
Trace.WriteLine(entry.Key.ToString() + " " + entry.Value.ToString());
}

Date calculations in C#

When given a start date a need to do various calculations on it to produce 3 other dates.
Basically I need to work out what date the user has been billed up to for different frequencies based on the current date.
Bi-Annually (billed twice a year),
Quarterly (billed 4 times a year),
and Two Monthly (billed ever other month).
Take the date 26/04/2008
- BiAnnually: This date would have been last billed on 26/10/2010 and should give the date 26/04/2011.
- Quarterly: This date would have been last billed on 26/01/2011 and should give the date 26/04/2011.
- Two Month: This date would have been last billed on 26/12/2010 and should give the date 26/02/2011.
Assistance is much appreciated.
I think that you can just do like this:
public void FindNextDate(DateTime startDate, int interval);
DateTime today = DateTime.Today;
do {
startDate = startDate.AddMonths(interval);
} while (startDate <= today);
return startDate;
}
Usage:
DateTime startDate = new DateTime(2008, m4, 26);
DateTime bi = FindNextDate(startDate, 6);
DateTime quarterly = FindNextDate(startDate, 3);
DateTime two = FindNextDate(startDate, 2);
I think all you want is something like
DateTime x = YourDateBasis;
y = x.AddMonths(6);
y = x.AddMonths(3);
y = x.AddMonths(2);
Then to edit from comment,
Date Math per the period cycle of the person's account, you would simply need the start and end date and keep adding respective months until you've created all expected months. Almost like that of a loan payment that's due every month for 3 years
DateTime CurrentDate = DateTime.Now;
while( CurrentDate < YourFinalDateInFuture )
{
CurrentDate = CurrentDate.AddMonths( CycleFrequency );
Add Record into your table as needed
Perform other calcs as needed
}
enum BillPeriod
{
TwoMonth = 2,
Quarterly = 3,
SemiAnnually = 6,
BiAnnually = 24
}
public Pair<Datetime, Datetime> BillDates(Datetime currentBillDate, BillPeriod period)
{
Datetime LastBill = currentBillDate.AddMonths(-1 * (int)period);
Datetime NextBill = currentBillDate.AddMonths((int)period);
return new Pair<Datetime,Datetime>(LastBill, NextBill);
}
This is a terrible solution, but it works. Remember, red-light, green-light, refactor. Here, we're at green-light:
namespace ConsoleApplication1 {
class Program {
static void Main(string[] args) {
Console.WriteLine(GetLastBilled(new DateTime(2008, 4, 26), 6));
Console.WriteLine(GetNextBilled(new DateTime(2008, 4, 26), 6));
Console.WriteLine(GetLastBilled(new DateTime(2008, 4, 26), 4));
Console.WriteLine(GetNextBilled(new DateTime(2008, 4, 26), 4));
Console.WriteLine(GetLastBilled(new DateTime(2008, 4, 26), 2));
Console.WriteLine(GetNextBilled(new DateTime(2008, 4, 26), 2));
Console.WriteLine("Complete...");
Console.ReadKey(true);
}
static DateTime GetLastBilled(DateTime initialDate, int billingInterval) {
// strip time and handle staggered month-end and 2/29
var result = initialDate.Date.AddYears(DateTime.Now.Year - initialDate.Year);
while (result > DateTime.Now.Date) {
result = result.AddMonths(billingInterval * -1);
}
return result;
}
static DateTime GetNextBilled(DateTime initialDate, int billingInterval) {
// strip time and handle staggered month-end and 2/29
var result = initialDate.Date.AddYears(DateTime.Now.Year - initialDate.Year);
while (result > DateTime.Now.Date) {
result = result.AddMonths(billingInterval * -1);
}
result = result.AddMonths(billingInterval);
return result;
}
}
}
This is really tricky. For example, you need to take into account that the date you billed could have been 2/29 on a leap year, and not all months have the same number of days. That's why I did the initialDate.Date.AddYears(DateTime.Now.Year - initialDate.Year); call.

Categories

Resources