Assume I have a user interface where the user can select days. Is there a way to check if the days selected are sequential, such as:
4/4, 4/5, 4/6, 4/7, 4/8, 4/9, 4/10 or
4/29, 4/30, 5/1, 5/2, 5/3
I know I probably can loop through the date range and check, but I was more curious if there was a built in method already to check for this.
Regarding the above scenarios, they are in order and they can roll over into the next month.
I am using the .NET Framework 2.0 and can't use LINQ.
Regarding Tom's answer:
DateTime dtStart = new DateTime(2011,5,4);
DateTime dtEnd = new DateTime(2011,5,11);
int numberOfDaysSelected = 7; //Assume 7 days were selected.
TimeSpan ts = dtEnd - dtStart;
if(ts.Days == numberOfDaysSelected - 1)
{
Console.WriteLine("Sequential");
}
else
{
Console.WriteLine("Non-Sequential");
}
I do not believe there is a built in method to achieve your desired results but if you can easily tell the earliest and latest dates, you could create a new TimeSpan by subtracting the the earliest date from the latest date and then verifying that the number of days of the timespan matches the number of dates selected - 1.
You didn't tell us if the days are ordered.
You didn't tell us if they might fall over a month boundary as in
30, 31, 1.
I'll assume ordered, and I'll assume they won't fall over a month boundary (because your example is ordered, and it doesn't fall over a month boundary).
Then you can say
public bool IsSequential(this IEnumerable<DateTime> sequence) {
Contract.Requires(sequence != null);
var e = sequence.GetEnumerator();
if(!e.MoveNext()) {
// empty sequence is sequential
return true;
}
int previous = e.Current.Date;
while(e.MoveNext()) {
if(e.Current.Date != previous.AddDays(1)) {
return false;
}
previous = e.Current.Date;
}
return true;
}
Note that this solution requires only walking the sequence once. If you don't have an ordered sequence, or if you permit falling over a month boundary the solution is more complicated.
Nothing built in but you can build one easily using Linq:
List<DateTime> timeList = new List<DateTime>();
//populate list..
bool isSequential = timeList.Zip(timeList.Skip(1),
(a, b) => b.Date == a.Date.AddDays(1))
.All(x => x);
Edited - misunderstood question first to mean ascending in time as opposed to sequential - fixed that.
Extension method using Linq:
public static bool IsContiguous(this IEnumerable<DateTime> dates)
{
var startDate = dates.FirstOrDefault();
if (startDate == null)
return true;
//.All() doesn't provide an indexed overload :(
return dates
.Select((d, i) => new { Date = d, Index = i })
.All(d => (d.Date - startDate).Days == d.Index);
}
Testing it:
List<DateTime> contiguousDates = new List<DateTime>
{
new DateTime(2011, 05, 05),
new DateTime(2011, 05, 06),
new DateTime(2011, 05, 07),
};
List<DateTime> randomDates = new List<DateTime>
{
new DateTime(2011, 05, 05),
new DateTime(2011, 05, 07),
new DateTime(2011, 05, 08),
};
Console.WriteLine(contiguousDates.IsContiguous());
Console.WriteLine(randomDates.IsContiguous());
Returns
True
False
EDIT:
.NET 2-like answer:
public static bool CheckContiguousDates(DateTime[] dates)
{
//assuming not null and count > 0
var startDate = dates[0];
for (int i = 0; i < dates.Length; i++)
{
if ((dates[i] - startDate).Days != i)
return false;
}
return true;
}
You can use the TimeGapCalculator of the Time Period Library for .NET to find gaps between multiple time periods (independent of order, count and overlapping):
// ----------------------------------------------------------------------
public void SequentialPeriodsDemo()
{
// sequential
ITimePeriodCollection periods = new TimePeriodCollection();
periods.Add( new Days( new DateTime( 2011, 5, 4 ), 2 ) );
periods.Add( new Days( new DateTime( 2011, 5, 6 ), 3 ) );
Console.WriteLine( "Sequential: " + IsSequential( periods ) );
periods.Add( new Days( new DateTime( 2011, 5, 10 ), 1 ) );
Console.WriteLine( "Sequential: " + IsSequential( periods ) );
} // SequentialPeriodsDemo
// --------------------------------------------------------------------
public bool IsSequential( ITimePeriodCollection periods, ITimePeriod limits = null )
{
return new TimeGapCalculator<TimeRange>(
new TimeCalendar() ).GetGaps( periods, limits ).Count == 0;
} // IsSequential
Related
I'm trying to workout the amount of time between two LocalDateTime values and exclude specific dates (in this example, it's bank holidays).
var bankHolidays = new[] { new LocalDate(2013, 12, 25), new LocalDate(2013, 12, 26) };
var localDateTime1 = new LocalDateTime(2013, 11, 18, 10, 30);
var localDateTime2 = new LocalDateTime(2013, 12, 29, 10, 15);
var differenceBetween = Period.Between(localDateTime1, localDateTime2, PeriodUnits.Days | PeriodUnits.HourMinuteSecond);
The differenceBetween value shows the number of days/hours/minutes/seconds between the two dates, as you would expect.
I could check every single day from the start date and see if the bankHolidays collection contains that date e.g.
var bankHolidays = new[] { new LocalDate(2013, 12, 25), new LocalDate(2013, 12, 26) };
var localDateTime1 = new LocalDateTime(2013, 11, 18, 10, 30);
var localDateTime2 = new LocalDateTime(2013, 12, 29, 10, 15);
var differenceBetween = Period.Between(localDateTime1, localDateTime2, PeriodUnits.Days | PeriodUnits.HourMinuteSecond);
var london = DateTimeZoneProviders.Tzdb["Europe/London"];
for (var i = 1; i < differenceBetween.Days; ++i)
{
var x = localDateTime1.InZoneStrictly(london) + Duration.FromStandardDays(i);
if (bankHolidays.Any(date => date == x.Date))
{
//subtract one day for the period.
}
}
I feel like I'm missing some obvious and there should be an easier method, is there a simpler way to find a period between two dates whilst excluding certain dates?
I also need to include weekends in this exclusion too, the obvious way seems to be to check the day of the week for weekends whilst checking bank holidays, this just doesn't seem like the best/correct way of handling it though.
I feel like I'm missing some obvious and there should be an easier method, is there a simpler way to find a period between two dates whilst excluding certain dates?
Well, it's relatively easy to count the number of bank holidays included in a date-to-date range:
Sort all the bank holidays in chronological order
Use a binary search to find out where the start date would come in the collection
Use a binary search to find out where the end date would come in the collection
Subtract one index from another to find how many entries are within that range
Work out the whole period using Period.Between as you're already doing
Subtract the number of entries in the range from the total number of days in the range
The fiddly bit is taking into account that the start and/or end dates may be bank holidays. There's a lot of potential for off-by-one errors, but with a good set of unit tests it should be okay.
Alternatively, if you've got relatively few bank holidays, you can just use:
var period = Period.Between(start, end,
PeriodUnits.Days | PeriodUnits.HourMinuteSecond);
var holidayCount = holidays.Count(x => x >= start && x <= end);
period = period - Period.FromDays(holidayCount);
Just use TimeSpan to get the difference, all times are in your current local time zone:
var bankHolidays = new[] { new DateTime(2013, 12, 25), new DateTime(2013, 12, 26) };
var localDateTime1 = new DateTime(2013, 11, 18, 10, 30, 0);
var localDateTime2 = new DateTime(2013, 12, 29, 10, 15, 0);
var span = localDateTime2 - localDateTime1;
var holidays = bankHolidays[1] - bankHolidays[0];
var duration = span-holidays;
Now duration is your time elapsed between localDateTime1 and localDateTime2.
If you want to exlude two dates via the bankHolidays you can easiely modify the operations above.
You might use an extra method for this operation:
public static TimeSpan GetPeriod(DateTime start, DateTime end, params DateTime[] exclude)
{
var span = end - start;
if (exclude == null) return span;
span = exclude.Where(d => d >= start && d <= end)
.Aggregate(span, (current, date) => current.Subtract(new TimeSpan(1, 0, 0, 0)));
return span;
}
Now you can just use this:
var duration = GetPeriod(localDateTime1, localDateTime2, bankHolidays);
Assume Financial Quarters always start on the 1st of a month and they are always 3 calendar months long.
Different organisations start their Financial Year (FY) in different months - some may be 1st April , some may be 1st July or could be just 1st Jan (which will match normal Calendar Quarters).
Given a date and a month that the FY starts on how can you determine the start of the quarter that the date falls in.
E.g.
DateTime getStartOfFinancialQtr(DateTime date, int monthFinancialYearStartsOn)
15th Jan when FY starts Jan would = 1st Jan
getStartOfFinancialQtr(new DateTime(2013,1,15), 1) == new DateTime(2013,1,1)
15th August when FY starts April would be 1st July
getStartOfFinancialQtr(new DateTime(2013,8,15), 4) == new DateTime(2013,7,1)
BUT 15th Jan 2013 when FY starts February would be 1st November 2012
getStartOfFinancialQtr(new DateTime(2013,1,15), 2) == new DateTime(2012,11,1)
The following solution is the most simple implementation I could think of and works without any - unnecessary - loops:
DateTime getStartOfFinancialQtr(DateTime date, int monthFinancialYearStartsOn)
{
var actualMonth = date.Month;
var financialYear = date.Year;
var difference = actualMonth - monthFinancialYearStartsOn;
if(difference < 0)
{
--financialYear;
difference += 12;
}
var quarter = difference / 3;
return new DateTime(financialYear, monthFinancialYearStartsOn, 1).AddMonths(quarter * 3);
}
Isn't it as simple as this? Am I missing something to this? A quarter is defined as a period of three months, so you just have to find where the given date is, and then compute where the quarter begins based off that given month of the date.
public DateTime GetStartOfFinancialQtr(DateTime dtGiven, int startMonth) {
DateTime dtQuarter = new DateTime(dtGiven.Year, startMonth, 1);
// Start Q is less than the given date
if(startMonth > dtGiven.Month) {
while(dtQuarter > dtGiven) {
dtQuarter = dtQuarter.AddMonths(-3);
}
}
// Start Q is larger than the given date
else {
while(dtQuarter.Month + 3 <= dtGiven.Month) {
dtQuarter = dtQuarter.AddMonths(3);
}
}
return dtQuarter;
}
Below is the testing I ran:
Console.WriteLine(GetStartOfFinancialQtr(new DateTime(2013, 1, 15), 1).ToString());
Console.WriteLine(GetStartOfFinancialQtr(new DateTime(2013, 8, 15), 4).ToString());
Console.WriteLine(GetStartOfFinancialQtr(new DateTime(2013, 1, 15), 2).ToString());
Console output:
01/01/2013 000000
07/01/2013 000000
11/01/2012 000000
You can use the Year class of the Time Period Library for .NET:
// ----------------------------------------------------------------------
public void FiscalYearRange()
{
// calendar
TimeCalendar fiscalYearCalendar = new TimeCalendar(
new TimeCalendarConfig
{
YearBaseMonth = YearMonth.April,
YearType = YearType.FiscalYear
} );
// time range
TimeRange timeRange = new TimeRange( new DateTime( 2007, 10, 1 ), new DateTime( 2012, 2, 25 ) );
Console.WriteLine( "Time range: " + timeRange );
Console.WriteLine();
// fiscal quarter
Console.WriteLine( "Start Quarter: " + new Quarter( timeRange.Start, fiscalYearCalendar ) );
Console.WriteLine( "End Quarter: " + new Quarter( timeRange.End, fiscalYearCalendar ) );
Console.WriteLine();
// fiscal year
Year year = new Year( timeRange.Start, fiscalYearCalendar );
while ( year.Start < timeRange.End )
{
Console.WriteLine( "Fiscal Year: " + year );
year = year.GetNextYear();
}
} // FiscalYearRange
As mentioned, you can easily obtain the answer from Nearest Completed quarter. Here's how you make the modification:
public static class DateTimeExtensions {
public static DateTime NearestQuarterEnd(
this DateTime date,
int firstMonthOfFiscalYear
) {
IEnumerable<DateTime> candidates =
QuartersInYear(date.Year, firstMonthOfFiscalYear)
.Concat(QuartersInYear(date.Year - 1, firstMonthOfFiscalYear));
return candidates.SkipWhile(d => d > date).First();
}
static Dictionary<Tuple<int, int>, List<DateTime>> dict =
new Dictionary<Tuple<int, int>, List<DateTime>>();
static IEnumerable<DateTime> QuartersInYear(
int year,
int firstMonthOfFiscalYear
) {
Contract.Requires(firstMonthOfFiscalYear >= 1
&& firstMonthOfFiscalYear <= 12);
var key = Tuple.Create(year, firstMonthOfFiscalYear);
if(dict.ContainsKey(key)) {
return dict[key];
}
else {
var value =
Enumerable
.Range(0, 4)
.Select(k => firstMonthOfFiscalYear + 3 * k)
.Select(m => m <= 12 ? m : m % 12)
.Select(m => new DateTime(year, m, 1))
.OrderByDescending(d => d)
.ToList();
dict.Add(key, value);
return value;
}
}
}
Usage:
Console.WriteLine(new DateTime(2013, 1, 15).NearestQuarterEnd(1));
Console.WriteLine(new DateTime(2013, 8, 15).NearestQuarterEnd(4));
Console.WriteLine(new DateTime(2013, 1, 15).NearestQuarterEnd(2));
Output:
1/1/2013 12:00:00 AM
7/1/2013 12:00:00 AM
11/1/2012 12:00:00 AM
This passes all three of your test cases.
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());
}
I've been racking my brains over this, but it's late on a Friday and I'm going round in circles.
I need to create a list of working weeks for a drop down list, with the week number as the value. So the code would output:
Monday 22nd August - Friday 26th September
Monday 29th August - Friday 2 September
Monday 5th September - Friday 9 September
etc..
For the whole year. Any ideas how I would achieve this?
Thanks.
I think the code below complies with ISO 8601:
var jan1 = new DateTime(DateTime.Today.Year , 1, 1);
//beware different cultures, see other answers
var startOfFirstWeek = jan1.AddDays(1 - (int)(jan1.DayOfWeek));
var weeks=
Enumerable
.Range(0,54)
.Select(i => new {
weekStart = startOfFirstWeek.AddDays(i * 7)
})
.TakeWhile(x => x.weekStart.Year <= jan1.Year)
.Select(x => new {
x.weekStart,
weekFinish=x.weekStart.AddDays(4)
})
.SkipWhile(x => x.weekFinish < jan1.AddDays(1) )
.Select((x,i) => new {
x.weekStart,
x.weekFinish,
weekNum=i+1
});
Bear in mind, that week calculations are done differently in different cultures and there is not a bug if you see week number 53!
using System.Globalization;
CultureInfo cultInfo = CultureInfo.CurrentCulture;
int weekNumNow = cultInfo.Calendar.GetWeekOfYear(DateTime.Now,
cultInfo.DateTimeFormat.CalendarWeekRule,
cultInfo.DateTimeFormat.FirstDayOfWeek);
Just updating what Spender put, because I wanted to make the output of your Datetimes more towards what you wanted.
DateTime jan1 = new DateTime(DateTime.Today.Year, 1, 1);
//beware different cultures, see other answers
DateTime startOfFirstWeek = jan1.AddDays(1 - (int)(jan1.DayOfWeek));
var weeks=
Enumerable
.Range(0,54)
.Select(i => new {
weekStart = startOfFirstWeek.AddDays(i * 7)
})
.TakeWhile(x => x.weekStart.Year <= jan1.Year)
.Select(x => new {
x.weekStart,
weekFinish=x.weekStart.AddDays(4)
})
.SkipWhile(x => x.weekFinish.Year < jan1.Year)
.Select((x,i) => new {
WeekStart = x.weekStart.ToString("dddd, d, MMMM"),
WeekFinish = x.weekFinish.ToString("dddd, d, MMMM"),
weekNum=i+1
});
The change to correct the formatting to what you wanted is in the last select of the anonymous object.
You can use the Week class of the Time Period Library for .NET:
DateTime start = DateTime.Now.Date;
DateTime end = start.AddYears( 1 );
Week week = new Week( start );
while ( week.Start < end )
{
Console.WriteLine( "week " + week );
week = week.GetNextWeek();
}
You may need to tweak this a bit, but it should get you what you need:
static void Main(string[] args)
{
List<DateTime[]> weeks = new List<DateTime[]>();
DateTime beginDate = new DateTime(2011, 01, 01);
DateTime endDate = new DateTime(2012, 01, 01);
DateTime monday = DateTime.Today;
DateTime friday = DateTime.Today;
while (beginDate < endDate)
{
beginDate = beginDate.AddDays(1);
if (beginDate.DayOfWeek == DayOfWeek.Monday)
{
monday = beginDate;
}
else if (beginDate.DayOfWeek == DayOfWeek.Friday)
{
friday = beginDate;
}
else if (beginDate.DayOfWeek == DayOfWeek.Saturday)
{
weeks.Add(new DateTime[] { monday, friday });
}
}
for (int x = 0; x < weeks.Count; x++)
{
Console.WriteLine(weeks[x][0].Date.ToShortDateString() + " - " + weeks[x][1].Date.ToShortDateString());
}
Console.ReadLine();
}
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.