Reputation: 24482
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)
Upvotes: 0
Views: 7124
Reputation:
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
Upvotes: 3
Reputation: 241789
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.
Upvotes: 1
Reputation: 174467
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);
}
Upvotes: 6
Reputation: 4002
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
Upvotes: 3